From 9860a59cef603e5acd7abd305b53108d5b3be184 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 28 Jun 2023 18:27:37 +0200 Subject: [PATCH 1/4] Clean up assignment graphql * Remove old assignment api REST code which is unused * Refactor handling AssignmentCompletionStatus enum * Add full test for GraphQL assignment completion mutation * Hide wagtail INFO logs on local dev --- client/src/gql/schema.graphql | 10 +- server/config/settings/base.py | 5 + server/config/urls.py | 19 +- .../assignment/graphql/mutations.py | 15 +- ...entcompletionauditlog_completion_status.py | 27 ++ .../migrations/0006_auto_20230628_1616.py | 37 ++ server/vbv_lernwelt/assignment/models.py | 44 ++- server/vbv_lernwelt/assignment/schema.py | 3 - server/vbv_lernwelt/assignment/services.py | 36 +- .../assignment/tests/test_assignment_api.py | 299 -------------- .../assignment/tests/test_graphql.py | 368 ++++++++++++++++++ .../assignment/tests/test_services.py | 31 +- server/vbv_lernwelt/assignment/views.py | 156 +------- .../vbv_lernwelt/core/middleware/graphene.py | 6 +- 14 files changed, 525 insertions(+), 531 deletions(-) create mode 100644 server/vbv_lernwelt/assignment/migrations/0005_alter_assignmentcompletionauditlog_completion_status.py create mode 100644 server/vbv_lernwelt/assignment/migrations/0006_auto_20230628_1616.py delete mode 100644 server/vbv_lernwelt/assignment/schema.py delete mode 100644 server/vbv_lernwelt/assignment/tests/test_assignment_api.py create mode 100644 server/vbv_lernwelt/assignment/tests/test_graphql.py diff --git a/client/src/gql/schema.graphql b/client/src/gql/schema.graphql index c35b37a3..c381bea3 100644 --- a/client/src/gql/schema.graphql +++ b/client/src/gql/schema.graphql @@ -184,7 +184,7 @@ scalar JSONString type Mutation { send_feedback(input: SendFeedbackInput!): SendFeedbackPayload - upsert_assignment_completion(assignment_id: ID!, assignment_user_id: ID, completion_data_string: String, completion_status: String, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float): AssignmentCompletionMutation + upsert_assignment_completion(assignment_id: ID!, assignment_user_id: ID, completion_data_string: String, completion_status: AssignmentCompletionStatus, course_session_id: ID!, evaluation_grade: Float, evaluation_points: Float): AssignmentCompletionMutation } type SendFeedbackPayload { @@ -222,4 +222,12 @@ input SendFeedbackInput { type AssignmentCompletionMutation { assignment_completion: AssignmentCompletionType +} + +"""An enumeration.""" +enum AssignmentCompletionStatus { + IN_PROGRESS + SUBMITTED + EVALUATION_IN_PROGRESS + EVALUATION_SUBMITTED } \ No newline at end of file diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 2f62cdac..bae81804 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -372,6 +372,11 @@ if IT_DJANGO_LOGGING_CONF == "IT_DJANGO_LOGGING_CONF_CONSOLE_COLOR": "level": "INFO", "propagate": False, }, + "wagtail": { + "handlers": ["default"], + "level": "WARNING" if IT_LOCAL_HIDE_DJANGO_SERVER_LOGS else "INFO", + "propagate": False, + }, "vbv_lernwelt": { "handlers": ["default"], "level": "DEBUG", diff --git a/server/config/urls.py b/server/config/urls.py index d4950815..b0912a61 100644 --- a/server/config/urls.py +++ b/server/config/urls.py @@ -11,13 +11,7 @@ from django.views.decorators.csrf import csrf_exempt from graphene_django.views import GraphQLView from ratelimit.exceptions import Ratelimited -from vbv_lernwelt.assignment.views import ( - evaluate_assignment_completion, - request_assignment_completion, - request_assignment_completion_for_user, - request_assignment_completion_status, - upsert_user_assignment_completion, -) +from vbv_lernwelt.assignment.views import request_assignment_completion_status from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt from vbv_lernwelt.core.schema import schema from vbv_lernwelt.core.views import ( @@ -120,21 +114,10 @@ urlpatterns = [ name="request_course_completion_for_user"), # assignment - path(r"api/assignment/upsert/", upsert_user_assignment_completion, - name="upsert_user_assignment_completion"), - path(r"api/assignment/evaluate/", evaluate_assignment_completion, - name="evaluate_assignment_completion"), - path(r"api/assignment///", - request_assignment_completion, - name="request_assignment_completion"), path( r"api/assignment///status/", request_assignment_completion_status, name="request_assignment_completion_status"), - path( - r"api/assignment////", - request_assignment_completion_for_user, - name="request_assignment_completion_for_user"), # documents path(r'api/core/document/start/', document_upload_start, diff --git a/server/vbv_lernwelt/assignment/graphql/mutations.py b/server/vbv_lernwelt/assignment/graphql/mutations.py index 52877782..632c04c5 100644 --- a/server/vbv_lernwelt/assignment/graphql/mutations.py +++ b/server/vbv_lernwelt/assignment/graphql/mutations.py @@ -5,7 +5,7 @@ import structlog from rest_framework.exceptions import PermissionDenied from vbv_lernwelt.assignment.graphql.types import AssignmentCompletionType -from vbv_lernwelt.assignment.models import Assignment +from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus from vbv_lernwelt.assignment.services import update_assignment_completion from vbv_lernwelt.core.models import User from vbv_lernwelt.course.models import CourseSession @@ -17,12 +17,14 @@ logger = structlog.get_logger(__name__) class AssignmentCompletionMutation(graphene.Mutation): assignment_completion = graphene.Field(AssignmentCompletionType) - class Input: + class Arguments: assignment_id = graphene.ID(required=True) course_session_id = graphene.ID(required=True) assignment_user_id = graphene.ID() - completion_status = graphene.String() + completion_status = graphene.Argument( + graphene.Enum.from_enum(AssignmentCompletionStatus) + ) completion_data_string = graphene.String() evaluation_grade = graphene.Float() @@ -36,7 +38,7 @@ class AssignmentCompletionMutation(graphene.Mutation): assignment_id, course_session_id, assignment_user_id=None, - completion_status="IN_PROGRESS", + completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS, completion_data_string="{}", evaluation_grade=None, evaluation_points=None, @@ -63,7 +65,10 @@ class AssignmentCompletionMutation(graphene.Mutation): evaluation_data = {} - if completion_status in ["EVALUATION_SUBMITTED", "EVALUATION_IN_PROGRESS"]: + if completion_status in [ + AssignmentCompletionStatus.EVALUATION_SUBMITTED, + AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, + ]: if not is_course_session_expert(info.context.user, course_session_id): raise PermissionDenied() diff --git a/server/vbv_lernwelt/assignment/migrations/0005_alter_assignmentcompletionauditlog_completion_status.py b/server/vbv_lernwelt/assignment/migrations/0005_alter_assignmentcompletionauditlog_completion_status.py new file mode 100644 index 00000000..623e4aa8 --- /dev/null +++ b/server/vbv_lernwelt/assignment/migrations/0005_alter_assignmentcompletionauditlog_completion_status.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.13 on 2023-06-28 14:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("assignment", "0004_assignment_assignment_type"), + ] + + operations = [ + migrations.AlterField( + model_name="assignmentcompletionauditlog", + name="completion_status", + field=models.CharField( + choices=[ + ("IN_PROGRESS", "IN_PROGRESS"), + ("SUBMITTED", "SUBMITTED"), + ("EVALUATION_IN_PROGRESS", "EVALUATION_IN_PROGRESS"), + ("EVALUATION_SUBMITTED", "EVALUATION_SUBMITTED"), + ], + default="IN_PROGRESS", + max_length=255, + ), + ), + ] diff --git a/server/vbv_lernwelt/assignment/migrations/0006_auto_20230628_1616.py b/server/vbv_lernwelt/assignment/migrations/0006_auto_20230628_1616.py new file mode 100644 index 00000000..e2a094bc --- /dev/null +++ b/server/vbv_lernwelt/assignment/migrations/0006_auto_20230628_1616.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.13 on 2023-06-28 14:16 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("learnpath", "0007_learningunit_title_hidden"), + ("assignment", "0005_alter_assignmentcompletionauditlog_completion_status"), + ] + + operations = [ + migrations.RemoveConstraint( + model_name="assignmentcompletion", + name="assignment_completion_unique_user_assignment_course_session", + ), + migrations.AddField( + model_name="assignmentcompletion", + name="circle", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="learnpath.circle", + ), + ), + migrations.AddConstraint( + model_name="assignmentcompletion", + constraint=models.UniqueConstraint( + fields=("assignment_user", "assignment", "course_session", "circle"), + name="assignment_completion_unique_user_assignment_course_session", + ), + ), + ] diff --git a/server/vbv_lernwelt/assignment/models.py b/server/vbv_lernwelt/assignment/models.py index d482a175..9d796804 100644 --- a/server/vbv_lernwelt/assignment/models.py +++ b/server/vbv_lernwelt/assignment/models.py @@ -107,14 +107,10 @@ class EvaluationTaskBlock(blocks.StructBlock): label = "Beurteilungskriterium" -AssignmentType = Enum( - "AssignmentType", - [ - "CASEWORK", # Geleitete Fallarbeit - "PREP_ASSIGNMENT", # Vorbereitungsauftrag - "REFLECTION", # Reflexion - ], -) +class AssignmentType(Enum): + CASEWORK = "CASEWORK" # Geleitete Fallarbeit + PREP_ASSIGNMENT = "PREP_ASSIGNMENT" # Vorbereitungsauftrag + REFLECTION = "REFLECTION" # Reflexion class Assignment(CourseBasePage): @@ -130,8 +126,8 @@ class Assignment(CourseBasePage): assignment_type = models.CharField( max_length=50, - choices=[(tag.name, tag.name) for tag in AssignmentType], - default=AssignmentType.CASEWORK.name, + choices=[(tag.value, tag.value) for tag in AssignmentType], + default=AssignmentType.CASEWORK.value, ) intro_text = RichTextField( @@ -242,14 +238,17 @@ class Assignment(CourseBasePage): return self.filter_user_subtasks() + self.get_evaluation_tasks() -AssignmentCompletionStatus = Enum( - "AssignmentCompletionStatus", - ["IN_PROGRESS", "SUBMITTED", "EVALUATION_IN_PROGRESS", "EVALUATION_SUBMITTED"], -) +class AssignmentCompletionStatus(Enum): + IN_PROGRESS = "IN_PROGRESS" + SUBMITTED = "SUBMITTED" + EVALUATION_IN_PROGRESS = "EVALUATION_IN_PROGRESS" + EVALUATION_SUBMITTED = "EVALUATION_SUBMITTED" -def is_valid_assignment_completion_status(status): - return status in AssignmentCompletionStatus.__members__ +def is_valid_assignment_completion_status( + completion_status: AssignmentCompletionStatus, +): + return completion_status.value in AssignmentCompletionStatus.__members__ class AssignmentCompletion(models.Model): @@ -271,11 +270,18 @@ class AssignmentCompletion(models.Model): assignment_user = models.ForeignKey(User, on_delete=models.CASCADE) assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE) course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE) + circle = models.ForeignKey( + "learnpath.Circle", + on_delete=models.CASCADE, + null=True, + blank=True, + default=None, + ) completion_status = models.CharField( max_length=255, - choices=[(acs.name, acs.name) for acs in AssignmentCompletionStatus], - default="IN_PROGRESS", + choices=[(acs.value, acs.value) for acs in AssignmentCompletionStatus], + default=AssignmentCompletionStatus.IN_PROGRESS.value, ) completion_data = models.JSONField(default=dict) @@ -284,7 +290,7 @@ class AssignmentCompletion(models.Model): class Meta: constraints = [ UniqueConstraint( - fields=["assignment_user", "assignment", "course_session"], + fields=["assignment_user", "assignment", "course_session", "circle"], name="assignment_completion_unique_user_assignment_course_session", ) ] diff --git a/server/vbv_lernwelt/assignment/schema.py b/server/vbv_lernwelt/assignment/schema.py deleted file mode 100644 index a58e2c4b..00000000 --- a/server/vbv_lernwelt/assignment/schema.py +++ /dev/null @@ -1,3 +0,0 @@ -import structlog - -logger = structlog.get_logger(__name__) diff --git a/server/vbv_lernwelt/assignment/services.py b/server/vbv_lernwelt/assignment/services.py index caca03ed..ae872d74 100644 --- a/server/vbv_lernwelt/assignment/services.py +++ b/server/vbv_lernwelt/assignment/services.py @@ -1,5 +1,4 @@ from copy import deepcopy -from typing import Type from django.utils import timezone from rest_framework import serializers @@ -21,7 +20,7 @@ def update_assignment_completion( assignment: Assignment, course_session: CourseSession, completion_data=None, - completion_status: Type[AssignmentCompletionStatus] = "IN_PROGRESS", + completion_status: AssignmentCompletionStatus = AssignmentCompletionStatus.IN_PROGRESS, evaluation_user: User | None = None, evaluation_grade: float | None = None, evaluation_points: float | None = None, @@ -58,12 +57,14 @@ def update_assignment_completion( if not is_valid_assignment_completion_status(completion_status): raise serializers.ValidationError( - {"completion_status": f"Invalid completion status {completion_status}"} + { + "completion_status": f"Invalid completion status {completion_status.value}" + } ) if validate_completion_status_change: # TODO: check time? - if completion_status == "SUBMITTED": + if completion_status == AssignmentCompletionStatus.SUBMITTED: if ac.completion_status in [ "SUBMITTED", "EVALUATION_IN_PROGRESS", @@ -74,7 +75,7 @@ def update_assignment_completion( "completion_status": f"Cannot update completion status from {ac.completion_status} to SUBMITTED" } ) - elif completion_status == "EVALUATION_SUBMITTED": + elif completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED: if ac.completion_status == "EVALUATION_SUBMITTED": raise serializers.ValidationError( { @@ -82,14 +83,20 @@ def update_assignment_completion( } ) - if completion_status == "IN_PROGRESS" and ac.completion_status != "IN_PROGRESS": + if ( + completion_status == AssignmentCompletionStatus.IN_PROGRESS + and ac.completion_status != "IN_PROGRESS" + ): raise serializers.ValidationError( { "completion_status": f"Cannot set completion status to IN_PROGRESS when it is {ac.completion_status}" } ) - if completion_status in ["EVALUATION_SUBMITTED", "EVALUATION_IN_PROGRESS"]: + if completion_status in [ + AssignmentCompletionStatus.EVALUATION_SUBMITTED, + AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, + ]: if evaluation_user is None: raise serializers.ValidationError( { @@ -98,7 +105,7 @@ def update_assignment_completion( ) ac.evaluation_user = evaluation_user - if completion_status == "EVALUATION_SUBMITTED": + if completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED: if evaluation_grade is None: raise serializers.ValidationError( { @@ -115,12 +122,12 @@ def update_assignment_completion( ac.evaluation_grade = evaluation_grade ac.evaluation_points = evaluation_points - if completion_status == "SUBMITTED": + if completion_status == AssignmentCompletionStatus.SUBMITTED: ac.submitted_at = timezone.now() - elif completion_status == "EVALUATION_SUBMITTED": + elif completion_status == AssignmentCompletionStatus.EVALUATION_SUBMITTED: ac.evaluation_submitted_at = timezone.now() - ac.completion_status = completion_status + ac.completion_status = completion_status.value # TODO: make more validation of the provided input -> maybe with graphql completion_data = _remove_unknown_entries(assignment, completion_data) @@ -140,13 +147,16 @@ def update_assignment_completion( ac.save() - if completion_status in ["EVALUATION_SUBMITTED", "SUBMITTED"]: + if completion_status in [ + AssignmentCompletionStatus.EVALUATION_SUBMITTED, + AssignmentCompletionStatus.SUBMITTED, + ]: acl = AssignmentCompletionAuditLog.objects.create( assignment_user=assignment_user, assignment=assignment, course_session=course_session, evaluation_user=evaluation_user, - completion_status=completion_status, + completion_status=completion_status.value, assignment_user_email=assignment_user.email, assignment_slug=assignment.slug, completion_data=deepcopy(ac.completion_data), diff --git a/server/vbv_lernwelt/assignment/tests/test_assignment_api.py b/server/vbv_lernwelt/assignment/tests/test_assignment_api.py deleted file mode 100644 index 86fe4edf..00000000 --- a/server/vbv_lernwelt/assignment/tests/test_assignment_api.py +++ /dev/null @@ -1,299 +0,0 @@ -import json - -from django.utils import timezone -from rest_framework.test import APITestCase - -from vbv_lernwelt.assignment.creators.create_assignments import ( - create_uk_fahrzeug_casework, -) -from vbv_lernwelt.assignment.models import ( - AssignmentCompletion, - AssignmentCompletionAuditLog, -) -from vbv_lernwelt.core.create_default_users import create_default_users -from vbv_lernwelt.core.models import User -from vbv_lernwelt.core.utils import find_first -from vbv_lernwelt.course.consts import COURSE_TEST_ID -from vbv_lernwelt.course.creators.test_course import create_test_course -from vbv_lernwelt.course.models import CourseSession, CourseSessionUser - - -class AssignmentApiTestCase(APITestCase): - def setUp(self) -> None: - create_default_users() - create_test_course(include_vv=False) - self.assignment = create_uk_fahrzeug_casework(course_id=COURSE_TEST_ID) - self.assignment_subtasks = self.assignment.filter_user_subtasks() - - self.cs = CourseSession.objects.create( - course_id=COURSE_TEST_ID, - title="Test Lehrgang Session", - ) - self.student = User.objects.get(username="student") - self.student_csu = CourseSessionUser.objects.create( - course_session=self.cs, - user=self.student, - ) - - self.expert = User.objects.get(username="admin") - self.expert_csu = CourseSessionUser.objects.create( - course_session=self.cs, user=self.expert, role="EXPERT" - ) - - def test_student_can_upsertAssignmentCompletion(self): - self.client.login(username="student", password="test") - url = f"/api/assignment/upsert/" - - user_text_input = find_first( - self.assignment_subtasks, pred=lambda x: x["type"] == "user_text_input" - ) - - response = self.client.post( - url, - { - "assignment_id": self.assignment.id, - "course_session_id": self.cs.id, - "completion_status": "IN_PROGRESS", - "completion_data": { - user_text_input["id"]: {"user_data": {"text": "Hallo via API"}}, - }, - }, - format="json", - ) - response_json = response.json() - print(json.dumps(response.json(), indent=2)) - - self.assertEqual(response.status_code, 200) - self.assertEqual(response_json["assignment_user"], self.student.id) - self.assertEqual(response_json["assignment"], self.assignment.id) - self.assertEqual(response_json["completion_status"], "IN_PROGRESS") - self.assertDictEqual( - response_json["completion_data"], - { - user_text_input["id"]: {"user_data": {"text": "Hallo via API"}}, - }, - ) - - db_entry = AssignmentCompletion.objects.get( - assignment_user=self.student, - course_session_id=self.cs.id, - assignment_id=self.assignment.id, - ) - self.assertEqual(db_entry.completion_status, "IN_PROGRESS") - self.assertDictEqual( - db_entry.completion_data, - { - user_text_input["id"]: {"user_data": {"text": "Hallo via API"}}, - }, - ) - - # read data via request api - response = self.client.get( - f"/api/assignment/{self.assignment.id}/{self.cs.id}/", - format="json", - ) - response_json = response.json() - print(json.dumps(response.json(), indent=2)) - self.assertDictEqual( - response_json["completion_data"], - { - user_text_input["id"]: {"user_data": {"text": "Hallo via API"}}, - }, - ) - - # submit the assignment - response = self.client.post( - url, - { - "assignment_id": self.assignment.id, - "course_session_id": self.cs.id, - "completion_status": "SUBMITTED", - "completion_data": { - user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}}, - }, - }, - format="json", - ) - response_json = response.json() - print(json.dumps(response.json(), indent=2)) - - self.assertEqual(response.status_code, 200) - self.assertEqual(response_json["assignment_user"], self.student.id) - self.assertEqual(response_json["assignment"], self.assignment.id) - self.assertEqual(response_json["completion_status"], "SUBMITTED") - self.assertDictEqual( - response_json["completion_data"], - { - user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}}, - }, - ) - - # second submit will fail - response = self.client.post( - url, - { - "assignment_id": self.assignment.id, - "course_session_id": self.cs.id, - "completion_status": "SUBMITTED", - "completion_data": { - user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}}, - }, - }, - format="json", - ) - response_json = response.json() - print(json.dumps(response.json(), indent=2)) - self.assertEqual(response.status_code, 404) - self.assertTrue("Cannot update completion status" in str(response_json)) - - def test_expert_can_gradeAssignmentCompletion(self): - # setup AssignmentCompletion - subtasks = self.assignment.filter_user_subtasks( - subtask_types=["user_text_input"] - ) - user_text_input = find_first( - subtasks, - pred=lambda x: (value := x.get("value")) - and value.get("text", "").startswith( - "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest?" - ), - ) - - ac = AssignmentCompletion.objects.create( - assignment_user=self.student, - assignment=self.assignment, - course_session=self.cs, - completion_status="SUBMITTED", - submitted_at=timezone.now(), - completion_data={ - user_text_input["id"]: { - "user_data": {"text": "Ich würde nichts weiteres empfehlen."} - }, - }, - ) - - # make api call - self.client.login(username="admin", password="test") - url = f"/api/assignment/evaluate/" - - response = self.client.post( - url, - { - "assignment_id": self.assignment.id, - "assignment_user_id": self.student.id, - "course_session_id": self.cs.id, - "completion_status": "EVALUATION_IN_PROGRESS", - "completion_data": { - user_text_input["id"]: { - "expert_data": {"points": 1, "comment": "Gut gemacht!"} - }, - }, - }, - format="json", - ) - response_json = response.json() - print(json.dumps(response.json(), indent=2)) - - self.assertEqual(response.status_code, 200) - self.assertEqual(response_json["assignment_user"], self.student.id) - self.assertEqual(response_json["assignment"], self.assignment.id) - self.assertEqual(response_json["completion_status"], "EVALUATION_IN_PROGRESS") - self.assertDictEqual( - response_json["completion_data"], - { - user_text_input["id"]: { - "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, - "expert_data": {"points": 1, "comment": "Gut gemacht!"}, - }, - }, - ) - - db_entry = AssignmentCompletion.objects.get( - assignment_user=self.student, - course_session_id=self.cs.id, - assignment_id=self.assignment.id, - ) - self.assertEqual(db_entry.completion_status, "EVALUATION_IN_PROGRESS") - self.assertDictEqual( - db_entry.completion_data, - { - user_text_input["id"]: { - "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, - "expert_data": {"points": 1, "comment": "Gut gemacht!"}, - }, - }, - ) - - # finish grading - response = self.client.post( - url, - { - "assignment_id": self.assignment.id, - "assignment_user_id": self.student.id, - "course_session_id": self.cs.id, - "completion_status": "EVALUATION_SUBMITTED", - "completion_data": { - user_text_input["id"]: { - "expert_data": {"points": 1, "comment": "Gut gemacht!"} - }, - }, - "evaluation_grade": 4.5, - "evaluation_points": 16, - }, - format="json", - ) - response_json = response.json() - print(json.dumps(response.json(), indent=2)) - - self.assertEqual(response.status_code, 200) - self.assertEqual(response_json["assignment_user"], self.student.id) - self.assertEqual(response_json["assignment"], self.assignment.id) - self.assertEqual(response_json["completion_status"], "EVALUATION_SUBMITTED") - self.assertDictEqual( - response_json["completion_data"], - { - user_text_input["id"]: { - "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, - "expert_data": {"points": 1, "comment": "Gut gemacht!"}, - }, - }, - ) - - db_entry = AssignmentCompletion.objects.get( - assignment_user=self.student, - course_session_id=self.cs.id, - assignment_id=self.assignment.id, - ) - self.assertEqual(db_entry.completion_status, "EVALUATION_SUBMITTED") - self.assertDictEqual( - db_entry.completion_data, - { - user_text_input["id"]: { - "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, - "expert_data": {"points": 1, "comment": "Gut gemacht!"}, - }, - }, - ) - - # `EVALUATION_SUBMITTED` will create a new AssignmentCompletionAuditLog - acl = AssignmentCompletionAuditLog.objects.get( - assignment_user=self.student, - course_session_id=self.cs.id, - assignment_id=self.assignment.id, - completion_status="EVALUATION_SUBMITTED", - ) - self.maxDiff = None - self.assertDictEqual( - acl.completion_data, - { - user_text_input["id"]: { - "id": user_text_input["id"], - "type": "user_text_input", - "value": { - "text": "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest? Begründe deine Empfehlung", - }, - "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, - "expert_data": {"points": 1, "comment": "Gut gemacht!"}, - }, - }, - ) diff --git a/server/vbv_lernwelt/assignment/tests/test_graphql.py b/server/vbv_lernwelt/assignment/tests/test_graphql.py new file mode 100644 index 00000000..8ad00838 --- /dev/null +++ b/server/vbv_lernwelt/assignment/tests/test_graphql.py @@ -0,0 +1,368 @@ +import json +from datetime import date + +from django.utils import timezone +from graphene_django.utils import GraphQLTestCase + +from vbv_lernwelt.assignment.models import ( + Assignment, + AssignmentCompletion, + AssignmentCompletionAuditLog, +) +from vbv_lernwelt.core.create_default_users import create_default_users +from vbv_lernwelt.core.models import User +from vbv_lernwelt.core.utils import find_first +from vbv_lernwelt.course.creators.test_course import create_test_course +from vbv_lernwelt.course.models import CourseSession + + +class AttendanceCourseUserMutationTestCase(GraphQLTestCase): + GRAPHQL_URL = "/server/graphql/" + + def setUp(self): + create_default_users() + create_test_course(include_vv=False, with_sessions=True) + self.course_session = CourseSession.objects.get(title="Test Bern 2022 a") + self.trainer = User.objects.get(username="test-trainer1@example.com") + self.student = User.objects.get(username="test-student1@example.com") + + self.assignment = Assignment.objects.get( + slug="test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice" + ) + self.assignment_subtasks = self.assignment.filter_user_subtasks() + + # self.client.force_login(self.trainer) + + def test_student_can_upsertAssignmentCompletion(self): + self.client.force_login(self.student) + user_text_input = find_first( + self.assignment_subtasks, pred=lambda x: x["type"] == "user_text_input" + ) + + completion_data_string = json.dumps( + { + user_text_input["id"]: {"user_data": {"text": "Hallo via API"}}, + } + ).replace('"', '\\"') + + query = f""" + mutation {{ + upsert_assignment_completion( + assignment_id: {self.assignment.id} + course_session_id: {self.course_session.id} + completion_status: IN_PROGRESS + completion_data_string: "{completion_data_string}" + ) {{ + assignment_completion {{ + id + completion_status + completion_data + assignment_user {{ id }} + assignment {{ id }} + }} + }} + }} + """ + + response = self.query(query) + self.assertResponseNoErrors(response) + + data = json.loads(response.content)["data"]["upsert_assignment_completion"][ + "assignment_completion" + ] + + self.assertEqual(data["assignment_user"]["id"], str(self.student.id)) + self.assertEqual(data["assignment"]["id"], str(self.assignment.id)) + self.assertEqual(data["completion_status"], "IN_PROGRESS") + self.assertDictEqual( + data["completion_data"], + { + user_text_input["id"]: {"user_data": {"text": "Hallo via API"}}, + }, + ) + + # check DB data + db_entry = AssignmentCompletion.objects.get( + assignment_user=self.student, + course_session_id=self.course_session.id, + assignment_id=self.assignment.id, + ) + self.assertEqual(db_entry.completion_status, "IN_PROGRESS") + self.assertDictEqual( + db_entry.completion_data, + { + user_text_input["id"]: {"user_data": {"text": "Hallo via API"}}, + }, + ) + + # submit the response + completion_data_string = json.dumps( + { + user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}}, + } + ).replace('"', '\\"') + + query = f""" + mutation {{ + upsert_assignment_completion( + assignment_id: {self.assignment.id} + course_session_id: {self.course_session.id} + completion_status: SUBMITTED + completion_data_string: "{completion_data_string}" + ) {{ + assignment_completion {{ + id + completion_status + completion_data + assignment_user {{ id }} + assignment {{ id }} + }} + }} + }} + """ + + response = self.query(query) + self.assertResponseNoErrors(response) + + data = json.loads(response.content)["data"]["upsert_assignment_completion"][ + "assignment_completion" + ] + + self.assertEqual(data["assignment_user"]["id"], str(self.student.id)) + self.assertEqual(data["assignment"]["id"], str(self.assignment.id)) + self.assertEqual(data["completion_status"], "SUBMITTED") + self.assertDictEqual( + data["completion_data"], + { + user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}}, + }, + ) + + # check DB data + db_entry = AssignmentCompletion.objects.get( + assignment_user=self.student, + course_session_id=self.course_session.id, + assignment_id=self.assignment.id, + ) + self.assertEqual(db_entry.completion_status, "SUBMITTED") + self.assertEqual(db_entry.submitted_at.date(), date.today()) + self.assertDictEqual( + db_entry.completion_data, + { + user_text_input["id"]: {"user_data": {"text": "Hallo via API 2"}}, + }, + ) + + # second submit will fail + completion_data_string = json.dumps( + { + user_text_input["id"]: {"user_data": {"text": "Hallo via API 3"}}, + } + ).replace('"', '\\"') + + query = f""" + mutation {{ + upsert_assignment_completion( + assignment_id: {self.assignment.id} + course_session_id: {self.course_session.id} + completion_status: SUBMITTED + completion_data_string: "{completion_data_string}" + ) {{ + assignment_completion {{ + id + completion_status + completion_data + assignment_user {{ id }} + assignment {{ id }} + }} + }} + }} + """ + + response = self.query(query) + self.assertResponseHasErrors(response) + self.assertTrue("Cannot update completion status" in str(response.json())) + + def test_expert_can_gradeAssignmentCompletion(self): + # setup AssignmentCompletion + subtasks = self.assignment.filter_user_subtasks( + subtask_types=["user_text_input"] + ) + user_text_input = find_first( + subtasks, + pred=lambda x: (value := x.get("value")) + and value.get("text", "").startswith( + "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest?" + ), + ) + + ac = AssignmentCompletion.objects.create( + assignment_user=self.student, + assignment=self.assignment, + course_session=self.course_session, + completion_status="SUBMITTED", + submitted_at=timezone.now(), + completion_data={ + user_text_input["id"]: { + "user_data": {"text": "Ich würde nichts weiteres empfehlen."} + }, + }, + ) + + self.client.force_login(self.trainer) + + completion_data_string = json.dumps( + { + user_text_input["id"]: { + "expert_data": {"points": 1, "comment": "Gut gemacht!"} + }, + } + ).replace('"', '\\"') + + query = f""" + mutation {{ + upsert_assignment_completion( + assignment_id: {self.assignment.id} + assignment_user_id: {self.student.id} + course_session_id: {self.course_session.id} + completion_status: EVALUATION_IN_PROGRESS + completion_data_string: "{completion_data_string}" + ) {{ + assignment_completion {{ + id + completion_status + completion_data + assignment_user {{ id }} + assignment {{ id }} + }} + }} + }} + """ + + response = self.query(query) + self.assertResponseNoErrors(response) + + data = json.loads(response.content)["data"]["upsert_assignment_completion"][ + "assignment_completion" + ] + + self.assertEqual(data["assignment_user"]["id"], str(self.student.id)) + self.assertEqual(data["assignment"]["id"], str(self.assignment.id)) + self.assertEqual(data["completion_status"], "EVALUATION_IN_PROGRESS") + self.assertDictEqual( + data["completion_data"], + { + user_text_input["id"]: { + "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, + "expert_data": {"points": 1, "comment": "Gut gemacht!"}, + }, + }, + ) + + db_entry = AssignmentCompletion.objects.get( + assignment_user=self.student, + course_session_id=self.course_session.id, + assignment_id=self.assignment.id, + ) + self.assertEqual(db_entry.completion_status, "EVALUATION_IN_PROGRESS") + self.assertDictEqual( + db_entry.completion_data, + { + user_text_input["id"]: { + "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, + "expert_data": {"points": 1, "comment": "Gut gemacht!"}, + }, + }, + ) + + # finish grading + completion_data_string = json.dumps( + { + user_text_input["id"]: { + "expert_data": {"points": 1, "comment": "Gut gemacht!"} + }, + } + ).replace('"', '\\"') + + query = f""" + mutation {{ + upsert_assignment_completion( + assignment_id: {self.assignment.id} + assignment_user_id: {self.student.id} + course_session_id: {self.course_session.id} + completion_status: EVALUATION_SUBMITTED + completion_data_string: "{completion_data_string}" + evaluation_grade: 4.5, + evaluation_points: 16, + ) {{ + assignment_completion {{ + id + completion_status + completion_data + assignment_user {{ id }} + assignment {{ id }} + }} + }} + }} + """ + + response = self.query(query) + self.assertResponseNoErrors(response) + + data = json.loads(response.content)["data"]["upsert_assignment_completion"][ + "assignment_completion" + ] + + self.assertEqual(data["assignment_user"]["id"], str(self.student.id)) + self.assertEqual(data["assignment"]["id"], str(self.assignment.id)) + self.assertEqual(data["completion_status"], "EVALUATION_SUBMITTED") + self.assertDictEqual( + data["completion_data"], + { + user_text_input["id"]: { + "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, + "expert_data": {"points": 1, "comment": "Gut gemacht!"}, + }, + }, + ) + + db_entry = AssignmentCompletion.objects.get( + assignment_user=self.student, + course_session_id=self.course_session.id, + assignment_id=self.assignment.id, + ) + self.assertEqual(db_entry.completion_status, "EVALUATION_SUBMITTED") + self.assertEqual(db_entry.evaluation_grade, 4.5) + self.assertEqual(db_entry.evaluation_points, 16) + self.assertDictEqual( + db_entry.completion_data, + { + user_text_input["id"]: { + "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, + "expert_data": {"points": 1, "comment": "Gut gemacht!"}, + }, + }, + ) + + # `EVALUATION_SUBMITTED` will create a new AssignmentCompletionAuditLog + acl = AssignmentCompletionAuditLog.objects.get( + assignment_user=self.student, + course_session_id=self.course_session.id, + assignment_id=self.assignment.id, + completion_status="EVALUATION_SUBMITTED", + ) + self.maxDiff = None + self.assertDictEqual( + acl.completion_data, + { + user_text_input["id"]: { + "id": user_text_input["id"], + "type": "user_text_input", + "value": { + "text": "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest? Begründe deine Empfehlung", + }, + "user_data": {"text": "Ich würde nichts weiteres empfehlen."}, + "expert_data": {"points": 1, "comment": "Gut gemacht!"}, + }, + }, + ) diff --git a/server/vbv_lernwelt/assignment/tests/test_services.py b/server/vbv_lernwelt/assignment/tests/test_services.py index 64e24d77..c523424a 100644 --- a/server/vbv_lernwelt/assignment/tests/test_services.py +++ b/server/vbv_lernwelt/assignment/tests/test_services.py @@ -8,6 +8,7 @@ from vbv_lernwelt.assignment.models import ( Assignment, AssignmentCompletion, AssignmentCompletionAuditLog, + AssignmentCompletionStatus, ) from vbv_lernwelt.assignment.services import update_assignment_completion from vbv_lernwelt.core.create_default_users import create_default_users @@ -161,7 +162,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED, ) ac = AssignmentCompletion.objects.get( @@ -216,7 +217,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment=self.assignment, course_session=self.course_session, submitted_at=timezone.now(), - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED.value, completion_data={ user_text_input0["id"]: { "user_data": {"text": "Am Anfang war das Wort... 0"} @@ -232,7 +233,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED, ) # can submit twice with flag @@ -240,7 +241,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED, validate_completion_status_change=False, ) @@ -280,7 +281,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED, copy_task_data=True, ) @@ -306,7 +307,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED, ) evaluation_task = self.assignment.get_evaluation_tasks()[0] @@ -320,7 +321,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): "expert_data": {"points": 2, "text": "Gut gemacht!"} }, }, - completion_status="EVALUATION_IN_PROGRESS", + completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, evaluation_user=self.trainer, ) @@ -352,7 +353,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED, completion_data={ user_text_input["id"]: { "user_data": {"text": "Ich würde nichts weiteres empfehlen."} @@ -369,7 +370,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): "expert_data": {"points": 1, "comment": "Gut gemacht!"} }, }, - completion_status="EVALUATION_IN_PROGRESS", + completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, evaluation_user=self.trainer, ) @@ -404,7 +405,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="IN_PROGRESS", + completion_status=AssignmentCompletionStatus.IN_PROGRESS.value, completion_data={ user_text_input["id"]: { "user_data": {"text": "Ich würde nichts weiteres empfehlen."} @@ -417,7 +418,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="EVALUATION_IN_PROGRESS", + completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, completion_data={ user_text_input["id"]: { "expert_data": {"points": 1, "comment": "Gut gemacht!"} @@ -445,7 +446,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED.value, completion_data={ user_text_input["id"]: { "user_data": {"text": "Ich würde nichts weiteres empfehlen."} @@ -464,7 +465,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): "expert_data": {"points": 2, "text": "Gut gemacht!"} }, }, - completion_status="EVALUATION_IN_PROGRESS", + completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, evaluation_user=self.trainer, ) @@ -475,7 +476,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment=self.assignment, course_session=self.course_session, completion_data={}, - completion_status="EVALUATION_SUBMITTED", + completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED, evaluation_user=self.trainer, evaluation_grade=None, evaluation_points=None, @@ -486,7 +487,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): assignment=self.assignment, course_session=self.course_session, completion_data={}, - completion_status="EVALUATION_SUBMITTED", + completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED, evaluation_user=self.trainer, evaluation_grade=4.5, evaluation_points=16, diff --git a/server/vbv_lernwelt/assignment/views.py b/server/vbv_lernwelt/assignment/views.py index 1b5650bd..9b80dd36 100644 --- a/server/vbv_lernwelt/assignment/views.py +++ b/server/vbv_lernwelt/assignment/views.py @@ -1,69 +1,14 @@ import structlog from rest_framework.decorators import api_view from rest_framework.exceptions import PermissionDenied -from rest_framework.generics import get_object_or_404 from rest_framework.response import Response -from wagtail.models import Page from vbv_lernwelt.assignment.models import AssignmentCompletion -from vbv_lernwelt.assignment.serializers import AssignmentCompletionSerializer -from vbv_lernwelt.assignment.services import update_assignment_completion -from vbv_lernwelt.core.models import User -from vbv_lernwelt.course.models import CourseSession -from vbv_lernwelt.course.permissions import ( - has_course_access, - has_course_access_by_page_request, - is_course_session_expert, -) +from vbv_lernwelt.course.permissions import is_course_session_expert logger = structlog.get_logger(__name__) -def _request_assignment_completion( - assignment_id, course_session_id, assignment_user_id -): - try: - response_data = AssignmentCompletionSerializer( - AssignmentCompletion.objects.get( - assignment_user_id=assignment_user_id, - assignment_id=assignment_id, - course_session_id=course_session_id, - ), - ).data - - return Response(status=200, data=response_data) - except Exception as e: - logger.error(e) - return Response({"error": str(e)}, status=404) - - -@api_view(["GET"]) -def request_assignment_completion(request, assignment_id, course_session_id): - course_id = get_object_or_404(CourseSession, id=course_session_id).course_id - if has_course_access(request.user, course_id): - return _request_assignment_completion( - assignment_id=assignment_id, - course_session_id=course_session_id, - assignment_user_id=request.user.id, - ) - raise PermissionDenied() - - -@api_view(["GET"]) -def request_assignment_completion_for_user( - request, assignment_id, course_session_id, user_id -): - if request.user.id == user_id or is_course_session_expert( - request.user, course_session_id - ): - return _request_assignment_completion( - assignment_id=assignment_id, - course_session_id=course_session_id, - assignment_user_id=user_id, - ) - raise PermissionDenied() - - @api_view(["GET"]) def request_assignment_completion_status(request, assignment_id, course_session_id): # TODO quickfix before GraphQL... @@ -75,102 +20,3 @@ def request_assignment_completion_status(request, assignment_id, course_session_ return Response(status=200, data=qs) raise PermissionDenied() - - -@api_view(["POST"]) -def upsert_user_assignment_completion(request): - try: - assignment_id = request.data.get("assignment_id") - course_session_id = request.data.get("course_session_id") - completion_status = request.data.get("completion_status", "IN_PROGRESS") - completion_data = request.data.get("completion_data", {}) - - assignment_page = Page.objects.get(id=assignment_id) - - if not has_course_access_by_page_request(request, assignment_page): - raise PermissionDenied() - - assignment = assignment_page.specific - - ac = update_assignment_completion( - assignment_user=request.user, - assignment=assignment, - course_session=CourseSession.objects.get(id=course_session_id), - completion_data=completion_data, - completion_status=completion_status, - copy_task_data=False, - ) - - logger.debug( - "upsert_user_assignment_completion successful", - label="assignment_api", - assignment_id=assignment.id, - assignment_title=assignment.title, - assignment_user_id=request.user.id, - course_session_id=course_session_id, - completion_status=completion_status, - ) - - return Response(status=200, data=AssignmentCompletionSerializer(ac).data) - except PermissionDenied as e: - raise e - except Exception as e: - logger.error(e, exc_info=True) - return Response({"error": str(e)}, status=404) - - -@api_view(["POST"]) -def evaluate_assignment_completion(request): - try: - assignment_id = request.data.get("assignment_id") - assignment_user_id = request.data.get("assignment_user_id") - course_session_id = request.data.get("course_session_id") - completion_status = request.data.get( - "completion_status", "EVALUATION_IN_PROGRESS" - ) - completion_data = request.data.get("completion_data", {}) - evaluation_grade = request.data.get("evaluation_grade", None) - evaluation_points = request.data.get("evaluation_grade", None) - - assignment_page = Page.objects.get(id=assignment_id) - assignment_user = User.objects.get(id=assignment_user_id) - - if not has_course_access_by_page_request(request, assignment_page): - raise PermissionDenied() - - if not is_course_session_expert(request.user, course_session_id): - raise PermissionDenied() - - assignment = assignment_page.specific - - ac = update_assignment_completion( - assignment_user=assignment_user, - assignment=assignment, - course_session=CourseSession.objects.get(id=course_session_id), - completion_data=completion_data, - completion_status=completion_status, - copy_task_data=False, - evaluation_user=request.user, - evaluation_grade=evaluation_grade, - evaluation_points=evaluation_points, - ) - - logger.debug( - "grade_assignment_completion successful", - label="assignment_api", - assignment_id=assignment.id, - assignment_title=assignment.title, - assignment_user_id=assignment_user_id, - course_session_id=course_session_id, - completion_status=completion_status, - evaluation_user_id=request.user.id, - evaluation_grade=evaluation_grade, - evaluation_points=evaluation_points, - ) - - return Response(status=200, data=AssignmentCompletionSerializer(ac).data) - except PermissionDenied as e: - raise e - except Exception as e: - logger.error(e, exc_info=True) - return Response({"error": str(e)}, status=404) diff --git a/server/vbv_lernwelt/core/middleware/graphene.py b/server/vbv_lernwelt/core/middleware/graphene.py index 4128d252..299891ae 100644 --- a/server/vbv_lernwelt/core/middleware/graphene.py +++ b/server/vbv_lernwelt/core/middleware/graphene.py @@ -1,5 +1,3 @@ -import traceback - import structlog from graphene import ResolveInfo @@ -12,5 +10,7 @@ class GrapheneErrorLoggingMiddleware(object): try: return next(root, info, **args) except Exception as error: - logger.error(traceback.format_exc()) + logger.error( + "GraphQL error", label="graphql_error", info=info, exc_info=error + ) raise error From df13e99d6f2a873fe3951569c9d854718c7ae884 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 28 Jun 2023 18:47:55 +0200 Subject: [PATCH 2/4] Play with generated graphql code --- client/codegen.ts | 7 + client/src/gql/fragment-masking.ts | 48 +--- client/src/gql/gql.ts | 8 +- client/src/gql/graphql.ts | 262 +++++++++--------- client/src/graphql/mutations.ts | 2 +- client/src/types.ts | 8 +- .../commands/create_default_courses.py | 4 +- 7 files changed, 163 insertions(+), 176 deletions(-) diff --git a/client/codegen.ts b/client/codegen.ts index 0ace6d9f..117de583 100644 --- a/client/codegen.ts +++ b/client/codegen.ts @@ -16,6 +16,13 @@ const config: CodegenConfig = { plugins: [], }, }, + config: { + enumsAsTypes: true, + namingConvention: { + typeNames: "change-case-all#pascalCase", + enumValues: "change-case-all#upperCase", + }, + }, }; export default config; diff --git a/client/src/gql/fragment-masking.ts b/client/src/gql/fragment-masking.ts index c000279a..a94b5c77 100644 --- a/client/src/gql/fragment-masking.ts +++ b/client/src/gql/fragment-masking.ts @@ -1,13 +1,11 @@ -import type { ResultOf, DocumentTypeDecoration, TypedDocumentNode } from '@graphql-typed-document-node/core'; -import type { FragmentDefinitionNode } from 'graphql'; -import type { Incremental } from './graphql'; +import type { ResultOf, TypedDocumentNode as DocumentNode, } from '@graphql-typed-document-node/core'; -export type FragmentType> = TDocumentType extends DocumentTypeDecoration< +export type FragmentType> = TDocumentType extends DocumentNode< infer TType, any > - ? [TType] extends [{ ' $fragmentName'?: infer TKey }] + ? TType extends { ' $fragmentName'?: infer TKey } ? TKey extends string ? { ' $fragmentRefs'?: { [key in TKey]: TType } } : never @@ -16,51 +14,35 @@ export type FragmentType> // return non-nullable if `fragmentType` is non-nullable export function useFragment( - _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> + _documentNode: DocumentNode, + fragmentType: FragmentType> ): TType; // return nullable if `fragmentType` is nullable export function useFragment( - _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> | null | undefined + _documentNode: DocumentNode, + fragmentType: FragmentType> | null | undefined ): TType | null | undefined; // return array of non-nullable if `fragmentType` is array of non-nullable export function useFragment( - _documentNode: DocumentTypeDecoration, - fragmentType: ReadonlyArray>> + _documentNode: DocumentNode, + fragmentType: ReadonlyArray>> ): ReadonlyArray; // return array of nullable if `fragmentType` is array of nullable export function useFragment( - _documentNode: DocumentTypeDecoration, - fragmentType: ReadonlyArray>> | null | undefined + _documentNode: DocumentNode, + fragmentType: ReadonlyArray>> | null | undefined ): ReadonlyArray | null | undefined; export function useFragment( - _documentNode: DocumentTypeDecoration, - fragmentType: FragmentType> | ReadonlyArray>> | null | undefined + _documentNode: DocumentNode, + fragmentType: FragmentType> | ReadonlyArray>> | null | undefined ): TType | ReadonlyArray | null | undefined { return fragmentType as any; } export function makeFragmentData< - F extends DocumentTypeDecoration, + F extends DocumentNode, FT extends ResultOf >(data: FT, _fragment: F): FragmentType { return data as FragmentType; -} -export function isFragmentReady( - queryNode: DocumentTypeDecoration, - fragmentNode: TypedDocumentNode, - data: FragmentType, any>> | null | undefined -): data is FragmentType { - const deferredFields = (queryNode as { __meta__?: { deferredFields: Record } }).__meta__ - ?.deferredFields; - - if (!deferredFields) return true; - - const fragDef = fragmentNode.definitions[0] as FragmentDefinitionNode | undefined; - const fragName = fragDef?.name?.value; - - const fields = (fragName && deferredFields[fragName]) || []; - return fields.length > 0 && fields.every(field => data && field in data); -} +} \ No newline at end of file diff --git a/client/src/gql/gql.ts b/client/src/gql/gql.ts index 9a620a5b..f47da5f3 100644 --- a/client/src/gql/gql.ts +++ b/client/src/gql/gql.ts @@ -10,11 +10,11 @@ import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document- * 2. It is not minifiable, so the string of a GraphQL query will be multiple times inside the bundle. * 3. It does not support dead code elimination, so it will add unused operations. * - * Therefore it is highly recommended to use the babel or swc plugin for production. + * Therefore it is highly recommended to use the babel-plugin for production. */ const documents = { "\n mutation SendFeedbackMutation($input: SendFeedbackInput!) {\n send_feedback(input: $input) {\n feedback_response {\n id\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument, - "\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n $completionStatus: String!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n": types.UpsertAssignmentCompletionDocument, + "\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n": types.UpsertAssignmentCompletionDocument, "\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument, "\n query courseQuery($courseId: Int!) {\n course(id: $courseId) {\n id\n slug\n title\n category_name\n learning_path {\n id\n }\n }\n }\n": types.CourseQueryDocument, }; @@ -25,7 +25,7 @@ const documents = { * * @example * ```ts - * const query = graphql(`query GetUser($id: ID!) { user(id: $id) { name } }`); + * const query = gql(`query GetUser($id: ID!) { user(id: $id) { name } }`); * ``` * * The query argument is unknown! @@ -40,7 +40,7 @@ export function graphql(source: "\n mutation SendFeedbackMutation($input: SendF /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n $completionStatus: String!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n"): (typeof documents)["\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n $completionStatus: String!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n"]; +export function graphql(source: "\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n"): (typeof documents)["\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $assignmentUserId: ID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationGrade: Float\n $evaluationPoints: Float\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_grade: $evaluationGrade\n evaluation_points: $evaluationPoints\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_grade\n evaluation_points\n completion_data\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/client/src/gql/graphql.ts b/client/src/gql/graphql.ts index 06926e49..204397e4 100644 --- a/client/src/gql/graphql.ts +++ b/client/src/gql/graphql.ts @@ -5,178 +5,180 @@ export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; -export type MakeEmpty = { [_ in K]?: never }; -export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { - ID: { input: string; output: string; } - String: { input: string; output: string; } - Boolean: { input: boolean; output: boolean; } - Int: { input: number; output: number; } - Float: { input: number; output: number; } + ID: string; + String: string; + Boolean: boolean; + Int: number; + Float: number; /** * The `DateTime` scalar type represents a DateTime * value as specified by * [iso8601](https://en.wikipedia.org/wiki/ISO_8601). */ - DateTime: { input: any; output: any; } + DateTime: any; /** * The `GenericScalar` scalar type represents a generic * GraphQL scalar value that could be: * String, Boolean, Int, Float, List or Object. */ - GenericScalar: { input: any; output: any; } - JSONStreamField: { input: any; output: any; } + GenericScalar: any; + JSONStreamField: any; /** * Allows use of a JSON String for input / output from the GraphQL schema. * * Use of this type is *not recommended* as you lose the benefits of having a defined, static * schema (one of the key benefits of GraphQL). */ - JSONString: { input: any; output: any; } + JSONString: any; }; /** An enumeration. */ -export enum AssignmentAssignmentAssignmentTypeChoices { +export type AssignmentAssignmentAssignmentTypeChoices = /** CASEWORK */ - Casework = 'CASEWORK', + | 'CASEWORK' /** PREP_ASSIGNMENT */ - PrepAssignment = 'PREP_ASSIGNMENT', + | 'PREP_ASSIGNMENT' /** REFLECTION */ - Reflection = 'REFLECTION' -} + | 'REFLECTION'; /** An enumeration. */ -export enum AssignmentAssignmentCompletionCompletionStatusChoices { +export type AssignmentAssignmentCompletionCompletionStatusChoices = /** EVALUATION_IN_PROGRESS */ - EvaluationInProgress = 'EVALUATION_IN_PROGRESS', + | 'EVALUATION_IN_PROGRESS' /** EVALUATION_SUBMITTED */ - EvaluationSubmitted = 'EVALUATION_SUBMITTED', + | 'EVALUATION_SUBMITTED' /** IN_PROGRESS */ - InProgress = 'IN_PROGRESS', + | 'IN_PROGRESS' /** SUBMITTED */ - Submitted = 'SUBMITTED' -} + | 'SUBMITTED'; export type AssignmentCompletionMutation = { __typename?: 'AssignmentCompletionMutation'; assignment_completion?: Maybe; }; +/** An enumeration. */ +export type AssignmentCompletionStatus = + | 'EVALUATION_IN_PROGRESS' + | 'EVALUATION_SUBMITTED' + | 'IN_PROGRESS' + | 'SUBMITTED'; + export type AssignmentCompletionType = { __typename?: 'AssignmentCompletionType'; - additional_json_data: Scalars['JSONString']['output']; + additional_json_data: Scalars['JSONString']; assignment: AssignmentType; assignment_user: UserType; - completion_data?: Maybe; + completion_data?: Maybe; completion_status: AssignmentAssignmentCompletionCompletionStatusChoices; - created_at: Scalars['DateTime']['output']; - evaluation_grade?: Maybe; - evaluation_points?: Maybe; - evaluation_submitted_at?: Maybe; + created_at: Scalars['DateTime']; + evaluation_grade?: Maybe; + evaluation_points?: Maybe; + evaluation_submitted_at?: Maybe; evaluation_user?: Maybe; - id: Scalars['ID']['output']; - submitted_at?: Maybe; - updated_at: Scalars['DateTime']['output']; + id: Scalars['ID']; + submitted_at?: Maybe; + updated_at: Scalars['DateTime']; }; export type AssignmentType = CoursePageInterface & { __typename?: 'AssignmentType'; assignment_type: AssignmentAssignmentAssignmentTypeChoices; - content_type?: Maybe; + content_type?: Maybe; /** Zeitaufwand als Text */ - effort_required: Scalars['String']['output']; + effort_required: Scalars['String']; /** Beschreibung der Bewertung */ - evaluation_description: Scalars['String']['output']; + evaluation_description: Scalars['String']; /** URL zum Beurteilungsinstrument */ - evaluation_document_url: Scalars['String']['output']; - evaluation_tasks?: Maybe; - frontend_url?: Maybe; - id?: Maybe; + evaluation_document_url: Scalars['String']; + evaluation_tasks?: Maybe; + frontend_url?: Maybe; + id?: Maybe; /** Erläuterung der Ausgangslage */ - intro_text: Scalars['String']['output']; - live?: Maybe; - performance_objectives?: Maybe; - slug?: Maybe; - tasks?: Maybe; - title?: Maybe; - translation_key?: Maybe; + intro_text: Scalars['String']; + live?: Maybe; + performance_objectives?: Maybe; + slug?: Maybe; + tasks?: Maybe; + title?: Maybe; + translation_key?: Maybe; }; /** An enumeration. */ -export enum CoreUserLanguageChoices { +export type CoreUserLanguageChoices = /** Deutsch */ - De = 'DE', + | 'DE' /** Français */ - Fr = 'FR', + | 'FR' /** Italiano */ - It = 'IT' -} + | 'IT'; export type CoursePageInterface = { - content_type?: Maybe; - frontend_url?: Maybe; - id?: Maybe; - live?: Maybe; - slug?: Maybe; - title?: Maybe; - translation_key?: Maybe; + content_type?: Maybe; + frontend_url?: Maybe; + id?: Maybe; + live?: Maybe; + slug?: Maybe; + title?: Maybe; + translation_key?: Maybe; }; export type CourseType = { __typename?: 'CourseType'; - category_name: Scalars['String']['output']; - id: Scalars['ID']['output']; + category_name: Scalars['String']; + id: Scalars['ID']; learning_path?: Maybe; - slug: Scalars['String']['output']; - title: Scalars['String']['output']; + slug: Scalars['String']; + title: Scalars['String']; }; export type ErrorType = { __typename?: 'ErrorType'; - field: Scalars['String']['output']; - messages: Array; + field: Scalars['String']; + messages: Array; }; export type FeedbackResponse = Node & { __typename?: 'FeedbackResponse'; - created_at: Scalars['DateTime']['output']; - data?: Maybe; + created_at: Scalars['DateTime']; + data?: Maybe; /** The ID of the object */ - id: Scalars['ID']['output']; + id: Scalars['ID']; }; export type LearningPathType = CoursePageInterface & { __typename?: 'LearningPathType'; - content_type?: Maybe; - depth: Scalars['Int']['output']; - draft_title: Scalars['String']['output']; - expire_at?: Maybe; - expired: Scalars['Boolean']['output']; - first_published_at?: Maybe; - frontend_url?: Maybe; - go_live_at?: Maybe; - has_unpublished_changes: Scalars['Boolean']['output']; - id?: Maybe; - last_published_at?: Maybe; - latest_revision_created_at?: Maybe; - live?: Maybe; - locked: Scalars['Boolean']['output']; - locked_at?: Maybe; + content_type?: Maybe; + depth: Scalars['Int']; + draft_title: Scalars['String']; + expire_at?: Maybe; + expired: Scalars['Boolean']; + first_published_at?: Maybe; + frontend_url?: Maybe; + go_live_at?: Maybe; + has_unpublished_changes: Scalars['Boolean']; + id?: Maybe; + last_published_at?: Maybe; + latest_revision_created_at?: Maybe; + live?: Maybe; + locked: Scalars['Boolean']; + locked_at?: Maybe; locked_by?: Maybe; - numchild: Scalars['Int']['output']; + numchild: Scalars['Int']; owner?: Maybe; - path: Scalars['String']['output']; + path: Scalars['String']; /** Die informative Beschreibung, dargestellt in Suchmaschinen-Ergebnissen unter der Überschrift. */ - search_description: Scalars['String']['output']; + search_description: Scalars['String']; /** Der Titel der Seite, dargestellt in Suchmaschinen-Ergebnissen als die verlinkte Überschrift. */ - seo_title: Scalars['String']['output']; + seo_title: Scalars['String']; /** Ob ein Link zu dieser Seite in automatisch generierten Menüs auftaucht. */ - show_in_menus: Scalars['Boolean']['output']; - slug?: Maybe; - title?: Maybe; - translation_key?: Maybe; - url_path: Scalars['String']['output']; + show_in_menus: Scalars['Boolean']; + slug?: Maybe; + title?: Maybe; + translation_key?: Maybe; + url_path: Scalars['String']; }; export type Mutation = { @@ -186,25 +188,25 @@ export type Mutation = { }; -export type MutationSend_FeedbackArgs = { +export type MutationSendFeedbackArgs = { input: SendFeedbackInput; }; -export type MutationUpsert_Assignment_CompletionArgs = { - assignment_id: Scalars['ID']['input']; - assignment_user_id?: InputMaybe; - completion_data_string?: InputMaybe; - completion_status?: InputMaybe; - course_session_id: Scalars['ID']['input']; - evaluation_grade?: InputMaybe; - evaluation_points?: InputMaybe; +export type MutationUpsertAssignmentCompletionArgs = { + assignment_id: Scalars['ID']; + assignment_user_id?: InputMaybe; + completion_data_string?: InputMaybe; + completion_status?: InputMaybe; + course_session_id: Scalars['ID']; + evaluation_grade?: InputMaybe; + evaluation_points?: InputMaybe; }; /** An object with an ID */ export type Node = { /** The ID of the object */ - id: Scalars['ID']['output']; + id: Scalars['ID']; }; export type Query = { @@ -216,32 +218,32 @@ export type Query = { export type QueryAssignmentArgs = { - id?: InputMaybe; - slug?: InputMaybe; + id?: InputMaybe; + slug?: InputMaybe; }; -export type QueryAssignment_CompletionArgs = { - assignment_id: Scalars['ID']['input']; - assignment_user_id?: InputMaybe; - course_session_id: Scalars['ID']['input']; +export type QueryAssignmentCompletionArgs = { + assignment_id: Scalars['ID']; + assignment_user_id?: InputMaybe; + course_session_id: Scalars['ID']; }; export type QueryCourseArgs = { - id?: InputMaybe; + id?: InputMaybe; }; export type SendFeedbackInput = { - clientMutationId?: InputMaybe; - course_session: Scalars['Int']['input']; - data?: InputMaybe; - page: Scalars['String']['input']; + clientMutationId?: InputMaybe; + course_session: Scalars['Int']; + data?: InputMaybe; + page: Scalars['String']; }; export type SendFeedbackPayload = { __typename?: 'SendFeedbackPayload'; - clientMutationId?: Maybe; + clientMutationId?: Maybe; /** May contain more than one error for same field. */ errors?: Maybe>>; feedback_response?: Maybe; @@ -249,14 +251,14 @@ export type SendFeedbackPayload = { export type UserType = { __typename?: 'UserType'; - avatar_url: Scalars['String']['output']; - email: Scalars['String']['output']; - first_name: Scalars['String']['output']; - id: Scalars['ID']['output']; + avatar_url: Scalars['String']; + email: Scalars['String']; + first_name: Scalars['String']; + id: Scalars['ID']; language: CoreUserLanguageChoices; - last_name: Scalars['String']['output']; + last_name: Scalars['String']; /** Erforderlich. 150 Zeichen oder weniger. Nur Buchstaben, Ziffern und @/./+/-/_. */ - username: Scalars['String']['output']; + username: Scalars['String']; }; export type SendFeedbackMutationMutationVariables = Exact<{ @@ -267,29 +269,29 @@ export type SendFeedbackMutationMutationVariables = Exact<{ export type SendFeedbackMutationMutation = { __typename?: 'Mutation', send_feedback?: { __typename?: 'SendFeedbackPayload', feedback_response?: { __typename?: 'FeedbackResponse', id: string } | null, errors?: Array<{ __typename?: 'ErrorType', field: string, messages: Array } | null> | null } | null }; export type UpsertAssignmentCompletionMutationVariables = Exact<{ - assignmentId: Scalars['ID']['input']; - courseSessionId: Scalars['ID']['input']; - assignmentUserId?: InputMaybe; - completionStatus: Scalars['String']['input']; - completionDataString: Scalars['String']['input']; - evaluationGrade?: InputMaybe; - evaluationPoints?: InputMaybe; + assignmentId: Scalars['ID']; + courseSessionId: Scalars['ID']; + assignmentUserId?: InputMaybe; + completionStatus: AssignmentCompletionStatus; + completionDataString: Scalars['String']; + evaluationGrade?: InputMaybe; + evaluationPoints?: InputMaybe; }>; export type UpsertAssignmentCompletionMutation = { __typename?: 'Mutation', upsert_assignment_completion?: { __typename?: 'AssignmentCompletionMutation', assignment_completion?: { __typename?: 'AssignmentCompletionType', id: string, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_grade?: number | null, evaluation_points?: number | null, completion_data?: any | null } | null } | null }; export type AssignmentCompletionQueryQueryVariables = Exact<{ - assignmentId: Scalars['ID']['input']; - courseSessionId: Scalars['ID']['input']; - assignmentUserId?: InputMaybe; + assignmentId: Scalars['ID']; + courseSessionId: Scalars['ID']; + assignmentUserId?: InputMaybe; }>; export type AssignmentCompletionQueryQuery = { __typename?: 'Query', assignment?: { __typename?: 'AssignmentType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, content_type?: string | null, effort_required: string, evaluation_description: string, evaluation_document_url: string, evaluation_tasks?: any | null, id?: string | null, intro_text: string, performance_objectives?: any | null, slug?: string | null, tasks?: any | null, title?: string | null, translation_key?: string | null } | null, assignment_completion?: { __typename?: 'AssignmentCompletionType', id: string, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_grade?: number | null, evaluation_points?: number | null, completion_data?: any | null, evaluation_user?: { __typename?: 'UserType', id: string } | null, assignment_user: { __typename?: 'UserType', id: string } } | null }; export type CourseQueryQueryVariables = Exact<{ - courseId: Scalars['Int']['input']; + courseId: Scalars['Int']; }>; @@ -297,6 +299,6 @@ export type CourseQueryQuery = { __typename?: 'Query', course?: { __typename?: ' export const SendFeedbackMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendFeedbackMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SendFeedbackInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"send_feedback"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feedback_response"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"messages"}}]}}]}}]}}]} as unknown as DocumentNode; -export const UpsertAssignmentCompletionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertAssignmentCompletion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsert_assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_data_string"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_grade"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_points"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}}]}}]} as unknown as DocumentNode; +export const UpsertAssignmentCompletionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpsertAssignmentCompletion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AssignmentCompletionStatus"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Float"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"upsert_assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionStatus"}}},{"kind":"Argument","name":{"kind":"Name","value":"completion_data_string"},"value":{"kind":"Variable","name":{"kind":"Name","value":"completionDataString"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_grade"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationGrade"}}},{"kind":"Argument","name":{"kind":"Name","value":"evaluation_points"},"value":{"kind":"Variable","name":{"kind":"Name","value":"evaluationPoints"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}}]}}]} as unknown as DocumentNode; export const AssignmentCompletionQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"assignmentCompletionQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"effort_required"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_description"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_document_url"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_tasks"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"intro_text"}},{"kind":"Field","name":{"kind":"Name","value":"performance_objectives"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"tasks"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"translation_key"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_completion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"assignment_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"assignment_user_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"assignmentUserId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"completion_status"}},{"kind":"Field","name":{"kind":"Name","value":"submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_submitted_at"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment_user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_grade"}},{"kind":"Field","name":{"kind":"Name","value":"evaluation_points"}},{"kind":"Field","name":{"kind":"Name","value":"completion_data"}}]}}]}}]} as unknown as DocumentNode; export const CourseQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"category_name"}},{"kind":"Field","name":{"kind":"Name","value":"learning_path"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/client/src/graphql/mutations.ts b/client/src/graphql/mutations.ts index 41f07155..1229745b 100644 --- a/client/src/graphql/mutations.ts +++ b/client/src/graphql/mutations.ts @@ -5,7 +5,7 @@ export const UPSERT_ASSIGNMENT_COMPLETION_MUTATION = graphql(` $assignmentId: ID! $courseSessionId: ID! $assignmentUserId: ID - $completionStatus: String! + $completionStatus: AssignmentCompletionStatus! $completionDataString: String! $evaluationGrade: Float $evaluationPoints: Float diff --git a/client/src/types.ts b/client/src/types.ts index d01c1a78..547abd8b 100644 --- a/client/src/types.ts +++ b/client/src/types.ts @@ -1,3 +1,4 @@ +import type { AssignmentCompletionStatus as AssignmentCompletionStatusGenerated } from "@/gql/graphql"; import type { Circle } from "@/services/circle"; import type { Component } from "vue"; @@ -495,12 +496,7 @@ export interface Notification { course: string | null; } -export type AssignmentCompletionStatus = - | "unknwown" - | "IN_PROGRESS" - | "SUBMITTED" - | "EVALUATION_IN_PROGRESS" - | "EVALUATION_SUBMITTED"; +export type AssignmentCompletionStatus = AssignmentCompletionStatusGenerated; export interface UserDataText { text: string; diff --git a/server/vbv_lernwelt/course/management/commands/create_default_courses.py b/server/vbv_lernwelt/course/management/commands/create_default_courses.py index 07ea8409..466efe4e 100644 --- a/server/vbv_lernwelt/course/management/commands/create_default_courses.py +++ b/server/vbv_lernwelt/course/management/commands/create_default_courses.py @@ -20,7 +20,7 @@ from vbv_lernwelt.assignment.creators.create_assignments import ( create_vv_gewinnen_casework, create_vv_reflection, ) -from vbv_lernwelt.assignment.models import Assignment +from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus from vbv_lernwelt.assignment.services import update_assignment_completion from vbv_lernwelt.assignment.tests.assignment_factories import AssignmentListPageFactory from vbv_lernwelt.competence.create_uk_competence_profile import ( @@ -450,7 +450,7 @@ def create_course_uk_de_assignment_completion_data(assignment, course_session, u assignment_user=user, assignment=assignment, course_session=course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED, ) From 2fdae7c1207a00f4844f21927938aa5db8ebf2dc Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 28 Jun 2023 19:35:20 +0200 Subject: [PATCH 3/4] Use different naming convention for GraphQL types --- client/src/gql/graphql.ts | 34 +++++++++---------- client/src/gql/schema.graphql | 12 +++---- .../assignment/graphql/mutations.py | 4 +-- .../assignment/graphql/queries.py | 8 ++--- .../vbv_lernwelt/assignment/graphql/types.py | 4 +-- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/client/src/gql/graphql.ts b/client/src/gql/graphql.ts index 204397e4..6f9da652 100644 --- a/client/src/gql/graphql.ts +++ b/client/src/gql/graphql.ts @@ -56,20 +56,13 @@ export type AssignmentAssignmentCompletionCompletionStatusChoices = export type AssignmentCompletionMutation = { __typename?: 'AssignmentCompletionMutation'; - assignment_completion?: Maybe; + assignment_completion?: Maybe; }; -/** An enumeration. */ -export type AssignmentCompletionStatus = - | 'EVALUATION_IN_PROGRESS' - | 'EVALUATION_SUBMITTED' - | 'IN_PROGRESS' - | 'SUBMITTED'; - -export type AssignmentCompletionType = { - __typename?: 'AssignmentCompletionType'; +export type AssignmentCompletionObjectType = { + __typename?: 'AssignmentCompletionObjectType'; additional_json_data: Scalars['JSONString']; - assignment: AssignmentType; + assignment: AssignmentObjectType; assignment_user: UserType; completion_data?: Maybe; completion_status: AssignmentAssignmentCompletionCompletionStatusChoices; @@ -83,8 +76,15 @@ export type AssignmentCompletionType = { updated_at: Scalars['DateTime']; }; -export type AssignmentType = CoursePageInterface & { - __typename?: 'AssignmentType'; +/** An enumeration. */ +export type AssignmentCompletionStatus = + | 'EVALUATION_IN_PROGRESS' + | 'EVALUATION_SUBMITTED' + | 'IN_PROGRESS' + | 'SUBMITTED'; + +export type AssignmentObjectType = CoursePageInterface & { + __typename?: 'AssignmentObjectType'; assignment_type: AssignmentAssignmentAssignmentTypeChoices; content_type?: Maybe; /** Zeitaufwand als Text */ @@ -211,8 +211,8 @@ export type Node = { export type Query = { __typename?: 'Query'; - assignment?: Maybe; - assignment_completion?: Maybe; + assignment?: Maybe; + assignment_completion?: Maybe; course?: Maybe; }; @@ -279,7 +279,7 @@ export type UpsertAssignmentCompletionMutationVariables = Exact<{ }>; -export type UpsertAssignmentCompletionMutation = { __typename?: 'Mutation', upsert_assignment_completion?: { __typename?: 'AssignmentCompletionMutation', assignment_completion?: { __typename?: 'AssignmentCompletionType', id: string, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_grade?: number | null, evaluation_points?: number | null, completion_data?: any | null } | null } | null }; +export type UpsertAssignmentCompletionMutation = { __typename?: 'Mutation', upsert_assignment_completion?: { __typename?: 'AssignmentCompletionMutation', assignment_completion?: { __typename?: 'AssignmentCompletionObjectType', id: string, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_grade?: number | null, evaluation_points?: number | null, completion_data?: any | null } | null } | null }; export type AssignmentCompletionQueryQueryVariables = Exact<{ assignmentId: Scalars['ID']; @@ -288,7 +288,7 @@ export type AssignmentCompletionQueryQueryVariables = Exact<{ }>; -export type AssignmentCompletionQueryQuery = { __typename?: 'Query', assignment?: { __typename?: 'AssignmentType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, content_type?: string | null, effort_required: string, evaluation_description: string, evaluation_document_url: string, evaluation_tasks?: any | null, id?: string | null, intro_text: string, performance_objectives?: any | null, slug?: string | null, tasks?: any | null, title?: string | null, translation_key?: string | null } | null, assignment_completion?: { __typename?: 'AssignmentCompletionType', id: string, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_grade?: number | null, evaluation_points?: number | null, completion_data?: any | null, evaluation_user?: { __typename?: 'UserType', id: string } | null, assignment_user: { __typename?: 'UserType', id: string } } | null }; +export type AssignmentCompletionQueryQuery = { __typename?: 'Query', assignment?: { __typename?: 'AssignmentObjectType', assignment_type: AssignmentAssignmentAssignmentTypeChoices, content_type?: string | null, effort_required: string, evaluation_description: string, evaluation_document_url: string, evaluation_tasks?: any | null, id?: string | null, intro_text: string, performance_objectives?: any | null, slug?: string | null, tasks?: any | null, title?: string | null, translation_key?: string | null } | null, assignment_completion?: { __typename?: 'AssignmentCompletionObjectType', id: string, completion_status: AssignmentAssignmentCompletionCompletionStatusChoices, submitted_at?: any | null, evaluation_submitted_at?: any | null, evaluation_grade?: number | null, evaluation_points?: number | null, completion_data?: any | null, evaluation_user?: { __typename?: 'UserType', id: string } | null, assignment_user: { __typename?: 'UserType', id: string } } | null }; export type CourseQueryQueryVariables = Exact<{ courseId: Scalars['Int']; diff --git a/client/src/gql/schema.graphql b/client/src/gql/schema.graphql index c381bea3..ab93d32e 100644 --- a/client/src/gql/schema.graphql +++ b/client/src/gql/schema.graphql @@ -1,7 +1,7 @@ type Query { course(id: Int): CourseType - assignment(id: ID, slug: String): AssignmentType - assignment_completion(assignment_id: ID!, course_session_id: ID!, assignment_user_id: ID): AssignmentCompletionType + assignment(id: ID, slug: String): AssignmentObjectType + assignment_completion(assignment_id: ID!, course_session_id: ID!, assignment_user_id: ID): AssignmentCompletionObjectType } type CourseType { @@ -96,7 +96,7 @@ enum CoreUserLanguageChoices { IT } -type AssignmentType implements CoursePageInterface { +type AssignmentObjectType implements CoursePageInterface { assignment_type: AssignmentAssignmentAssignmentTypeChoices! """Erläuterung der Ausgangslage""" @@ -136,7 +136,7 @@ enum AssignmentAssignmentAssignmentTypeChoices { scalar JSONStreamField -type AssignmentCompletionType { +type AssignmentCompletionObjectType { id: ID! created_at: DateTime! updated_at: DateTime! @@ -146,7 +146,7 @@ type AssignmentCompletionType { evaluation_grade: Float evaluation_points: Float assignment_user: UserType! - assignment: AssignmentType! + assignment: AssignmentObjectType! completion_status: AssignmentAssignmentCompletionCompletionStatusChoices! completion_data: GenericScalar additional_json_data: JSONString! @@ -221,7 +221,7 @@ input SendFeedbackInput { } type AssignmentCompletionMutation { - assignment_completion: AssignmentCompletionType + assignment_completion: AssignmentCompletionObjectType } """An enumeration.""" diff --git a/server/vbv_lernwelt/assignment/graphql/mutations.py b/server/vbv_lernwelt/assignment/graphql/mutations.py index 632c04c5..edb4145b 100644 --- a/server/vbv_lernwelt/assignment/graphql/mutations.py +++ b/server/vbv_lernwelt/assignment/graphql/mutations.py @@ -4,7 +4,7 @@ import graphene import structlog from rest_framework.exceptions import PermissionDenied -from vbv_lernwelt.assignment.graphql.types import AssignmentCompletionType +from vbv_lernwelt.assignment.graphql.types import AssignmentCompletionObjectType from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus from vbv_lernwelt.assignment.services import update_assignment_completion from vbv_lernwelt.core.models import User @@ -15,7 +15,7 @@ logger = structlog.get_logger(__name__) class AssignmentCompletionMutation(graphene.Mutation): - assignment_completion = graphene.Field(AssignmentCompletionType) + assignment_completion = graphene.Field(AssignmentCompletionObjectType) class Arguments: assignment_id = graphene.ID(required=True) diff --git a/server/vbv_lernwelt/assignment/graphql/queries.py b/server/vbv_lernwelt/assignment/graphql/queries.py index f7847844..cb68a509 100644 --- a/server/vbv_lernwelt/assignment/graphql/queries.py +++ b/server/vbv_lernwelt/assignment/graphql/queries.py @@ -2,8 +2,8 @@ import graphene from rest_framework.exceptions import PermissionDenied from vbv_lernwelt.assignment.graphql.types import ( - AssignmentCompletionType, - AssignmentType, + AssignmentCompletionObjectType, + AssignmentObjectType, ) from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletion from vbv_lernwelt.course.graphql.types import resolve_course_page @@ -13,14 +13,14 @@ from vbv_lernwelt.course.permissions import has_course_access, is_course_session class AssignmentQuery(object): assignment = graphene.Field( - AssignmentType, id=graphene.ID(), slug=graphene.String() + AssignmentObjectType, id=graphene.ID(), slug=graphene.String() ) def resolve_assignment(root, info, id=None, slug=None): return resolve_course_page(Assignment, root, info, id=id, slug=slug) assignment_completion = graphene.Field( - AssignmentCompletionType, + AssignmentCompletionObjectType, assignment_id=graphene.ID(required=True), course_session_id=graphene.ID(required=True), assignment_user_id=graphene.ID(required=False), diff --git a/server/vbv_lernwelt/assignment/graphql/types.py b/server/vbv_lernwelt/assignment/graphql/types.py index 9d33971b..4c50016b 100644 --- a/server/vbv_lernwelt/assignment/graphql/types.py +++ b/server/vbv_lernwelt/assignment/graphql/types.py @@ -6,7 +6,7 @@ from vbv_lernwelt.core.graphql.types import JSONStreamField from vbv_lernwelt.course.schema import CoursePageInterface -class AssignmentType(DjangoObjectType): +class AssignmentObjectType(DjangoObjectType): tasks = JSONStreamField() evaluation_tasks = JSONStreamField() performance_objectives = JSONStreamField() @@ -23,7 +23,7 @@ class AssignmentType(DjangoObjectType): ) -class AssignmentCompletionType(DjangoObjectType): +class AssignmentCompletionObjectType(DjangoObjectType): completion_data = GenericScalar() class Meta: From a692c819eeb2f7a6d4a300a45423ed43ef635662 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 30 Jun 2023 11:33:21 +0200 Subject: [PATCH 4/4] Add generated types so that they can be referenced in code --- client/codegen.ts | 3 +++ client/codegenGenerateTypenames.js | 11 ++++++++ client/src/gql/typenames.ts | 27 +++++++++++++++++++ client/src/graphql/client.ts | 10 ++++--- .../course/creators/test_course.py | 4 +-- 5 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 client/codegenGenerateTypenames.js create mode 100644 client/src/gql/typenames.ts diff --git a/client/codegen.ts b/client/codegen.ts index 117de583..4956f075 100644 --- a/client/codegen.ts +++ b/client/codegen.ts @@ -15,6 +15,9 @@ const config: CodegenConfig = { }, plugins: [], }, + "./src/gql/typenames.ts": { + plugins: ["./codegenGenerateTypenames.js"], + }, }, config: { enumsAsTypes: true, diff --git a/client/codegenGenerateTypenames.js b/client/codegenGenerateTypenames.js new file mode 100644 index 00000000..984e1e5b --- /dev/null +++ b/client/codegenGenerateTypenames.js @@ -0,0 +1,11 @@ +// eslint-disable-next-line no-undef +module.exports = { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + plugin: (schema, _documents, _config) => { + const typeNames = Object.keys(schema.getTypeMap()) + .filter((name) => !name.startsWith("__")) // Filter out built-in types + .map((name) => `export const ${name} = "${name}";\n`); + + return typeNames.join(""); + }, +}; diff --git a/client/src/gql/typenames.ts b/client/src/gql/typenames.ts new file mode 100644 index 00000000..00c8620f --- /dev/null +++ b/client/src/gql/typenames.ts @@ -0,0 +1,27 @@ +export const AssignmentAssignmentAssignmentTypeChoices = "AssignmentAssignmentAssignmentTypeChoices"; +export const AssignmentAssignmentCompletionCompletionStatusChoices = "AssignmentAssignmentCompletionCompletionStatusChoices"; +export const AssignmentCompletionMutation = "AssignmentCompletionMutation"; +export const AssignmentCompletionObjectType = "AssignmentCompletionObjectType"; +export const AssignmentCompletionStatus = "AssignmentCompletionStatus"; +export const AssignmentObjectType = "AssignmentObjectType"; +export const Boolean = "Boolean"; +export const CoreUserLanguageChoices = "CoreUserLanguageChoices"; +export const CoursePageInterface = "CoursePageInterface"; +export const CourseType = "CourseType"; +export const DateTime = "DateTime"; +export const ErrorType = "ErrorType"; +export const FeedbackResponse = "FeedbackResponse"; +export const Float = "Float"; +export const GenericScalar = "GenericScalar"; +export const ID = "ID"; +export const Int = "Int"; +export const JSONStreamField = "JSONStreamField"; +export const JSONString = "JSONString"; +export const LearningPathType = "LearningPathType"; +export const Mutation = "Mutation"; +export const Node = "Node"; +export const Query = "Query"; +export const SendFeedbackInput = "SendFeedbackInput"; +export const SendFeedbackPayload = "SendFeedbackPayload"; +export const String = "String"; +export const UserType = "UserType"; diff --git a/client/src/graphql/client.ts b/client/src/graphql/client.ts index da7eebd8..0779344f 100644 --- a/client/src/graphql/client.ts +++ b/client/src/graphql/client.ts @@ -5,6 +5,10 @@ import schema from "../gql/minifiedSchema.json"; import { devtoolsExchange } from "@urql/devtools"; import { cacheExchange } from "@urql/exchange-graphcache"; import { Client, fetchExchange } from "@urql/vue"; +import { + AssignmentCompletionMutation, + AssignmentCompletionObjectType, +} from "../gql/typenames"; export const graphqlClient = new Client({ url: import.meta.env.VITE_GRAPHQL_URL || "/server/graphql/", @@ -28,13 +32,13 @@ export const graphqlClient = new Client({ const id = info.variables.id; if (id) { const completionData = cache.resolve( - { __typename: "AssignmentCompletionType", id: "1" }, + { __typename: AssignmentCompletionObjectType, id: "1" }, "completion_data" ); return { - __typename: "AssignmentCompletionMutation", + __typename: AssignmentCompletionMutation, assignment_completion: { - __typename: "AssignmentCompletionType", + __typename: AssignmentCompletionObjectType, id: id, completion_data: Object.assign( {}, diff --git a/server/vbv_lernwelt/course/creators/test_course.py b/server/vbv_lernwelt/course/creators/test_course.py index e6681709..aaa0d743 100644 --- a/server/vbv_lernwelt/course/creators/test_course.py +++ b/server/vbv_lernwelt/course/creators/test_course.py @@ -10,7 +10,7 @@ from vbv_lernwelt.assignment.creators.create_assignments import ( create_uk_fahrzeug_casework, create_uk_fahrzeug_prep_assignment, ) -from vbv_lernwelt.assignment.models import Assignment +from vbv_lernwelt.assignment.models import Assignment, AssignmentCompletionStatus from vbv_lernwelt.assignment.services import update_assignment_completion from vbv_lernwelt.assignment.tests.assignment_factories import AssignmentListPageFactory from vbv_lernwelt.competence.factories import ( @@ -139,7 +139,7 @@ def create_test_assignment_submitted_data(assignment, course_session, user): assignment_user=user, assignment=assignment, course_session=course_session, - completion_status="SUBMITTED", + completion_status=AssignmentCompletionStatus.SUBMITTED, )