vbv/server/vbv_lernwelt/assignment/tests/test_services.py

556 lines
20 KiB
Python

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="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]
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,
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, "SUBMITTED")
self.assertEqual(ac.submitted_at.date(), date.today())
self.assertEqual(ac.evaluation_user, self.trainer)
# 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):
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?"
),
)
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):
# 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)