vbv/server/vbv_lernwelt/learnpath/models.py

361 lines
11 KiB
Python

import re
from django.db import models
from django.utils.text import slugify
from wagtail.admin.panels import FieldPanel, PageChooserPanel
from wagtail.fields import RichTextField
from wagtail.models import Page
from vbv_lernwelt.core.constants import DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER
from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.course.models import CourseBasePage, CoursePage
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 save(self, clean=True, user=None, log_action=False, **kwargs):
self.slug = find_available_slug(
slugify(f"{self.get_parent().slug}-lp", allow_unicode=True),
ignore_page_id=self.id,
)
super(LearningPath, self).save(clean, user, log_action, **kwargs)
def __str__(self):
return f"{self.title}"
def get_learning_path_url(self):
return f"/course/{self.slug.replace('-lp', '')}/learn"
def get_cockpit_url(self):
return f"/course/{self.slug.replace('-lp', '')}/cockpit"
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 save(self, clean=True, user=None, log_action=False, **kwargs):
self.slug = find_slug_with_parent_prefix(self, "topic")
super(Topic, self).save(clean, user, log_action, **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 Circle(CourseBasePage):
parent_page_types = ["learnpath.LearningPath"]
subpage_types = [
"learnpath.LearningSequence",
"learnpath.LearningUnit",
"learnpath.LearningContentAssignment",
"learnpath.LearningContentAttendanceDay",
"learnpath.LearningContentFeedback",
"learnpath.LearningContentLearningModule",
"learnpath.LearningContentMediaLibrary",
"learnpath.LearningContentPlaceholder",
"learnpath.LearningContentRichText",
"learnpath.LearningContentTest",
"learnpath.LearningContentVideo",
]
serialize_field_names = [
"children",
"description",
"goals",
]
description = models.TextField(default="", blank=True)
goals = RichTextField()
content_panels = Page.content_panels + [
FieldPanel("description"),
FieldPanel("goals"),
]
def get_frontend_url(self):
r = re.compile(r"^(?P<coursePart>.+?)-lp-circle-(?P<circlePart>.+?)$")
m = r.match(self.slug)
if m is None:
return "ERROR: could not parse slug"
return f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}"
def save(self, clean=True, user=None, log_action=False, **kwargs):
self.slug = find_slug_with_parent_prefix(self, "circle")
super(Circle, self).save(clean, user, log_action, **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"""
<span style="display: inline-flex; align-items: center; font-size: 1.25rem; font-weight: 700;">
<{self.icon} style="height: 32px; width: 32px;"></{self.icon}>
<span style="margin-left: 8px;">{self.draft_title}</span>
</span>"""
def get_admin_display_title(self):
return f"LS: {self.draft_title}"
def save(self, clean=True, user=None, log_action=False, **kwargs):
self.slug = find_slug_with_parent_prefix(self, "ls")
super(LearningSequence, self).save(clean, user, log_action, **kwargs)
def get_frontend_url(self):
r = re.compile(
r"^(?P<coursePart>.+?)-lp-circle-(?P<circlePart>.+?)-ls-(?P<lsPart>.+?)$"
)
m = r.match(self.slug)
if m is None:
return "ERROR: could not parse slug"
return f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}#ls-{m.group('lsPart')}"
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 save(self, clean=True, user=None, log_action=False, **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).save(clean, user, log_action, **kwargs)
def get_frontend_url(self):
r = re.compile(
r"^(?P<coursePart>.+?)-lp-circle-(?P<circlePart>.+?)-lu-(?P<luPart>.+?)$"
)
m = r.match(self.slug)
if m is None:
return "ERROR: could not parse slug"
return f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}#lu-{m.group('luPart')}"
def get_evaluate_url(self):
r = re.compile(
r"^(?P<coursePart>.+?)-lp-circle-(?P<circlePart>.+?)-lu-(?P<luPart>.+?)$"
)
m = r.match(self.slug)
return f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}/evaluate/{m.group('luPart')}"
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'<span style="font-weight: 700; font-size: 20px;">{self.draft_title}</span>'
class LearningContent(CourseBasePage):
class Meta:
abstract = True
serialize_field_names = [
"minutes",
"description",
"content_url",
]
minutes = models.PositiveIntegerField(default=15)
description = RichTextField(blank=True)
content_url = models.TextField(blank=True)
content_panels = [
FieldPanel("title", classname="full title"),
FieldPanel("minutes"),
FieldPanel("content_url"),
FieldPanel("description"),
]
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"""
<span style="display: inline-flex; align-items: center;">
<it-icon-checkbox-unchecked style="height: 24px; width: 24px;"></it-icon-checkbox-unchecked>
<span style="margin-left: 8px;">{self.get_admin_display_title()}</span>
</span>"""
def get_frontend_url(self):
r = re.compile(
r"^(?P<coursePart>.+?)-lp-circle-(?P<circlePart>.+?)-lc-(?P<lcPart>.+)$"
)
m = r.match(self.slug)
if m is None:
return "ERROR: could not parse slug"
return f"/course/{m.group('coursePart')}/learn/{m.group('circlePart')}/{m.group('lcPart')}"
def save(self, clean=True, user=None, log_action=False, **kwargs):
self.slug = find_slug_with_parent_prefix(self, "lc")
super().save(**kwargs)
class LearningContentAttendanceDay(LearningContent):
parent_page_types = ["learnpath.Circle"]
subpage_types = []
class LearningContentVideo(LearningContent):
parent_page_types = ["learnpath.Circle"]
subpage_types = []
class LearningContentPlaceholder(LearningContent):
parent_page_types = ["learnpath.Circle"]
subpage_types = []
class LearningContentFeedback(LearningContent):
parent_page_types = ["learnpath.Circle"]
subpage_types = []
class LearningContentLearningModule(LearningContent):
parent_page_types = ["learnpath.Circle"]
subpage_types = []
class LearningContentMediaLibrary(LearningContent):
parent_page_types = ["learnpath.Circle"]
subpage_types = []
class LearningContentTest(LearningContent):
parent_page_types = ["learnpath.Circle"]
subpage_types = []
class LearningContentRichText(LearningContent):
text = RichTextField(blank=True, features=DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER)
parent_page_types = ["learnpath.Circle"]
serialize_field_names = LearningContent.serialize_field_names + [
"text",
]
subpage_types = []
content_panels = LearningContent.content_panels + [
FieldPanel("text", classname="Text"),
]
class LearningContentAssignment(LearningContent):
serialize_field_names = LearningContent.serialize_field_names + [
"content_assignment_id",
]
parent_page_types = ["learnpath.Circle"]
subpage_types = []
content_assignment = models.ForeignKey(
"assignment.Assignment",
on_delete=models.PROTECT,
related_name="+",
)
content_panels = [
FieldPanel("title", classname="full title"),
FieldPanel("minutes"),
PageChooserPanel("content_assignment", "assignment.Assignment"),
FieldPanel("content_url"),
FieldPanel("description"),
]
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),
ignore_page_id=page.id,
)