# Create your models here. from django.utils.text import slugify from wagtail.api import APIField from wagtail.blocks import StreamBlock from wagtail.fields import StreamField from wagtail.models import Page, Orderable from vbv_lernwelt.learnpath.models_competences import * from vbv_lernwelt.learnpath.models_learning_unit_content import RiseTrainingBlock, VideoBlock, PodcastBlock, \ CompetenceBlock, ExerciseBlock, SelfEvaluationBlock, DocumentBlock, KnowledgeBlock from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class 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'), ] api_fields = [ APIField('title'), APIField('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): 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.LearningSequence', 'learnpath.LearningUnit'] content_panels = Page.content_panels + [ FieldPanel('description'), FieldPanel('goals'), ] api_fields = [ APIField('title'), APIField('description'), ] 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}" class LearningSequence(Page): icon = models.CharField(max_length=255, default="IconLsStart") parent_page_types = ['learnpath.Circle'] panels = [ FieldPanel('title'), FieldPanel('icon'), ] class Meta: verbose_name = "Learning Sequence" def __str__(self): return f"{self.title}" @classmethod def get_serializer_class(cls): return get_it_serializer_class(cls, field_names=['id', 'title', 'icon', 'slug', 'type', 'translation_key']) def get_admin_display_title(self): return f'{self.icon} {self.draft_title}' def full_clean(self, *args, **kwargs): super(LearningSequence, self).full_clean(*args, **kwargs) class LearningUnit(Page): """ This is a group of contents, with the übung and test it is one unit. ... more of a structural charactacter. """ # TODO: Review model architecture, is the stream field the right thing here? parent_page_types = ['learnpath.Circle'] subpage_types = [] minutes = models.PositiveIntegerField(default=15) package = models.CharField(max_length=255, default="", blank=True) content_blocks = [ ('video', VideoBlock()), ('rise_training', RiseTrainingBlock()), ('podcast', PodcastBlock()), ('competence', CompetenceBlock()), ('exercise', ExerciseBlock()), ('self_evaluation', SelfEvaluationBlock()), ('document', DocumentBlock()), ('knowledge', KnowledgeBlock()), ] contents = StreamField( StreamBlock(content_blocks), blank=False, min_num=1, max_num=1 ) content_panels = [ FieldPanel('title', classname="full title"), FieldPanel('minutes'), StreamFieldPanel('contents'), ] def get_admin_display_title(self): display_title = '' if self.package: display_title += f'{self.package}: ' if len(self.contents) > 0: display_title += f'{self.contents[0].block_type.capitalize()}: ' display_title += self.draft_title return display_title 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) @classmethod def get_serializer_class(cls): return get_it_serializer_class(cls, field_names=['id', 'title', 'minutes', 'package', 'contents', 'slug', 'type', 'translation_key']) 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