From 08e567262320fb2d2db28afd827cf367dd6e7ab0 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 8 Jun 2022 16:19:37 +0200 Subject: [PATCH] Add completion app --- server/config/settings/base.py | 4 +- server/config/urls.py | 1 + server/vbv_lernwelt/completion/__init__.py | 0 server/vbv_lernwelt/completion/admin.py | 3 + server/vbv_lernwelt/completion/apps.py | 6 ++ .../completion/migrations/0001_initial.py | 66 +++++++++++++++++++ .../completion/migrations/__init__.py | 0 server/vbv_lernwelt/completion/models.py | 59 +++++++++++++++++ .../vbv_lernwelt/completion/tests/__init__.py | 0 .../vbv_lernwelt/completion/tests/test_api.py | 28 ++++++++ server/vbv_lernwelt/completion/urls.py | 7 ++ server/vbv_lernwelt/completion/views.py | 19 ++++++ 12 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 server/vbv_lernwelt/completion/__init__.py create mode 100644 server/vbv_lernwelt/completion/admin.py create mode 100644 server/vbv_lernwelt/completion/apps.py create mode 100644 server/vbv_lernwelt/completion/migrations/0001_initial.py create mode 100644 server/vbv_lernwelt/completion/migrations/__init__.py create mode 100644 server/vbv_lernwelt/completion/models.py create mode 100644 server/vbv_lernwelt/completion/tests/__init__.py create mode 100644 server/vbv_lernwelt/completion/tests/test_api.py create mode 100644 server/vbv_lernwelt/completion/urls.py create mode 100644 server/vbv_lernwelt/completion/views.py diff --git a/server/config/settings/base.py b/server/config/settings/base.py index c26fa569..9983f04d 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -2,9 +2,10 @@ Base settings to build other settings files upon. """ import logging +from pathlib import Path + import structlog from environs import Env -from pathlib import Path from vbv_lernwelt.core.utils import structlog_add_app_info @@ -103,6 +104,7 @@ LOCAL_APPS = [ "vbv_lernwelt.simpletodo", "vbv_lernwelt.sso", "vbv_lernwelt.learnpath", + "vbv_lernwelt.completion", ] # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS diff --git a/server/config/urls.py b/server/config/urls.py index 6d8d9bf1..49992051 100644 --- a/server/config/urls.py +++ b/server/config/urls.py @@ -42,6 +42,7 @@ urlpatterns = [ path('documents/', include(wagtaildocs_urls)), path('pages/', include(wagtail_urls)), path('learnpath/', include("vbv_lernwelt.learnpath.urls")), + path('api/completion/', include("vbv_lernwelt.completion.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 diff --git a/server/vbv_lernwelt/completion/__init__.py b/server/vbv_lernwelt/completion/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/completion/admin.py b/server/vbv_lernwelt/completion/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/server/vbv_lernwelt/completion/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/server/vbv_lernwelt/completion/apps.py b/server/vbv_lernwelt/completion/apps.py new file mode 100644 index 00000000..f5b6cfca --- /dev/null +++ b/server/vbv_lernwelt/completion/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CompletionConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'vbv_lernwelt.completion' diff --git a/server/vbv_lernwelt/completion/migrations/0001_initial.py b/server/vbv_lernwelt/completion/migrations/0001_initial.py new file mode 100644 index 00000000..31c21594 --- /dev/null +++ b/server/vbv_lernwelt/completion/migrations/0001_initial.py @@ -0,0 +1,66 @@ +# Generated by Django 3.2.13 on 2022-06-08 13:52 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='UserCircleCompletion', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('circle_key', models.UUIDField()), + ('json_data', models.JSONField(blank=True, default=dict)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='LearningUnitQuestionCompletion', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('question_key', models.UUIDField()), + ('circle_key', models.UUIDField()), + ('completed', models.BooleanField(default=True)), + ('json_data', models.JSONField(blank=True, default=dict)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='LearningContentCompletion', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('learning_content_key', models.UUIDField()), + ('circle_key', models.UUIDField()), + ('completed', models.BooleanField(default=True)), + ('json_data', models.JSONField(blank=True, default=dict)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddConstraint( + model_name='usercirclecompletion', + constraint=models.UniqueConstraint(fields=('user', 'circle_key'), name='unique_user_circle_completion'), + ), + migrations.AddConstraint( + model_name='learningunitquestioncompletion', + constraint=models.UniqueConstraint(fields=('user', 'question_key'), name='unique_user_question_key'), + ), + migrations.AddConstraint( + model_name='learningcontentcompletion', + constraint=models.UniqueConstraint(fields=('user', 'learning_content_key'), name='unique_user_learning_content_key'), + ), + ] diff --git a/server/vbv_lernwelt/completion/migrations/__init__.py b/server/vbv_lernwelt/completion/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/completion/models.py b/server/vbv_lernwelt/completion/models.py new file mode 100644 index 00000000..306a2004 --- /dev/null +++ b/server/vbv_lernwelt/completion/models.py @@ -0,0 +1,59 @@ +from django.db import models +from django.db.models import UniqueConstraint + +from vbv_lernwelt.core.models import User + + +class LearningContentCompletion(models.Model): + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + user = models.ForeignKey(User, on_delete=models.CASCADE) + learning_content_key = models.UUIDField() + circle_key = models.UUIDField() + + completed = models.BooleanField(default=True) + json_data = models.JSONField(default=dict, blank=True) + + class Meta: + constraints = [ + UniqueConstraint( + fields=['user', 'learning_content_key', ], + name='unique_user_learning_content_key' + ) + ] + + +class LearningUnitQuestionCompletion(models.Model): + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + user = models.ForeignKey(User, on_delete=models.CASCADE) + question_key = models.UUIDField() + circle_key = models.UUIDField() + + completed = models.BooleanField(default=True) + json_data = models.JSONField(default=dict, blank=True) + + class Meta: + constraints = [ + UniqueConstraint( + fields=['user', 'question_key', ], + name='unique_user_question_key' + ) + ] + + +class UserCircleCompletion(models.Model): + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + user = models.ForeignKey(User, on_delete=models.CASCADE) + circle_key = models.UUIDField() + + json_data = models.JSONField(default=dict, blank=True) + + class Meta: + constraints = [ + UniqueConstraint(fields=['user', 'circle_key'], name='unique_user_circle_completion') + ] diff --git a/server/vbv_lernwelt/completion/tests/__init__.py b/server/vbv_lernwelt/completion/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/server/vbv_lernwelt/completion/tests/test_api.py b/server/vbv_lernwelt/completion/tests/test_api.py new file mode 100644 index 00000000..2e6c6a81 --- /dev/null +++ b/server/vbv_lernwelt/completion/tests/test_api.py @@ -0,0 +1,28 @@ +from rest_framework.test import APITestCase, APIClient + +from vbv_lernwelt.core.create_default_users import create_default_users +from vbv_lernwelt.core.models import User +from vbv_lernwelt.learnpath.models import LearningContent +from vbv_lernwelt.learnpath.tests.create_default_learning_path import create_default_learning_path +from vbv_lernwelt.learnpath.tests.test_create_default_learning_path import create_locales_for_wagtail + + +class CompletionApiTestCase(APITestCase): + @classmethod + def setUpClass(cls) -> None: + super(CompletionApiTestCase, cls).setUpClass() + create_locales_for_wagtail() + create_default_users() + create_default_learning_path() + + def setUp(self) -> None: + self.user = User.objects.get(username='student') + self.client.login(username='student', password='student') + + def test_completeLearningContent_works(self): + learning_content_key = LearningContent.objects.get(title='Einleitung Circle "Anlayse"').translation_key + response = self.client.post(f'/api/completion/complete_learning_content/', { + 'learning_content_key': learning_content_key + }) + print(response.content) + self.assertEqual(response.status_code, 201) diff --git a/server/vbv_lernwelt/completion/urls.py b/server/vbv_lernwelt/completion/urls.py new file mode 100644 index 00000000..820574e3 --- /dev/null +++ b/server/vbv_lernwelt/completion/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from vbv_lernwelt.completion.views import complete_learning_content + +urlpatterns = [ + path(r"complete_learning_content/", complete_learning_content, name="complete_learning_content"), +] diff --git a/server/vbv_lernwelt/completion/views.py b/server/vbv_lernwelt/completion/views.py new file mode 100644 index 00000000..fa03935f --- /dev/null +++ b/server/vbv_lernwelt/completion/views.py @@ -0,0 +1,19 @@ +from rest_framework.decorators import api_view +from rest_framework.response import Response + +from vbv_lernwelt.completion.models import LearningContentCompletion +from vbv_lernwelt.learnpath.models import LearningContent + + +@api_view(['POST']) +def complete_learning_content(request): + learning_content_key = request.data.get('learning_content_key') + learning_content = LearningContent.objects.get(translation_key=learning_content_key) + + LearningContentCompletion.objects.create( + user=request.user, + learning_content_key=learning_content_key, + circle_key=learning_content.get_parent().translation_key, + ) + + return Response(status=201)