Working serialization

This commit is contained in:
Daniel Egger 2022-06-01 22:01:03 +02:00
parent 4a2d049969
commit f480665537
13 changed files with 135 additions and 150 deletions

View File

@ -18,9 +18,9 @@ from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
from vbv_lernwelt.core.views import (
rate_limit_exceeded_view,
permission_denied_view,
check_rate_limit, vue_home, cypress_reset_view,
check_rate_limit, cypress_reset_view,
)
from .wagtail_api import api_router
from .wagtail_api import wagtail_api_router
def raise_example_error(request):
@ -42,6 +42,7 @@ urlpatterns = [
path('cms/', include(wagtailadmin_urls)),
path('documents/', include(wagtaildocs_urls)),
path('pages/', include(wagtail_urls)),
path('learnpath/', include("vbv_lernwelt.learnpath.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
# Static file serving when using Gunicorn + Uvicorn for local web socket development
@ -56,7 +57,7 @@ if settings.ALLOW_LOCAL_LOGIN:
urlpatterns += [
# API base url
path("api/", include("config.api_router")),
path('wagtailapi/v2/', api_router.urls),
path('wagtailapi/v2/', wagtail_api_router.urls),
# DRF auth token
path("auth-token/", obtain_auth_token),
@ -111,5 +112,5 @@ if settings.DEBUG:
# serve everything else via the vue app
urlpatterns += [re_path(r'^.*$', vue_home, name='home')]
# urlpatterns += [re_path(r'^.*$', vue_home, name='home')]

View File

@ -1,16 +1,16 @@
from wagtail.api.v2.views import PagesAPIViewSet
from wagtail.api.v2.router import WagtailAPIRouter
from wagtail.images.api.v2.views import ImagesAPIViewSet
from wagtail.api.v2.views import PagesAPIViewSet
from wagtail.documents.api.v2.views import DocumentsAPIViewSet
from wagtail.images.api.v2.views import ImagesAPIViewSet
# Create the router. "wagtailapi" is the URL namespace
api_router = WagtailAPIRouter('wagtailapi')
wagtail_api_router = WagtailAPIRouter('wagtailapi')
# Add the three endpoints using the "register_endpoint" method.
# The first parameter is the name of the endpoint (eg. pages, images). This
# is used in the URL of the endpoint
# The second parameter is the endpoint class that handles the requests
api_router.register_endpoint('pages', PagesAPIViewSet)
api_router.register_endpoint('images', ImagesAPIViewSet)
api_router.register_endpoint('documents', DocumentsAPIViewSet)
wagtail_api_router.register_endpoint('pages', PagesAPIViewSet)
wagtail_api_router.register_endpoint('images', ImagesAPIViewSet)
wagtail_api_router.register_endpoint('documents', DocumentsAPIViewSet)

View File

@ -1,4 +1,4 @@
# Generated by Django 3.2.12 on 2022-06-01 13:32
# Generated by Django 3.2.12 on 2022-06-01 15:39
from django.db import migrations, models
import django.db.models.deletion
@ -16,19 +16,6 @@ class Migration(migrations.Migration):
]
operations = [
migrations.CreateModel(
name='Circle',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
('description', models.TextField(blank=True, default='')),
('goals', models.TextField(blank=True, default='')),
],
options={
'verbose_name': 'Circle',
},
bases=('wagtailcore.page', models.Model),
),
migrations.CreateModel(
name='Competence',
fields=[
@ -64,14 +51,23 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='LearningSequence',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
('title', models.CharField(default='', max_length=256)),
('circle', modelcluster.fields.ParentalKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='learning_sequences', to='learnpath.circle')),
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
],
options={
'verbose_name': 'Learning Sequence',
},
bases=('wagtailcore.page',),
),
migrations.CreateModel(
name='LearningUnit',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
('contents', wagtail.core.fields.StreamField([('web_based_training', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('video', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())]))], blank=True, null=True)),
],
options={
'verbose_name': 'Learning Unit',
},
bases=('wagtailcore.page',),
),
migrations.CreateModel(
name='Topic',
@ -86,19 +82,6 @@ class Migration(migrations.Migration):
'verbose_name': 'Topic',
},
),
migrations.CreateModel(
name='LearningUnit',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
('contents', wagtail.core.fields.StreamField([('web_based_training', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())])), ('video', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())]))], blank=True, null=True)),
('learning_sequence', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='learning_units', to='learnpath.learningsequence')),
],
options={
'verbose_name': 'Learning Unit',
},
bases=('wagtailcore.page', models.Model),
),
migrations.CreateModel(
name='FullfillmentCriteria',
fields=[
@ -116,9 +99,17 @@ class Migration(migrations.Migration):
name='competence_page',
field=modelcluster.fields.ParentalKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='competences', to='learnpath.competencepage'),
),
migrations.AddField(
model_name='circle',
name='topic',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='circles', to='learnpath.topic'),
migrations.CreateModel(
name='Circle',
fields=[
('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
('description', models.TextField(blank=True, default='')),
('goals', models.TextField(blank=True, default='')),
('topic', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='circles', to='learnpath.topic')),
],
options={
'verbose_name': 'Circle',
},
bases=('wagtailcore.page',),
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 3.2.12 on 2022-06-01 14:18
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('learnpath', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='learningunit',
name='sort_order',
),
]

