from django.db import models from django.utils.text import slugify from wagtail import blocks from wagtail.admin.panels import FieldPanel, StreamFieldPanel from wagtail.blocks import StreamBlock from wagtail.fields import StreamField from wagtail.images.blocks import ImageChooserBlock from wagtail.models import Page from vbv_lernwelt.core.model_utils import find_available_slug from vbv_lernwelt.course.models import CourseBasePage, CoursePage from vbv_lernwelt.learnpath.models_learning_unit_content import ( AssignmentBlock, BookBlock, DocumentBlock, ExerciseBlock, FeedbackBlock, LearningModuleBlock, MediaLibraryBlock, OnlineTrainingBlock, PlaceholderBlock, ResourceBlock, TestBlock, VideoBlock, ) class LearningPath(CourseBasePage): serialize_field_names = ["children", "course"] content_panels = Page.content_panels subpage_types = ["learnpath.Circle", "learnpath.Topic"] parent_page_types = ["course.CoursePage"] class Meta: verbose_name = "Learning Path" def full_clean(self, *args, **kwargs): self.slug = find_available_slug( slugify(f"{self.get_parent().slug}-lp", allow_unicode=True) ) super(LearningPath, self).full_clean(*args, **kwargs) def __str__(self): return f"{self.title}" def get_frontend_url(self): return f"/course/{self.get_parent().slug}/learn" class Topic(CourseBasePage): serialize_field_names = ["is_visible"] is_visible = models.BooleanField(default=True) parent_page_types = ["learnpath.LearningPath"] panels = [ FieldPanel("title"), FieldPanel("is_visible"), ] def full_clean(self, *args, **kwargs): self.slug = find_slug_with_parent_prefix(self, "topic") super(Topic, self).full_clean(*args, **kwargs) def get_admin_display_title(self): return f"Thema: {self.draft_title}" class Meta: verbose_name = "Topic" def __str__(self): return f"{self.title}" class PersonBlock(blocks.StructBlock): first_name = blocks.CharBlock() last_name = blocks.CharBlock() email = blocks.EmailBlock() photo = ImageChooserBlock(required=False) biography = blocks.RichTextBlock(required=False) class Meta: icon = "user" class Circle(CourseBasePage): parent_page_types = ["learnpath.LearningPath"] subpage_types = [ "learnpath.LearningSequence", "learnpath.LearningUnit", "learnpath.LearningContent", ] serialize_field_names = [ "children", "description", "goal_description", "goals", "job_situation_description", "job_situations", "experts", ] description = models.TextField(default="", blank=True) goal_description = models.TextField(default="", blank=True) goals = StreamField( [ ("goal", blocks.TextBlock()), ], use_json_field=True, ) job_situation_description = models.TextField(default="", blank=True) job_situations = StreamField( [ ("job_situation", blocks.CharBlock()), ], use_json_field=True, ) experts = StreamField( [ ("person", PersonBlock()), ], use_json_field=True, ) content_panels = Page.content_panels + [ FieldPanel("description"), FieldPanel("goals"), FieldPanel("job_situations"), FieldPanel("experts"), ] def get_frontend_url(self): short_slug = self.slug.replace(f"{self.get_parent().slug}-circle-", "") return f"{self.get_parent().specific.get_frontend_url()}/{short_slug}" def full_clean(self, *args, **kwargs): self.slug = find_slug_with_parent_prefix(self, "circle") super(Circle, self).full_clean(*args, **kwargs) class Meta: verbose_name = "Circle" def __str__(self): return f"{self.title}" class LearningSequence(CourseBasePage): serialize_field_names = ["icon"] parent_page_types = ["learnpath.Circle"] subpage_types = [] icon = models.CharField(max_length=255, default="it-icon-ls-start") content_panels = Page.content_panels + [ FieldPanel("icon"), ] class Meta: verbose_name = "Learning Sequence" def __str__(self): return f"{self.title}" def get_admin_display_title(self): return f"{self.icon} {self.draft_title}" def get_admin_display_title_html(self): return f""" <{self.icon} style="height: 32px; width: 32px;"> {self.draft_title} """ def get_admin_display_title(self): return f"LS: {self.draft_title}" def full_clean(self, *args, **kwargs): self.slug = find_slug_with_parent_prefix(self, "ls") super(LearningSequence, self).full_clean(*args, **kwargs) def get_frontend_url(self): short_slug = self.slug.replace(f"{self.get_parent().slug}-", "") return f"{self.get_parent().specific.get_frontend_url()}#{short_slug}" class LearningUnit(CourseBasePage): parent_page_types = ["learnpath.Circle"] subpage_types = [] course_category = models.ForeignKey( "course.CourseCategory", on_delete=models.SET_NULL, null=True, blank=True ) content_panels = Page.content_panels + [ FieldPanel("course_category"), ] class Meta: verbose_name = "Learning Unit" def __str__(self): return f"{self.title}" def full_clean(self, *args, **kwargs): course = None course_parent_page = self.get_ancestors().exact_type(CoursePage).last() if course_parent_page: course = course_parent_page.specific.course if self.course_category is None and course: self.course_category = course.coursecategory_set.filter( general=True ).first() if self.course_category.general: self.slug = find_slug_with_parent_prefix(self, "lu") else: self.slug = find_slug_with_parent_prefix( self, "lu", self.course_category.title ) super(LearningUnit, self).full_clean(*args, **kwargs) def get_frontend_url(self): short_slug = self.slug.replace(f"{self.get_parent().slug}-lu-", "") return f"{self.get_parent().specific.get_frontend_url()}#lu-{short_slug}" def get_evaluate_url(self): short_slug = self.slug.replace(f"{self.get_parent().slug}-lu-", "") return f"{self.get_parent().specific.get_frontend_url()}/evaluate/{short_slug}" def get_admin_display_title(self): return f"LE: {self.draft_title}" @classmethod def get_serializer_class(cls): from vbv_lernwelt.learnpath.serializers import LearningUnitSerializer return LearningUnitSerializer def get_admin_display_title_html(self): return f'{self.draft_title}' class LearningContent(CourseBasePage): serialize_field_names = [ "minutes", "contents", ] parent_page_types = ["learnpath.Circle"] subpage_types = [] minutes = models.PositiveIntegerField(default=15) content_blocks = [ ("video", VideoBlock()), ("resource", ResourceBlock()), ("exercise", ExerciseBlock()), ("learningmodule", LearningModuleBlock()), ("online_training", OnlineTrainingBlock()), ("media_library", MediaLibraryBlock()), ("document", DocumentBlock()), ("test", TestBlock()), ("book", BookBlock()), ("assignment", AssignmentBlock()), ("placeholder", PlaceholderBlock()), ("feedback", FeedbackBlock()), ] 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 len(self.contents) > 0: display_title += f"{self.contents[0].block_type.capitalize()}: " display_title += self.draft_title return display_title def get_admin_display_title_html(self): return f""" {self.get_admin_display_title()} """ class Meta: verbose_name = "Learning Content" def get_frontend_url(self): short_slug = self.slug.replace(f"{self.get_parent().slug}-lc-", "") return f"{self.get_parent().specific.get_frontend_url()}/{short_slug}" def full_clean(self, *args, **kwargs): self.slug = find_slug_with_parent_prefix(self, "lc") super(LearningContent, self).full_clean(*args, **kwargs) def find_slug_with_parent_prefix(page, type_prefix, slug_postfix=None): parent_slug = page.get_ancestors().exact_type(LearningPath, Circle).last().slug if parent_slug: slug_prefix = f"{parent_slug}-{type_prefix}" else: slug_prefix = type_prefix if slug_postfix is None: slug_postfix = page.title return find_available_slug( slugify(f"{slug_prefix}-{slug_postfix}", allow_unicode=True) )