# Create your models here. from django.db import models from modelcluster.fields import ParentalKey from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel, InlinePanel from wagtail.core.blocks import StreamBlock from wagtail.core.fields import StreamField from wagtail.core.models import Page, Orderable from django.utils.text import slugify from vbv_lernwelt.learnpath.models_learning_unit_content import WebBasedTrainingBlock, VideoBlock class LearningPath(Page): # PageChooserPanel('related_page', 'demo.PublisherPage'), content_panels = Page.content_panels + [ InlinePanel('topics', label="Topics"), ] subpage_types = ['learnpath.Circle'] class Meta: verbose_name = "Learning Path" def __str__(self): return f"{self.title}" class Topic(Orderable): title = models.TextField(default='') is_visible = models.BooleanField(default=True) learning_path = ParentalKey('learnpath.LearningPath', null=True, blank=True, on_delete=models.CASCADE, related_name='topics', ) panels = [FieldPanel('title'), FieldPanel('is_visible'), ] # content_panels = Page.content_panels + [ # FieldPanel('is_visible', classname="full"), # PageChooserPanel('learning_path', 'learnpath.LearningPath'), # ] # parent_page_types = ['learnpath.LearningPath'] # subpage_types = ['learnpath.Circle'] def full_clean(self, *args, **kwargs): self.slug = find_available_slug(Topic, slugify(self.title, allow_unicode=True)) super(Topic, self).full_clean(*args, **kwargs) class Meta: verbose_name = "Topic" def __str__(self): return f"{self.title}" class Circle(Page, Orderable): description = models.TextField(default="", blank=True) goals = models.TextField(default="", blank=True) topic = models.ForeignKey( 'learnpath.Topic', null=True, blank=True, on_delete=models.SET_NULL, related_name='circles' ) parent_page_types = ['learnpath.Learningpath'] subpage_types = ['learnpath.LearningUnit'] content_panels = Page.content_panels + [ FieldPanel('description'), FieldPanel('topic'), FieldPanel('goals'), InlinePanel('learning_sequences', label="Learning Sequences"), ] def full_clean(self, *args, **kwargs): self.slug = find_available_slug(Circle, slugify(self.title, allow_unicode=True)) super(Circle, self).full_clean(*args, **kwargs) class Meta: verbose_name = "Circle" def __str__(self): return f"{self.title}" IN_CIRCLE = 'INCIRCLE' START = 'START' END = 'END' LEARNING_SEQUENCE_CATEGORIES = [ (IN_CIRCLE, 'In Circle'), (START, 'Start'), (END, 'End') ] class LearningSequence(Orderable): # TODO: How to do a icon choice field? title = models.CharField(max_length=256, default='') category = models.CharField(max_length=16, choices=LEARNING_SEQUENCE_CATEGORIES, default=IN_CIRCLE) circle = ParentalKey( 'learnpath.Circle', null=True, blank=True, on_delete=models.CASCADE, related_name='learning_sequences', ) panels = [FieldPanel('title'), FieldPanel('category')] class Meta: verbose_name = "Learning Sequence" def __str__(self): return f"{self.title}" class LearningUnit(Page, Orderable): # TODO: Review model architecture, is the stream fiel the right thing here? parent_page_types = ['learnpath.Circle'] learning_sequence = models.ForeignKey( 'learnpath.LearningSequence', null=True, blank=True, on_delete=models.CASCADE, related_name='learning_units' ) content_blocks = [ ('web_based_training', WebBasedTrainingBlock()), ('video', VideoBlock()), ] contents = StreamField(StreamBlock(content_blocks), null=True, blank=True, min_num=1, max_num=1) content_panels = [ FieldPanel('title', classname="full title"), StreamFieldPanel('contents'), FieldPanel('learning_sequence') ] subpage_types = [] class Meta: verbose_name = "Learning Unit" def full_clean(self, *args, **kwargs): self.slug = find_available_slug(LearningUnit, slugify(self.title, allow_unicode=True)) super(LearningUnit, self).full_clean(*args, **kwargs) def __str__(self): return f"{self.title}" def find_available_slug(model, requested_slug, ignore_page_id=None): """ Finds an available slug within the specified parent. If the requested slug is not available, this adds a number on the end, for example: - 'requested-slug' - 'requested-slug-1' - 'requested-slug-2' And so on, until an available slug is found. The `ignore_page_id` keyword argument is useful for when you are updating a page, you can pass the page being updated here so the page's current slug is not treated as in use by another page. """ # TODO: In comparison ot wagtails own function, I look for the same model instead of the parent pages = model.objects.filter(slug__startswith=requested_slug) if ignore_page_id: pages = pages.exclude(id=ignore_page_id) existing_slugs = set(pages.values_list("slug", flat=True)) slug = requested_slug number = 1 while slug in existing_slugs: slug = requested_slug + "-" + str(number) number += 1 return slug