vbv/server/vbv_lernwelt/learnpath/models.py

265 lines
7.2 KiB
Python

# 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