import json from datetime import date from django.core.files.uploadedfile import SimpleUploadedFile 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 from vbv_lernwelt.files.models import UploadFile from vbv_lernwelt.notify.models import Notification 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.assignment_task_ids = [t.id for t in self.assignment.tasks] # 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" ) task_id = self.assignment_task_ids[0] # Create file uploaded_file = SimpleUploadedFile("file.txt", b"file_content") file = UploadFile( original_file_name="file.txt", file_name="file.txt", file_type="text/plain", uploaded_by=self.student, file=uploaded_file, ) file.full_clean() file.save() file_id = str(file.id) completion_data_string = json.dumps( { user_text_input["id"]: {"user_data": {"text": "Hallo via API"}}, task_id: {"user_data": {"fileId": file_id}}, } ).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 task_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"}}, task_id: {"user_data": {"fileId": file_id}}, }, ) task_data = data["task_completion_data"][task_id] self.maxDiff = None self.assertEqual(task_data["user_data"]["fileId"], file_id) self.assertEqual(task_data["user_data"]["fileInfo"]["id"], file_id) self.assertEqual(task_data["user_data"]["fileInfo"]["name"], "file.txt") self.assertTrue( task_data["user_data"]["fileInfo"]["url"].startswith( "https://s3.eu-central-1.amazonaws.com/myvbv-dev.iterativ.ch" ) ) # 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"}}, task_id: {"user_data": {"fileId": file_id}}, }, ) # 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"}}, task_id: {"user_data": {"fileId": file_id}}, }, ) # 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"}}, task_id: {"user_data": {"fileId": file_id}}, }, ) # check notification self.assertEqual(Notification.objects.count(), 1) notification = Notification.objects.first() self.assertEqual( "Test Student1 hat die geleitete Fallarbeit «Überprüfen einer Motorfahrzeugs-Versicherungspolice» abgegeben.", notification.verb, ) self.assertEqual( "test-trainer1@example.com", notification.recipient.email, ) self.assertEqual( "test-student1@example.com", notification.actor.email, ) self.assertEqual( "USER_INTERACTION", notification.notification_category, ) self.assertEqual( "CASEWORK_SUBMITTED", notification.notification_trigger, ) self.assertEqual( notification.action_object, db_entry, ) self.assertEqual( notification.course_session, self.course_session, ) self.assertTrue( "/course/test-lehrgang/assignment-evaluation" in notification.target_url ) # 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?" ), ) AssignmentCompletion.objects.create( assignment_user=self.student, assignment=self.assignment, learning_content_page=self.assignment.find_attached_learning_content(), 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 }} }} }} }} """ print(query) 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_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_points, 16) self.assertEqual(db_entry.evaluation_max_points, 24) self.assertTrue(db_entry.evaluation_passed) 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!"}, }, }, ) # check notification self.assertEqual(Notification.objects.count(), 1) notification = Notification.objects.first() self.assertEqual( "Test Trainer1 hat die geleitete Fallarbeit «Überprüfen einer Motorfahrzeugs-Versicherungspolice» bewertet.", notification.verb, ) self.assertEqual( "test-student1@example.com", notification.recipient.email, ) self.assertEqual( "test-trainer1@example.com", notification.actor.email, ) self.assertEqual( "USER_INTERACTION", notification.notification_category, ) self.assertEqual( "CASEWORK_EVALUATED", notification.notification_trigger, ) self.assertEqual( notification.action_object, db_entry, ) self.assertEqual( notification.course_session, self.course_session, ) self.assertEqual( notification.target_url, "/course/test-lehrgang/learn/fahrzeug/überprüfen-einer-motorfahrzeug-versicherungspolice", ) # `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!"}, }, }, ) self.assertEqual(acl.evaluation_points, 16) self.assertEqual(acl.evaluation_max_points, 24) self.assertTrue(acl.evaluation_passed) def test_student_can_upsert_reflection(self): """ when upserting a reflection, it will store also the page_id of the learning content """ reflection = Assignment.objects.get(slug="test-lehrgang-assignment-reflexion") reflection_subtasks = reflection.filter_user_subtasks() reflection_learning_content = reflection.find_attached_learning_content() self.client.force_login(self.student) user_text_input = find_first( reflection_subtasks, pred=lambda x: x["type"] == "user_text_input" ) completion_data_string = json.dumps( { user_text_input["id"]: { "user_data": {"text": "Hallo via Reflection API"} }, } ).replace('"', '\\"') query = f""" mutation {{ upsert_assignment_completion( assignment_id: {reflection.id} course_session_id: {self.course_session.id} learning_content_page_id: {reflection_learning_content.id} completion_status: IN_PROGRESS completion_data_string: "{completion_data_string}" ) {{ assignment_completion {{ id completion_status completion_data assignment_user {{ id }} assignment {{ id }} learning_content_page_id }} }} }} """ response = self.query(query) self.assertResponseNoErrors(response) data = json.loads(response.content)["data"]["upsert_assignment_completion"][ "assignment_completion" ] print(data) self.assertEqual(data["assignment_user"]["id"], str(self.student.id)) self.assertEqual(data["assignment"]["id"], str(reflection.id)) self.assertEqual(data["completion_status"], "IN_PROGRESS") self.assertEqual( data["learning_content_page_id"], str(reflection_learning_content.id) ) self.assertDictEqual( data["completion_data"], { user_text_input["id"]: { "user_data": {"text": "Hallo via Reflection API"} }, }, ) # check DB data db_entry = AssignmentCompletion.objects.get( assignment_user=self.student, course_session_id=self.course_session.id, assignment_id=reflection.id, learning_content_page_id=reflection_learning_content.id, ) self.assertEqual(db_entry.completion_status, "IN_PROGRESS") self.assertDictEqual( db_entry.completion_data, { user_text_input["id"]: { "user_data": {"text": "Hallo via Reflection API"} }, }, )