diff --git a/prepare_server.sh b/prepare_server.sh index ad375e5b..8fd54393 100755 --- a/prepare_server.sh +++ b/prepare_server.sh @@ -97,6 +97,7 @@ if [ "$SKIP_SETUP" = false ]; then echo "python server/manage.py create_default_courses $course_param" python server/manage.py create_default_courses $course_param python server/manage.py create_default_notifications + python server/manage.py create_default_assignments # make django translations (cd server && python manage.py compilemessages) diff --git a/server/vbv_lernwelt/assignment/apps.py b/server/vbv_lernwelt/assignment/apps.py index d80b629e..5a2ee9f3 100644 --- a/server/vbv_lernwelt/assignment/apps.py +++ b/server/vbv_lernwelt/assignment/apps.py @@ -2,5 +2,5 @@ from django.apps import AppConfig class AssignmentConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'assignment' + default_auto_field = "django.db.models.BigAutoField" + name = "vbv_lernwelt.assignment" diff --git a/server/vbv_lernwelt/assignment/creators/create_assignments.py b/server/vbv_lernwelt/assignment/creators/create_assignments.py new file mode 100644 index 00000000..9c5e6ebd --- /dev/null +++ b/server/vbv_lernwelt/assignment/creators/create_assignments.py @@ -0,0 +1,29 @@ +from wagtail_factories import StreamFieldFactory + +from vbv_lernwelt.assignment.tests.assignment_factories import ( + PerformanceObjectiveBlockFactory, + AssignmentPageFactory, + AssignmentFactory, + TaskBlockFactory, +) +from vbv_lernwelt.course.consts import COURSE_UK +from vbv_lernwelt.course.models import CoursePage + + +def create_assignments(): + course_page = CoursePage.objects.get(course_id=COURSE_UK) + assignment_page = AssignmentPageFactory( + parent=course_page, + ) + AssignmentFactory( + parent=assignment_page, + title="Auftrag 1", + performance_objectives=StreamFieldFactory( + { + "performance_objective": PerformanceObjectiveBlockFactory(), + } + ), + effort_required="1 - 2 Stunden", + assessment_document_url="https://www.vbv.ch", + tasks=StreamFieldFactory({"task": TaskBlockFactory()}), + ) diff --git a/server/vbv_lernwelt/assignment/management/commands/__init__.py b/server/vbv_lernwelt/assignment/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/assignment/management/commands/create_default_assignments.py b/server/vbv_lernwelt/assignment/management/commands/create_default_assignments.py new file mode 100644 index 00000000..1e2d6fc6 --- /dev/null +++ b/server/vbv_lernwelt/assignment/management/commands/create_default_assignments.py @@ -0,0 +1,9 @@ +import djclick as click + +from vbv_lernwelt.assignment.creators.create_assignments import create_assignments + + +@click.command() +def command(): + print("Creating default assignments") + create_assignments() diff --git a/server/vbv_lernwelt/assignment/migrations/0001_initial.py b/server/vbv_lernwelt/assignment/migrations/0001_initial.py index 7f2bc488..0ccccce3 100644 --- a/server/vbv_lernwelt/assignment/migrations/0001_initial.py +++ b/server/vbv_lernwelt/assignment/migrations/0001_initial.py @@ -1,8 +1,8 @@ -# Generated by Django 3.2.13 on 2023-03-28 08:53 +# Generated by Django 3.2.13 on 2023-03-30 13:30 -import assignment.models from django.db import migrations, models import django.db.models.deletion +import vbv_lernwelt.assignment.models import wagtail.blocks import wagtail.fields @@ -13,10 +13,24 @@ class Migration(migrations.Migration): dependencies = [ ('wagtailcore', '0069_log_entry_jsonfield'), - ('files', '0001_initial'), ] operations = [ + migrations.CreateModel( + name='Assignment', + 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')), + ('starting_position', models.TextField()), + ('performance_objectives', wagtail.fields.StreamField([('performance_objective', wagtail.blocks.StructBlock([('text', wagtail.blocks.TextBlock())]))], use_json_field=True)), + ('effort_required', models.TextField()), + ('assessment_document_url', models.TextField()), + ('tasks', wagtail.fields.StreamField([('task', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('file_submission_required', wagtail.blocks.BooleanBlock()), ('content', wagtail.blocks.StreamBlock([('explanation', wagtail.blocks.StructBlock([('text', wagtail.blocks.TextBlock())])), ('user_text_input', vbv_lernwelt.assignment.models.UserTextInputBlock()), ('user_confirmation', wagtail.blocks.StructBlock([('text', wagtail.blocks.TextBlock())]))], use_json_field=True))]))], use_json_field=True)), + ], + options={ + 'verbose_name': 'Auftrag', + }, + bases=('wagtailcore.page',), + ), migrations.CreateModel( name='AssignmentPage', fields=[ @@ -28,19 +42,10 @@ class Migration(migrations.Migration): bases=('wagtailcore.page',), ), migrations.CreateModel( - name='Assignment', + name='AssignmentSubmission', 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')), - ('starting_position', models.CharField(max_length=1024)), - ('due', models.DateTimeField()), - ('performance_objectives', wagtail.fields.StreamField([('performance_objective', assignment.models.PerformanceObjectiveBlock())], use_json_field=True)), - ('effort_required', models.CharField(max_length=255)), - ('tasks', wagtail.fields.StreamField([('task', wagtail.blocks.StructBlock([('title', wagtail.blocks.CharBlock()), ('file_submission_required', wagtail.blocks.BooleanBlock()), ('content', wagtail.blocks.StreamBlock([('explanation', assignment.models.ExplanationBlock()), ('user_text_input', assignment.models.UserTextInputBlock()), ('user_confirmation', assignment.models.UserConfirmationBlock())], use_json_field=True))]))], use_json_field=True)), - ('assessment_tool', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, to='files.uploadfile')), + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), ], - options={ - 'verbose_name': 'Auftrag', - }, - bases=('wagtailcore.page',), ), ] diff --git a/server/vbv_lernwelt/assignment/models.py b/server/vbv_lernwelt/assignment/models.py index bed1ba4c..6dc99c17 100644 --- a/server/vbv_lernwelt/assignment/models.py +++ b/server/vbv_lernwelt/assignment/models.py @@ -1,20 +1,26 @@ from django.db import models -from django.db.models import DateTimeField, CharField +from django.db.models import Model, TextField from wagtail import blocks from wagtail.admin.panels import FieldPanel from wagtail.blocks import StreamBlock from wagtail.fields import StreamField from wagtail.models import Page -from vbv_lernwelt.files.models import UploadFile + +class AssignmentSubmission(Model): + created_at = models.DateTimeField(auto_now_add=True) -class ExplanationBlock(blocks.CharBlock): +class ExplanationBlock(blocks.StructBlock): + text = blocks.TextBlock() + class Meta: icon = "comment" -class PerformanceObjectiveBlock(blocks.CharBlock): +class PerformanceObjectiveBlock(blocks.StructBlock): + text = blocks.TextBlock() + class Meta: icon = "tick" @@ -24,8 +30,8 @@ class UserTextInputBlock(blocks.StaticBlock): icon = "edit" -class UserConfirmationBlock(blocks.CharBlock): - confirmation_text = blocks.BooleanBlock() +class UserConfirmationBlock(blocks.StructBlock): + text = blocks.TextBlock() class Meta: icon = "tick-inverse" @@ -49,17 +55,15 @@ class TaskBlock(blocks.StructBlock): class Assignment(Page): - # TODO: Referenz auf durchfhrung - starting_position = CharField(max_length=1024) - due = DateTimeField() # Zwingend teil der Durchführung + starting_position = TextField() performance_objectives = StreamField( [ ("performance_objective", PerformanceObjectiveBlock()), ], use_json_field=True, ) - effort_required = CharField(max_length=255) - assessment_tool = models.OneToOneField(UploadFile, on_delete=models.PROTECT) + effort_required = TextField() + assessment_document_url = TextField() tasks = StreamField( [ ("task", TaskBlock()), @@ -69,16 +73,18 @@ class Assignment(Page): content_panels = Page.content_panels + [ FieldPanel("starting_position"), - FieldPanel("due"), FieldPanel("performance_objectives"), FieldPanel("effort_required"), - FieldPanel("assessment_tool"), + FieldPanel("assessment_document_url"), FieldPanel("tasks"), ] + subpage_types = [] + class Meta: verbose_name = "Auftrag" class AssignmentPage(Page): subpage_types = ["assignment.Assignment"] + parent_page_types = ["course.CoursePage"] diff --git a/server/vbv_lernwelt/assignment/tests.py b/server/vbv_lernwelt/assignment/tests.py deleted file mode 100644 index 7ce503c2..00000000 --- a/server/vbv_lernwelt/assignment/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/server/vbv_lernwelt/assignment/tests/__init__.py b/server/vbv_lernwelt/assignment/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/assignment/tests/assignment_factories.py b/server/vbv_lernwelt/assignment/tests/assignment_factories.py index 1c4d281c..69e4130a 100644 --- a/server/vbv_lernwelt/assignment/tests/assignment_factories.py +++ b/server/vbv_lernwelt/assignment/tests/assignment_factories.py @@ -1,48 +1,60 @@ import wagtail_factories from vbv_lernwelt.assignment.models import AssignmentPage, Assignment, TaskBlock +from vbv_lernwelt.assignment.models import ( + ExplanationBlock, + UserConfirmationBlock, + PerformanceObjectiveBlock, +) class ExplanationBlockFactory(wagtail_factories.StructBlockFactory): - class Meta: - model = "assignment.ExplanationBlock" - text = """Erläutere die Kundensituation und die Ausgangslage. Hast du alle Informationen, die du für den Policen-Check benötigst? Halte die wichtigsten Eckwerte des aktuellen Versicherungsverhältnisse in deiner Dokumentation fest (z.B wie lang wo versichert, Alter des Fahrzeugs, Kundenprofil, etc.) """ - -class UserTextInputBlockFactory(wagtail_factories.StructBlockFactory): class Meta: - model = "assignment.UserTextInputBlock" + model = ExplanationBlock class UserConfirmationBlockFactory(wagtail_factories.StructBlockFactory): - class Meta: - model = "assignment.UserConfirmationBlock" + text = "Ja, ich habe Motorfahrzeugversicherungspolice von jemandem aus meiner Familie oder meinem Freundeskreis erhalten." - confirmation_text = "Ja, ich habe Motorfahrzeugversicherungspolice von jemandem aus meiner Familie oder meinem Freundeskreis erhalten." + class Meta: + model = UserConfirmationBlock class TaskBlockFactory(wagtail_factories.StructBlockFactory): - class Meta: - model = TaskBlock - title = "Teilauftrag" file_submission_required = False content = wagtail_factories.StreamFieldFactory( [ ("explanation", ExplanationBlockFactory()), - ("user_text_input", UserTextInputBlockFactory()), + ("user_text_input", "static_block"), ("user_confirmation", UserConfirmationBlockFactory()), ], use_json_field=True, ) + class Meta: + model = TaskBlock + + +class PerformanceObjectiveBlockFactory(wagtail_factories.StructBlockFactory): + text = "Die Teilnehmer können die wichtigsten Eckwerte eines Versicherungsverhältnisses erfassen." + + class Meta: + model = PerformanceObjectiveBlock + class AssignmentFactory(wagtail_factories.PageFactory): title = "Auftrag" + starting_position = """Jemand aus deiner Familie oder aus deinem Freundeskreis möchte sein + Versicherungspolice überprüfen lassen. Diese Person kommt nun mit ihrer Police auf dich zu + und bittet dich als Versicherungsprofi, diese kritisch zu überprüfen und ihr gg. Anpassungsvorschläge + zu unterbreiten. In diesem Kompetenznachweis kannst du nun dein Wissen und Können im Bereich + der Motorfahrzeugversicherung unter Beweis stellen.""" class Meta: model = Assignment diff --git a/server/vbv_lernwelt/learnpath/migrations/0013_alter_learningcontent_contents.py b/server/vbv_lernwelt/learnpath/migrations/0013_alter_learningcontent_contents.py new file mode 100644 index 00000000..dc53258c --- /dev/null +++ b/server/vbv_lernwelt/learnpath/migrations/0013_alter_learningcontent_contents.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2023-03-29 13:39 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('learnpath', '0012_auto_20230309_0711'), + ] + + operations = [ + migrations.AlterField( + model_name='learningcontent', + name='contents', + field=wagtail.fields.StreamField([('video', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('resource', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock()), ('text', wagtail.blocks.RichTextBlock(required=False))])), ('exercise', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('learningmodule', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('online_training', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('media_library', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('document', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('test', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('book', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('assignment', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('placeholder', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('feedback', wagtail.blocks.StructBlock([]))], use_json_field=None), + ), + ] diff --git a/server/vbv_lernwelt/learnpath/migrations/0014_alter_learningcontent_contents.py b/server/vbv_lernwelt/learnpath/migrations/0014_alter_learningcontent_contents.py new file mode 100644 index 00000000..bc33968f --- /dev/null +++ b/server/vbv_lernwelt/learnpath/migrations/0014_alter_learningcontent_contents.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2023-03-29 13:44 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('learnpath', '0013_alter_learningcontent_contents'), + ] + + operations = [ + migrations.AlterField( + model_name='learningcontent', + name='contents', + field=wagtail.fields.StreamField([('video', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('resource', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock()), ('text', wagtail.blocks.RichTextBlock(required=False))])), ('exercise', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('learningmodule', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('online_training', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('media_library', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('document', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('test', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('book', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('assignment', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock())])), ('placeholder', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('feedback', wagtail.blocks.StructBlock([]))], use_json_field=None), + ), + ] diff --git a/server/vbv_lernwelt/learnpath/migrations/0015_alter_learningcontent_contents.py b/server/vbv_lernwelt/learnpath/migrations/0015_alter_learningcontent_contents.py new file mode 100644 index 00000000..3d1c1e58 --- /dev/null +++ b/server/vbv_lernwelt/learnpath/migrations/0015_alter_learningcontent_contents.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2023-03-30 13:30 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('learnpath', '0014_alter_learningcontent_contents'), + ] + + operations = [ + migrations.AlterField( + model_name='learningcontent', + name='contents', + field=wagtail.fields.StreamField([('video', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('resource', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock()), ('text', wagtail.blocks.RichTextBlock(required=False))])), ('exercise', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('learningmodule', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('online_training', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('media_library', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('document', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('test', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('book', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('assignment', wagtail.blocks.StructBlock([('assignment_slug', wagtail.blocks.TextBlock())])), ('placeholder', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.TextBlock())])), ('feedback', wagtail.blocks.StructBlock([]))], use_json_field=None), + ), + ] diff --git a/server/vbv_lernwelt/learnpath/models_learning_unit_content.py b/server/vbv_lernwelt/learnpath/models_learning_unit_content.py index ce7351a8..5efef576 100644 --- a/server/vbv_lernwelt/learnpath/models_learning_unit_content.py +++ b/server/vbv_lernwelt/learnpath/models_learning_unit_content.py @@ -2,9 +2,11 @@ from wagtail import blocks class AssignmentBlock(blocks.StructBlock): - description = blocks.TextBlock() - url = blocks.TextBlock() - text = blocks.RichTextBlock(required=False) + # TODO: Find way to let user select assignment through foreign key + # Wagtail block data is not stored as "true" database objects, + # but only as JSON text stored against the page, so there's no way to define relations such as ForeignKeys. + # A possible solution are InlinePanels: https://docs.wagtail.org/en/stable/reference/pages/panels.html#inlinepanel + assignment_slug = blocks.TextBlock() class Meta: icon = "media"