From 9c775266466f18c317b5bff43bec5af8611dc5a4 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 27 Sep 2022 15:57:17 +0200 Subject: [PATCH] Add initial competence django app --- server/config/settings/base.py | 1 + server/vbv_lernwelt/competence/__init__.py | 0 server/vbv_lernwelt/competence/admin.py | 3 + server/vbv_lernwelt/competence/apps.py | 6 ++ .../create_default_competence_profile.py | 21 ++++ server/vbv_lernwelt/competence/factories.py | 18 ++++ .../competence/management/__init__.py | 9 ++ .../management/commands/__init__.py | 9 ++ .../create_default_competence_profile.py | 8 ++ .../competence/migrations/0001_initial.py | 39 +++++++ .../competence/migrations/__init__.py | 0 server/vbv_lernwelt/competence/models.py | 50 +++++++++ .../vbv_lernwelt/competence/tests/__init__.py | 9 ++ .../tests/media_library_factories.py | 100 ++++++++++++++++++ .../tests/test_create_default_documents.py | 25 +++++ .../test_create_default_media_library.py | 29 +++++ .../tests/test_media_library_factories.py | 47 ++++++++ server/vbv_lernwelt/competence/views.py | 0 .../0002_alter_learningcontent_contents.py | 20 ++++ 19 files changed, 394 insertions(+) create mode 100644 server/vbv_lernwelt/competence/__init__.py create mode 100644 server/vbv_lernwelt/competence/admin.py create mode 100644 server/vbv_lernwelt/competence/apps.py create mode 100644 server/vbv_lernwelt/competence/create_default_competence_profile.py create mode 100644 server/vbv_lernwelt/competence/factories.py create mode 100644 server/vbv_lernwelt/competence/management/__init__.py create mode 100644 server/vbv_lernwelt/competence/management/commands/__init__.py create mode 100644 server/vbv_lernwelt/competence/management/commands/create_default_competence_profile.py create mode 100644 server/vbv_lernwelt/competence/migrations/0001_initial.py create mode 100644 server/vbv_lernwelt/competence/migrations/__init__.py create mode 100644 server/vbv_lernwelt/competence/models.py create mode 100644 server/vbv_lernwelt/competence/tests/__init__.py create mode 100644 server/vbv_lernwelt/competence/tests/media_library_factories.py create mode 100644 server/vbv_lernwelt/competence/tests/test_create_default_documents.py create mode 100644 server/vbv_lernwelt/competence/tests/test_create_default_media_library.py create mode 100644 server/vbv_lernwelt/competence/tests/test_media_library_factories.py create mode 100644 server/vbv_lernwelt/competence/views.py create mode 100644 server/vbv_lernwelt/learnpath/migrations/0002_alter_learningcontent_contents.py diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 047a5627..2a0bd504 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -105,6 +105,7 @@ LOCAL_APPS = [ "vbv_lernwelt.sso", "vbv_lernwelt.course", "vbv_lernwelt.learnpath", + "vbv_lernwelt.competence", "vbv_lernwelt.media_library", "vbv_lernwelt.completion", ] diff --git a/server/vbv_lernwelt/competence/__init__.py b/server/vbv_lernwelt/competence/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/competence/admin.py b/server/vbv_lernwelt/competence/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/server/vbv_lernwelt/competence/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/server/vbv_lernwelt/competence/apps.py b/server/vbv_lernwelt/competence/apps.py new file mode 100644 index 00000000..a8310128 --- /dev/null +++ b/server/vbv_lernwelt/competence/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class MediaLibraryConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'vbv_lernwelt.competence' diff --git a/server/vbv_lernwelt/competence/create_default_competence_profile.py b/server/vbv_lernwelt/competence/create_default_competence_profile.py new file mode 100644 index 00000000..1d99baaf --- /dev/null +++ b/server/vbv_lernwelt/competence/create_default_competence_profile.py @@ -0,0 +1,21 @@ +from vbv_lernwelt.competence.factories import CompetenceProfilePageFactory, PerformanceCriteriaFactory +from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN +from vbv_lernwelt.course.models import CoursePage, Course +from vbv_lernwelt.learnpath.models import LearningUnit + + +def create_default_competence_profile(): + course = Course.objects.get(id=COURSE_VERSICHERUNGSVERMITTLERIN) + course_page = CoursePage.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN) + + competence_profile_page = CompetenceProfilePageFactory( + title='Kompetenzprofil', + parent=course_page, + ) + + PerformanceCriteriaFactory( + parent=competence_profile_page, + title='B1.3 Fahrzeug', + text='Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).', + learning_unit=LearningUnit.objects.get(slug='versicherungsvermittlerin-lp-circle-analyse-lu-auto-verkaufen'), + ) diff --git a/server/vbv_lernwelt/competence/factories.py b/server/vbv_lernwelt/competence/factories.py new file mode 100644 index 00000000..1a3f918a --- /dev/null +++ b/server/vbv_lernwelt/competence/factories.py @@ -0,0 +1,18 @@ +import wagtail_factories + +from vbv_lernwelt.competence.models import CompetenceProfilePage, PerformanceCriteria + + +class CompetenceProfilePageFactory(wagtail_factories.PageFactory): + title = 'Kompetenzprofil' + + class Meta: + model = CompetenceProfilePage + + +class PerformanceCriteriaFactory(wagtail_factories.PageFactory): + title = 'A.1' + text = 'Bestehende Kunden so zu beraten, dass sie von diesen weiterempfohlen werden' + + class Meta: + model = PerformanceCriteria diff --git a/server/vbv_lernwelt/competence/management/__init__.py b/server/vbv_lernwelt/competence/management/__init__.py new file mode 100644 index 00000000..2b30a24a --- /dev/null +++ b/server/vbv_lernwelt/competence/management/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# +# Iterativ GmbH +# http://www.iterativ.ch/ +# +# Copyright (c) 2015 Iterativ GmbH. All rights reserved. +# +# Created on 2022-08-18 +# @author: lorenz.padberg@iterativ.ch diff --git a/server/vbv_lernwelt/competence/management/commands/__init__.py b/server/vbv_lernwelt/competence/management/commands/__init__.py new file mode 100644 index 00000000..2b30a24a --- /dev/null +++ b/server/vbv_lernwelt/competence/management/commands/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# +# Iterativ GmbH +# http://www.iterativ.ch/ +# +# Copyright (c) 2015 Iterativ GmbH. All rights reserved. +# +# Created on 2022-08-18 +# @author: lorenz.padberg@iterativ.ch diff --git a/server/vbv_lernwelt/competence/management/commands/create_default_competence_profile.py b/server/vbv_lernwelt/competence/management/commands/create_default_competence_profile.py new file mode 100644 index 00000000..dfa6a993 --- /dev/null +++ b/server/vbv_lernwelt/competence/management/commands/create_default_competence_profile.py @@ -0,0 +1,8 @@ +import djclick as click + +from vbv_lernwelt.competence.create_default_competence_profile import create_default_competence_profile + + +@click.command() +def command(): + create_default_competence_profile() diff --git a/server/vbv_lernwelt/competence/migrations/0001_initial.py b/server/vbv_lernwelt/competence/migrations/0001_initial.py new file mode 100644 index 00000000..3ea97541 --- /dev/null +++ b/server/vbv_lernwelt/competence/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 3.2.13 on 2022-09-27 13:54 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('wagtailcore', '0069_log_entry_jsonfield'), + ('learnpath', '0002_alter_learningcontent_contents'), + ] + + operations = [ + migrations.CreateModel( + name='CompetenceProfilePage', + 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')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + migrations.CreateModel( + name='PerformanceCriteria', + 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')), + ('text', models.TextField(default='')), + ('learning_unit', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='learnpath.learningunit')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/server/vbv_lernwelt/competence/migrations/__init__.py b/server/vbv_lernwelt/competence/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/competence/models.py b/server/vbv_lernwelt/competence/models.py new file mode 100644 index 00000000..6dd132ec --- /dev/null +++ b/server/vbv_lernwelt/competence/models.py @@ -0,0 +1,50 @@ +from django.db import models +from django.utils.text import slugify +from wagtail.admin.panels import FieldPanel +from wagtail.models import Page + +from vbv_lernwelt.core.model_utils import find_available_slug +from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class + + +class CompetenceProfilePage(Page): + parent_page_types = ['course.CoursePage'] + subpage_types = ['competence.PerformanceCriteria'] + + content_panels = [ + FieldPanel('title', classname="full title"), + ] + + def full_clean(self, *args, **kwargs): + self.slug = find_available_slug(slugify(f"{self.get_parent().slug}-competence", allow_unicode=True)) + super(CompetenceProfilePage, self).full_clean(*args, **kwargs) + + @classmethod + def get_serializer_class(cls): + return get_it_serializer_class( + cls, [ + 'id', 'title', 'slug', 'type', 'translation_key', + 'course', + 'children', + ] + ) + + +class PerformanceCriteria(Page): + parent_page_types = ['competence.CompetenceProfilePage'] + text = models.TextField(default='') + learning_unit = models.ForeignKey( + 'learnpath.LearningUnit', + null=True, + blank=True, + on_delete=models.SET_NULL, + ) + + content_panels = [ + FieldPanel('title'), + FieldPanel('learning_unit'), + ] + + def full_clean(self, *args, **kwargs): + self.slug = find_available_slug(slugify(f"{self.get_parent()}-crit-{self.title}", allow_unicode=True)) + super(PerformanceCriteria, self).full_clean(*args, **kwargs) diff --git a/server/vbv_lernwelt/competence/tests/__init__.py b/server/vbv_lernwelt/competence/tests/__init__.py new file mode 100644 index 00000000..ae29b5e3 --- /dev/null +++ b/server/vbv_lernwelt/competence/tests/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# +# Iterativ GmbH +# http://www.iterativ.ch/ +# +# Copyright (c) 2015 Iterativ GmbH. All rights reserved. +# +# Created on 2022-08-16 +# @author: lorenz.padberg@iterativ.ch diff --git a/server/vbv_lernwelt/competence/tests/media_library_factories.py b/server/vbv_lernwelt/competence/tests/media_library_factories.py new file mode 100644 index 00000000..1173e2b0 --- /dev/null +++ b/server/vbv_lernwelt/competence/tests/media_library_factories.py @@ -0,0 +1,100 @@ +import uuid + +import wagtail_factories + +from vbv_lernwelt.competence.content_blocks import MediaContentCollection, AnchorBlock, LinkBlock, \ + CrossReferenceBlock +from vbv_lernwelt.competence.models import LibraryDocument, MediaLibraryPage, MediaCategoryPage + + +class LibraryDocumentFactory(wagtail_factories.DocumentFactory): + link_display_text = 'Dokument herunter laden' + thumbnail = 'https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/thumbnails/image/file.jpg' + + class Meta: + model = LibraryDocument + + +class MediaLibraryPageFactory(wagtail_factories.PageFactory): + title = 'Mediathek' + + class Meta: + model = MediaLibraryPage + + +class AnchorBlockFactory(wagtail_factories.StructBlockFactory): + class Meta: + model = AnchorBlock + + +class LinkBlockFactory(wagtail_factories.StructBlockFactory): + title = 'Interesting link' + description = 'This link is really interesting...' + url = 'https://www.vbv.ch/' + + class Meta: + model = LinkBlock + + +class CrossReferenceBlockFactory(wagtail_factories.StructBlockFactory): + class Meta: + model = CrossReferenceBlock + + +class MediaContentCollectionFactory(wagtail_factories.StructBlockFactory): + title = 'Links' + contents = wagtail_factories.StreamFieldFactory({ + 'Links': LinkBlockFactory, + 'Documents': LibraryDocumentFactory + }) + + class Meta: + model = MediaContentCollection + + +class MediaCategoryPageFactory(wagtail_factories.PageFactory): + title = 'Fahrzeug' + introduction_text = 'Das Auto ist für viele der grösste Stolz! Es birgt aber ...' + description_title = 'Das erwartet dich in diesem Handlungsfeld' + + class Meta: + model = MediaCategoryPage + + +def create_media_content_link(link_block=None): + if link_block is None: + link_block = LinkBlockFactory() + return { + "id": str(uuid.uuid4()), + "type": "Links", + "value": dict(link_block.items()) + } + + +def create_link_collection(links_dict=None): + return { + "id": str(uuid.uuid4()), + "type": "content_collection", + "value": { + "title": "Links", + "contents": [link_dict for link_dict in links_dict] + } + } + + +def create_document_collection(document_ids=None): + if document_ids is None: + document_ids = [d.id for d in LibraryDocument.objects.all()] + + return { + "id": str(uuid.uuid4()), + "type": "content_collection", + "value": { + "title": "Lernmedien", + "contents": [{ + "id": str(uuid.uuid4()), + "type": "Documents", + "value": doc_id + } for doc_id in document_ids] + } + } diff --git a/server/vbv_lernwelt/competence/tests/test_create_default_documents.py b/server/vbv_lernwelt/competence/tests/test_create_default_documents.py new file mode 100644 index 00000000..1708b216 --- /dev/null +++ b/server/vbv_lernwelt/competence/tests/test_create_default_documents.py @@ -0,0 +1,25 @@ +from django.test import TestCase +from wagtail.core.models import Collection + +from vbv_lernwelt.core.create_default_users import create_default_users +from vbv_lernwelt.core.tests.helpers import create_locales_for_wagtail +from vbv_lernwelt.competence.create_default_documents import create_default_collections, create_default_documents +from vbv_lernwelt.competence.models import LibraryDocument + + +class TestCreateDefaultDocuments(TestCase): + def setUp(self) -> None: + create_default_users() + create_locales_for_wagtail() + + def test_create_default_collections(self): + create_default_collections() + qs = Collection.objects.filter(name="Versicherungsvermittler/in") + self.assertTrue(qs.exists()) + + def test_create_default_documents(self): + create_default_collections() + + create_default_documents() + qs = LibraryDocument.objects.all() + self.assertTrue(qs.exists()) diff --git a/server/vbv_lernwelt/competence/tests/test_create_default_media_library.py b/server/vbv_lernwelt/competence/tests/test_create_default_media_library.py new file mode 100644 index 00000000..29362d0d --- /dev/null +++ b/server/vbv_lernwelt/competence/tests/test_create_default_media_library.py @@ -0,0 +1,29 @@ +from django.test import TestCase + +from vbv_lernwelt.core.create_default_users import create_default_users +from vbv_lernwelt.core.tests.helpers import create_locales_for_wagtail +from vbv_lernwelt.learnpath.tests.learning_path_factories import LearningPathFactory +from vbv_lernwelt.competence.create_default_competence import create_default_competence +from vbv_lernwelt.competence.models import MediaLibraryPage, MediaCategoryPage + + +class TestCreateDefaultDocuments(TestCase): + def setUp(self) -> None: + create_default_users() + create_locales_for_wagtail() + LearningPathFactory() + create_default_competence() + + + def test_create_default_competence(self): + + self.assertEqual(MediaLibraryPage.objects.all().count(), 1) + self.assertEqual(MediaCategoryPage.objects.all().count(), 12) + + def test_create_category_fahrzeug_contains_content(self): + fahrzeug = MediaCategoryPage.objects.get(title='Fahrzeug') + + + + + diff --git a/server/vbv_lernwelt/competence/tests/test_media_library_factories.py b/server/vbv_lernwelt/competence/tests/test_media_library_factories.py new file mode 100644 index 00000000..11f3002a --- /dev/null +++ b/server/vbv_lernwelt/competence/tests/test_media_library_factories.py @@ -0,0 +1,47 @@ +import json + +from django.test import TestCase + +from vbv_lernwelt.core.create_default_users import create_default_users +from vbv_lernwelt.core.tests.helpers import create_locales_for_wagtail +from vbv_lernwelt.competence.models import MediaCategoryPage +from vbv_lernwelt.competence.tests.competence_factories import MediaContentCollectionFactory, MediaCategoryPageFactory, \ + LinkBlockFactory, generate_default_content2, collection_body_dict + + +class TestMediaLibraryFactories(TestCase): + def setUp(self) -> None: + create_default_users() + create_locales_for_wagtail() + + def test_content_collection_factory(self): + content_collection = MediaContentCollectionFactory() + self.assertEqual(content_collection.get('title'), 'Links') + self.assertEqual(content_collection.get('collection_type'), 'LearningMedia') + + def test_link_block_factory(self): + link = LinkBlockFactory(title='MyLink') + self.assertEqual(link.get('description'), 'This link is really interesting...') + self.assertEqual(link.get('url'), 'www.example.com') + self.assertEqual(link.get('link_display_text'), 'Link öffnen') + self.assertEqual(link.get('title'), 'MyLink') + + def test_category_contains_content_collection(self): + default_content = generate_default_content2() + default_content['body__content_collection__0__title'] = 'Spidf' + + category = MediaCategoryPageFactory(**default_content) + print(category.body.raw_data) + self.assertNotEqual(category.body.raw_data, []) + + def collection_via_dict_generation(self): + category = MediaCategoryPageFactory() + category.body = json.dumps(collection_body_dict()) + category.save() + category_id = category.id + new_category = MediaCategoryPage.objects.get(id=category_id) + self.assertNotEqual(new_category.body, []) + self.assertNotEqual(new_category.body, []) + + + diff --git a/server/vbv_lernwelt/competence/views.py b/server/vbv_lernwelt/competence/views.py new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/learnpath/migrations/0002_alter_learningcontent_contents.py b/server/vbv_lernwelt/learnpath/migrations/0002_alter_learningcontent_contents.py new file mode 100644 index 00000000..6db404e2 --- /dev/null +++ b/server/vbv_lernwelt/learnpath/migrations/0002_alter_learningcontent_contents.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2022-09-27 13:54 + +from django.db import migrations +import wagtail.blocks +import wagtail.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('learnpath', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='learningcontent', + name='contents', + field=wagtail.fields.StreamField([('video', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('resource', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('exercise', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('online_training', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('media_library', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('document', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('test', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('book', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('assignment', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())]))], use_json_field=None), + ), + ]