from datetime import date from django.test import TestCase from django.utils import timezone from rest_framework import serializers 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 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 from vbv_lernwelt.learnpath.models import LearningContentAssignment class UpdateAssignmentCompletionTestCase(TestCase): def setUp(self): create_default_users() self.course = create_test_course(include_vv=False) self.assignment = ( self.course.coursepage.get_descendants() .exact_type(Assignment) .filter(assignment__assignment_type="MANDATORY_CASEWORK") .first() .specific ) self.course_session = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Bern 2022 a", ) self.user = User.objects.get(username="student") self.trainer = User.objects.get(username="admin") def test_can_store_new_user_input(self): subtasks = self.assignment.filter_user_subtasks() user_text_input = find_first( subtasks, pred=lambda x: x["type"] == "user_text_input" ) user_confirmation = find_first( subtasks, pred=lambda x: x["type"] == "user_confirmation" ) update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={ user_text_input["id"]: { "user_data": {"text": "Viel habe ich nicht zu sagen..."} }, user_confirmation["id"]: {"user_data": {"confirmation": True}}, }, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) self.assertDictEqual( ac.completion_data, { user_text_input["id"]: { "user_data": {"text": "Viel habe ich nicht zu sagen..."} }, user_confirmation["id"]: {"user_data": {"confirmation": True}}, }, ) def test_can_update_user_input(self): subtasks = self.assignment.filter_user_subtasks( subtask_types=["user_text_input"] ) user_text_input0 = subtasks[0] user_text_input1 = subtasks[1] ac = AssignmentCompletion.objects.create( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={ user_text_input0["id"]: { "user_data": {"text": "Am Anfang war das Wort... 0"} }, user_text_input1["id"]: { "user_data": {"text": "Am Anfang war das Wort... 1"} }, }, ) update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={ user_text_input1["id"]: { "user_data": {"text": "Viel mehr gibt es nicht zu sagen."} } }, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) user_input0 = ac.completion_data[user_text_input0["id"]]["user_data"]["text"] user_input1 = ac.completion_data[user_text_input1["id"]]["user_data"]["text"] self.assertEqual(user_input0, "Am Anfang war das Wort... 0") self.assertEqual(user_input1, "Viel mehr gibt es nicht zu sagen.") def test_will_not_store_unknown_user_tasks(self): random_uuid = "7b60903b-d2a5-4798-b6cd-5b51e63e98ab" update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={ random_uuid: { "user_data": {"text": "Viel mehr gibt es nicht zu sagen."} } }, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) self.assertEqual(ac.completion_data, {}) def test_completion_status_submitted(self): subtasks = self.assignment.filter_user_subtasks( subtask_types=["user_text_input"] ) user_text_input0 = subtasks[0] user_text_input1 = subtasks[1] ac = AssignmentCompletion.objects.create( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={ user_text_input0["id"]: { "user_data": {"text": "Am Anfang war das Wort... 0"} }, user_text_input1["id"]: { "user_data": {"text": "Am Anfang war das Wort... 1"} }, }, ) update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.SUBMITTED, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) self.assertEqual(ac.completion_status, "SUBMITTED") self.assertEqual(ac.submitted_at.date(), date.today()) # will create AssignmentCompletionAuditLog entry acl = AssignmentCompletionAuditLog.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_status="SUBMITTED", ) self.assertEqual(acl.created_at.date(), date.today()) self.assertEqual(acl.assignment_user_email, "student") self.assertEqual( acl.assignment_slug, "test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice", ) # AssignmentCompletionAuditLog entry will remain event after deletion of foreign keys ac.delete() self.user.delete() self.course.coursepage.get_descendants().exact_type( LearningContentAssignment ).delete() self.assignment.delete() acl = AssignmentCompletionAuditLog.objects.get(id=acl.id) self.assertEqual(acl.created_at.date(), date.today()) self.assertEqual(acl.assignment_user_email, "student") self.assertEqual( acl.assignment_slug, "test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice", ) self.assertIsNone(acl.assignment_user) self.assertIsNone(acl.assignment) def test_completion_status_submitted_cannot_submit_twice(self): subtasks = self.assignment.filter_user_subtasks( subtask_types=["user_text_input"] ) user_text_input0 = subtasks[0] user_text_input1 = subtasks[1] ac = AssignmentCompletion.objects.create( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, submitted_at=timezone.now(), completion_status=AssignmentCompletionStatus.SUBMITTED.value, completion_data={ user_text_input0["id"]: { "user_data": {"text": "Am Anfang war das Wort... 0"} }, user_text_input1["id"]: { "user_data": {"text": "Am Anfang war das Wort... 1"} }, }, ) with self.assertRaises(serializers.ValidationError) as error: update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.SUBMITTED, ) # can submit twice with flag update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.SUBMITTED, validate_completion_status_change=False, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) self.assertEqual(ac.completion_status, "SUBMITTED") self.assertEqual(ac.submitted_at.date(), date.today()) def test_copy_task_data(self): 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.user, assignment=self.assignment, course_session=self.course_session, completion_data={ user_text_input["id"]: { "user_data": {"text": "Ich würde nichts weiteres empfehlen."} }, }, ) update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.SUBMITTED, copy_task_data=True, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) self.assertEqual(ac.completion_status, "SUBMITTED") user_input = ac.completion_data[user_text_input["id"]] self.assertEqual( user_input["user_data"]["text"], "Ich würde nichts weiteres empfehlen." ) self.assertTrue( user_input["value"]["text"].startswith( "Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest?" ) ) def test_can_evaluate_with_evaluation_tasks(self): ac = AssignmentCompletion.objects.create( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.SUBMITTED, ) evaluation_task = self.assignment.get_evaluation_tasks()[0] update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={ evaluation_task["id"]: { "expert_data": {"points": 2, "text": "Gut gemacht!"} }, }, completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, evaluation_user=self.trainer, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) self.assertEqual(ac.completion_status, "EVALUATION_IN_PROGRESS") trainer_input = ac.completion_data[evaluation_task["id"]] self.assertDictEqual( trainer_input["expert_data"], {"points": 2, "text": "Gut gemacht!"} ) def test_can_add_evaluation_data_without_loosing_user_input_data(self): 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.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.SUBMITTED, completion_data={ user_text_input["id"]: { "user_data": {"text": "Ich würde nichts weiteres empfehlen."} }, }, ) update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={ user_text_input["id"]: { "expert_data": {"points": 1, "comment": "Gut gemacht!"} }, }, completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, evaluation_user=self.trainer, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) self.assertEqual(ac.completion_status, "EVALUATION_IN_PROGRESS") user_input = ac.completion_data[user_text_input["id"]] self.assertDictEqual( user_input["expert_data"], {"points": 1, "comment": "Gut gemacht!"} ) self.assertEqual( user_input["user_data"]["text"], "Ich würde nichts weiteres empfehlen." ) def test_cannot_evaluate_data_without_evaluation_user(self): 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.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.IN_PROGRESS.value, completion_data={ user_text_input["id"]: { "user_data": {"text": "Ich würde nichts weiteres empfehlen."} }, }, ) with self.assertRaises(serializers.ValidationError) as error: update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, completion_data={ user_text_input["id"]: { "expert_data": {"points": 1, "comment": "Gut gemacht!"} }, }, ) self.assertTrue( "evaluation_user" in error.exception.detail, ) def test_can_submit_evaluation(self): 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.user, assignment=self.assignment, course_session=self.course_session, completion_status=AssignmentCompletionStatus.SUBMITTED.value, completion_data={ user_text_input["id"]: { "user_data": {"text": "Ich würde nichts weiteres empfehlen."} }, }, ) evaluation_task = self.assignment.get_evaluation_tasks()[0] update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={ evaluation_task["id"]: { "expert_data": {"points": 2, "text": "Gut gemacht!"} }, }, completion_status=AssignmentCompletionStatus.EVALUATION_IN_PROGRESS, evaluation_user=self.trainer, ) with self.assertRaises(serializers.ValidationError) as error: # not setting grade will raise an error update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={}, completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED, evaluation_user=self.trainer, evaluation_points=None, ) update_assignment_completion( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_data={}, completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED, evaluation_user=self.trainer, evaluation_points=16, ) ac = AssignmentCompletion.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, ) self.assertEqual(ac.completion_status, "EVALUATION_SUBMITTED") self.assertEqual(ac.evaluation_points, 16) self.assertEqual(ac.evaluation_max_points, 24) self.assertTrue(ac.evaluation_passed) trainer_input = ac.completion_data[evaluation_task["id"]] self.assertDictEqual( trainer_input["expert_data"], {"points": 2, "text": "Gut gemacht!"} ) user_input = ac.completion_data[user_text_input["id"]] self.assertDictEqual( user_input["user_data"], {"text": "Ich würde nichts weiteres empfehlen."} ) # will create AssignmentCompletionAuditLog entry acl = AssignmentCompletionAuditLog.objects.get( assignment_user=self.user, assignment=self.assignment, course_session=self.course_session, completion_status="EVALUATION_SUBMITTED", ) self.assertEqual(acl.created_at.date(), date.today()) self.assertEqual(acl.assignment_user_email, "student") self.assertEqual( acl.assignment_slug, "test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice", ) trainer_input = acl.completion_data[evaluation_task["id"]] self.assertDictEqual( trainer_input["expert_data"], {"points": 2, "text": "Gut gemacht!"} ) user_input = acl.completion_data[user_text_input["id"]] self.assertDictEqual( user_input["user_data"], {"text": "Ich würde nichts weiteres empfehlen."} ) self.assertEqual(acl.evaluation_points, 16) self.assertEqual(acl.evaluation_max_points, 24) self.assertTrue(acl.evaluation_passed) # AssignmentCompletionAuditLog entry will remain event after deletion of foreign keys ac.delete() self.user.delete() self.course.coursepage.get_descendants().exact_type( LearningContentAssignment ).delete() self.assignment.delete() acl = AssignmentCompletionAuditLog.objects.get(id=acl.id) self.assertEqual(acl.created_at.date(), date.today()) self.assertEqual(acl.assignment_user_email, "student") self.assertEqual( acl.assignment_slug, "test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice", ) self.assertIsNone(acl.assignment_user) self.assertIsNone(acl.assignment)