# Create your models here. from django.utils.text import slugify from wagtail.core.blocks import StreamBlock from wagtail.core.fields import StreamField from wagtail.core.models import Page, Orderable from vbv_lernwelt.learnpath.models_competences import * from vbv_lernwelt.learnpath.models_learning_unit_content import WebBasedTrainingBlock, VideoBlock from grapple.helpers import register_query_field import graphene from grapple.models import ( GraphQLString, GraphQLPage, GraphQLStreamfield, GraphQLBoolean, GraphQLInt, GraphQLForeignKey, GraphQLField ) @register_query_field("learning_path") class LearningPath(Page): # PageChooserPanel('related_page', 'demo.PublisherPage'), content_panels = Page.content_panels + [ InlinePanel('topics', label="Topics"), ] subpage_types = ['learnpath.Circle'] graphql_fields = [ GraphQLString("title", required=True), ] 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'), ] graphql_fields = [ GraphQLString("title"), GraphQLBoolean("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"), ] # graphql_fields = [ GraphQLString("title", required=True), GraphQLString("description"), GraphQLString("goals"), ] 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'), FieldPanel('circle')] graphql_fields = [ GraphQLString("title", required=True), GraphQLBoolean("category"), ] class Meta: verbose_name = "Learning Sequence" def __str__(self): return f"{self.title}" def full_clean(self, *args, **kwargs): self.slug = find_available_slug(LearningSequence, slugify(self.title, allow_unicode=True)) super(LearningSequence, self).full_clean(*args, **kwargs) class LearningPackage(Orderable): title = models.CharField(max_length=256, default='') learning_sequence = models.ForeignKey( 'learnpath.LearningSequence', null=True, blank=True, on_delete=models.SET_NULL, related_name='learning_packages', ) panels = [FieldPanel('title')] graphql_fields = [ GraphQLString("title", required=False), ] def full_clean(self, *args, **kwargs): self.slug = find_available_slug(LearningPackage, slugify(self.title, allow_unicode=True)) super(LearningPackage, self).full_clean(*args, **kwargs) class LearningUnit(Page, Orderable): """ 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'] learning_package = models.ForeignKey( 'learnpath.LearningPackage', null=True, blank=True, on_delete=models.SET_NULL, 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"), FieldPanel('learning_package'), StreamFieldPanel('contents'), ] graphql_fields = [ GraphQLString("title", required=True), GraphQLStreamfield('contents') ] 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