View File

@ -1,17 +0,0 @@
# Generated by Django 3.2.12 on 2022-06-01 14:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('learnpath', '0002_remove_learningunit_sort_order'),
]
operations = [
migrations.RemoveField(
model_name='circle',
name='sort_order',
),
]

View File

@ -6,10 +6,10 @@ from wagtail.api import APIField
from wagtail.core.blocks import StreamBlock
from wagtail.core.fields import StreamField
from wagtail.core.models import Page, Orderable
from wagtail.snippets.models import register_snippet
from vbv_lernwelt.learnpath.models_competences import *
from vbv_lernwelt.learnpath.models_learning_unit_content import WebBasedTrainingBlock, VideoBlock
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
@register_query_field("learning_path")
@ -22,7 +22,6 @@ class LearningPath(Page):
subpage_types = ['learnpath.Circle']
class Meta:
verbose_name = "Learning Path"
@ -83,23 +82,20 @@ class Circle(Page):
)
parent_page_types = ['learnpath.LearningPath']
subpage_types = ['learnpath.LearningUnit']
subpage_types = ['learnpath.LearningSequence', 'learnpath.LearningUnit']
content_panels = Page.content_panels + [
FieldPanel('description'),
FieldPanel('goals'),
InlinePanel('learning_sequences', label="Learning Sequences"),
]
api_fields = [
APIField('title'),
APIField('description'),
APIField('topic'),
APIField('learning_sequences'),
]
def full_clean(self, *args, **kwargs):
self.slug = find_available_slug(Circle, slugify(self.title, allow_unicode=True))
# self.slug = find_available_slug(Circle, slugify(self.title, allow_unicode=True))
super(Circle, self).full_clean(*args, **kwargs)
class Meta:
@ -109,33 +105,21 @@ class Circle(Page):
return f"{self.title}"
@register_snippet
class LearningSequence(Orderable):
# TODO: How to do a icon choice field?
title = models.CharField(max_length=256, default='')
circle = ParentalKey(
'learnpath.Circle',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='learning_sequences',
)
class LearningSequence(Page):
parent_page_types = ['learnpath.Circle']
panels = [FieldPanel('title'), FieldPanel('circle')]
api_fields = [
APIField('title'),
APIField('category'),
APIField('learning_packages'),
]
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', 'slug', 'type'])
def full_clean(self, *args, **kwargs):
super(LearningSequence, self).full_clean(*args, **kwargs)
@ -146,14 +130,7 @@ class LearningUnit(Page):
"""
# TODO: Review model architecture, is the stream field the right thing here?
parent_page_types = ['learnpath.Circle']
learning_sequence = models.ForeignKey(
'learnpath.LearningSequence',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='learning_units',
)
parent_page_types = ['learnpath.LearningSequence']
content_blocks = [
('web_based_training', WebBasedTrainingBlock()),
@ -166,14 +143,13 @@ class LearningUnit(Page):
content_panels = [
FieldPanel('title', classname="full title"),
FieldPanel('learning_sequence'),
# FieldPanel('learning_sequence'),
StreamFieldPanel('contents'),
]
api_fields = [
APIField('title'),
APIField('contents'),
APIField('content_blocks'),
]
subpage_types = []
@ -185,6 +161,10 @@ class LearningUnit(Page):
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', 'contents', 'slug', 'type'])
def __str__(self):
return f"{self.title}"

View File

@ -11,9 +11,6 @@ class VideoBlock(blocks.StructBlock):
class Meta:
icon = 'media'
def get_api_representation(self, value, context=None):
return {'sdfsdf': 1,
'sldkfm': 3}
RISE = 'rise'
@ -32,8 +29,3 @@ class WebBasedTrainingBlock(blocks.StructBlock):
class Meta:
icon = 'media'
def get_api_representation(self, value, context=None):
return {'sdfsdf': 1,
'sldkfm': 3}

View File

@ -0,0 +1,15 @@
import wagtail.api.v2.serializers as wagtail_serializers
def get_it_serializer_class(model, field_names):
return wagtail_serializers.get_serializer_class(model, field_names=field_names, meta_fields=[], base=ItBaseSerializer)
class ItTypeField(wagtail_serializers.TypeField):
def to_representation(self, obj):
name = type(obj)._meta.app_label + '.' + type(obj).__name__
return name
class ItBaseSerializer(wagtail_serializers.BaseSerializer):
type = ItTypeField(read_only=True)

View File

@ -0,0 +1,20 @@
from rest_framework import serializers
from vbv_lernwelt.learnpath.models import Circle
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
class CircleSerializer(get_it_serializer_class(Circle, [])):
children = serializers.SerializerMethodField()
meta_fields = []
def get_children(self, obj):
return [c.specific.get_serializer_class()(c.specific).data for c in obj.get_children()]
def get_meta_label(self, obj):
return obj._meta.label
class Meta:
model = Circle
fields = ['id', 'title', 'slug', 'children', 'type']

View File

@ -62,10 +62,10 @@ von Neukunden zu benützen
Lösungsvorschläge zu skizzieren und
zu visualisieren""")
sequence_1 = LearningSequenceFactory(title="Starten", circle=circle_4)
sequence_1 = LearningSequenceFactory(title="Starten", parent=circle_4)
learning_unit = LearningUnitFactory(title='Einleitung Circle "Anlayse"', parent=circle_4, learning_sequence=sequence_1)
learning_unit = LearningUnitFactory.create(title='** Einstieg Video"', parent=circle_4, learning_sequence=sequence_1)
learning_unit = LearningUnitFactory(title='Einleitung Circle "Anlayse"', parent=circle_4)
learning_unit = LearningUnitFactory.create(title='** Einstieg Video"', parent=circle_4)
video_url = "https://www.vbv.ch/fileadmin/vbv/Videos/Statements_Externe/Janos_M/Testimonial_Janos_Mischler_PositiveEffekte.mp4"
video_title = "Ausbildung ist pflicht"
video_description = "Erfahren Sie, was für Janos Mischler die positiven Aspekte von ständiger Weiterbildung sind aus fachlicher und aus persönlicher Sicht."
@ -73,38 +73,38 @@ von Neukunden zu benützen
learning_unit.contents.append(('video', video_block))
learning_unit.save()
learning_unit = LearningUnitFactory.create(title='** Web Based Training"', parent=circle_4, learning_sequence=sequence_1)
learning_unit = LearningUnitFactory.create(title='** Web Based Training"', parent=circle_4)
wbt_url = "web_based_trainings/rise_cmi5_test_export/scormcontent/index.html"
wbt_block = WebBasedTrainingBlockFactory(content_type="web_based_training", url=wbt_url)
learning_unit.contents.append(('web_based_training', wbt_block))
learning_unit.save()
learning_unit = LearningUnitFactory.create(title="Selbsteinschätzung", parent=circle_4, learning_sequence=sequence_1)
learning_unit = LearningUnitFactory.create(title="Selbsteinschätzung", parent=circle_4)
sequence_2 = LearningSequenceFactory.create(title="Beobachten", circle=circle_4)
learning_unit = LearningUnitFactory.create(title="Mein Motorfahrzeug kaufen", parent=circle_4, learning_sequence=sequence_2)
learning_unit = LearningUnitFactory.create(title="Sich selbständig machen", parent=circle_4, learning_sequence=sequence_2)
sequence_2 = LearningSequenceFactory.create(title="Beobachten", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Mein Motorfahrzeug kaufen", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Sich selbständig machen", parent=circle_4)
sequence_3 = LearningSequenceFactory.create(title="Anwenden", circle=circle_4)
learning_unit = LearningUnitFactory.create(title="Nora kauft sich ein neues Auto", parent=circle_4, learning_sequence=sequence_3)
learning_unit = LearningUnitFactory.create(title="Manuel träumt von einem neuen Tesla", parent=circle_4, learning_sequence=sequence_3)
learning_unit = LearningUnitFactory.create(title="Deine Erkenntnisse und Learnings", parent=circle_4, learning_sequence=sequence_3)
sequence_3 = LearningSequenceFactory.create(title="Anwenden", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Nora kauft sich ein neues Auto", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Manuel träumt von einem neuen Tesla", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Deine Erkenntnisse und Learnings", parent=circle_4)
sequence_4 = LearningSequenceFactory.create(title="Üben", circle=circle_4)
learning_unit = LearningUnitFactory.create(title="Ermittlung des Kundenbedarfs", parent=circle_4, learning_sequence=sequence_4)
learning_unit = LearningUnitFactory.create(title="Aktives Zuhören", parent=circle_4, learning_sequence=sequence_4)
learning_unit = LearningUnitFactory.create(title="In Bildern Sprechen", parent=circle_4, learning_sequence=sequence_4)
learning_unit = LearningUnitFactory.create(title="Priorisieren des Bedarfs", parent=circle_4, learning_sequence=sequence_4)
learning_unit = LearningUnitFactory.create(title="Zusammenfassung des Bedarfs", parent=circle_4, learning_sequence=sequence_4)
sequence_4 = LearningSequenceFactory.create(title="Üben", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Ermittlung des Kundenbedarfs", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Aktives Zuhören", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="In Bildern Sprechen", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Priorisieren des Bedarfs", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Zusammenfassung des Bedarfs", parent=circle_4)
sequence_5 = LearningSequenceFactory.create(title="Testen", circle=circle_4)
learning_unit = LearningUnitFactory.create(title="Bedarfsfragen", parent=circle_4, learning_sequence=sequence_5)
learning_unit = LearningUnitFactory.create(title="Andwendung der Fragetechniken", parent=circle_4, learning_sequence=sequence_5)
sequence_5 = LearningSequenceFactory.create(title="Testen", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Bedarfsfragen", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Andwendung der Fragetechniken", parent=circle_4)
sequence_5 = LearningSequenceFactory.create(title="Vernetzen", circle=circle_4)
learning_unit = LearningUnitFactory.create(title="Online Training", parent=circle_4, learning_sequence=sequence_5)
sequence_5 = LearningSequenceFactory.create(title="Vernetzen", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Online Training", parent=circle_4)
sequence_6 = LearningSequenceFactory.create(title="Beenden", circle=circle_4)
learning_unit = LearningUnitFactory.create(title="Selbsteinschätzung", parent=circle_4, learning_sequence=sequence_6)
sequence_6 = LearningSequenceFactory.create(title="Beenden", parent=circle_4)
learning_unit = LearningUnitFactory.create(title="Selbsteinschätzung", parent=circle_4)
circle_5 = CircleFactory.create(title="Lösung",
parent=lp,

View File

@ -27,7 +27,7 @@ class CircleFactory(wagtail_factories.PageFactory):
model = Circle
class LearningSequenceFactory(factory.django.DjangoModelFactory):
class LearningSequenceFactory(wagtail_factories.PageFactory):
title = "Grundlagen"
class Meta:

View File

@ -0,0 +1,10 @@
from django.conf.urls import url, include
from django.urls import path
from rest_framework.routers import DefaultRouter
from . import views
from .views import circle_view
urlpatterns = [
path(r"api/circle/<slug:slug>/", circle_view, name="circle_view"),
]

View File

@ -1,3 +1,13 @@
from django.shortcuts import render
# Create your views here.
from rest_framework.decorators import api_view
from rest_framework.response import Response
from vbv_lernwelt.learnpath.models import Circle
from vbv_lernwelt.learnpath.serializers import CircleSerializer
@api_view(['GET'])
def circle_view(request, slug):
circle = Circle.objects.get(slug=slug)
serializer = CircleSerializer(circle)
return Response(serializer.data)