vbv/server/vbv_lernwelt/dashboard/tests/graphql/test_assignment.py

479 lines
16 KiB
Python

from datetime import datetime
from typing import Tuple
from graphene_django.utils import GraphQLTestCase
from vbv_lernwelt.assignment.models import Assignment, AssignmentType
from vbv_lernwelt.course.creators.test_utils import (
add_course_session_group_supervisor,
add_course_session_user,
create_assignment,
create_assignment_completion,
create_assignment_learning_content,
create_circle,
create_competence_certificate,
create_course,
create_course_session,
create_course_session_assignment,
create_course_session_edoniq_test,
create_course_session_group,
create_user,
)
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
from vbv_lernwelt.course_session.models import (
CourseSessionAssignment,
CourseSessionEdoniqTest,
)
from vbv_lernwelt.learnpath.models import Circle
class AssignmentTestCase(GraphQLTestCase):
GRAPHQL_URL = "/server/graphql/"
GRAPHQL_QUERY = """query($course_id: ID!) {
course_statistics(course_id: $course_id) {
assignments{
summary{
completed_count
average_passed
}
records{
course_session_id
course_session_assignment_id
circle_id
generation
assignment_title
assignment_type_translation_key
details_url
deadline
metrics {
passed_count
failed_count
unranked_count
ranking_completed
average_passed
}
}
}
}
}"""
def setUp(self):
self.course, self.course_page = create_course("Test Course")
self.course_session = create_course_session(course=self.course, title=":)")
self.circle, _ = create_circle(title="Circle", course_page=self.course_page)
self.supervisor = create_user("supervisor")
group = create_course_session_group(course_session=self.course_session)
add_course_session_group_supervisor(group=group, user=self.supervisor)
self.m1 = create_user("member_1")
add_course_session_user(
course_session=self.course_session,
user=self.m1,
role=CourseSessionUser.Role.MEMBER,
)
self.m2 = create_user("member_2")
add_course_session_user(
course_session=self.course_session,
user=self.m2,
role=CourseSessionUser.Role.MEMBER,
)
self.m3 = create_user("member_3")
add_course_session_user(
course_session=self.course_session,
user=self.m3,
role=CourseSessionUser.Role.MEMBER,
)
self.e1 = create_user("expert_1")
add_course_session_user(
course_session=self.course_session,
user=self.e1,
role=CourseSessionUser.Role.EXPERT,
)
self.client.force_login(self.supervisor)
def test_dashboard_contains_casework(self):
self._test_assignment_type_dashboard_details(
assignment_type=AssignmentType.CASEWORK
)
def test_dashboard_contains_edoniq_tests(self):
self._test_assignment_type_dashboard_details(
assignment_type=AssignmentType.EDONIQ_TEST
)
def test_dashboard_not_contains_unsupported_types(self):
"""
Since everything is mixed in the same table, we need to make sure
that the dashboard only contains the supported types does not
get confused by the unsupported ones.
"""
irrelevant_types_for_dashboard = set(AssignmentType) - {
AssignmentType.CASEWORK,
AssignmentType.EDONIQ_TEST,
}
for assignment_type in irrelevant_types_for_dashboard:
self._test_assignment_type_not_in_dashboard(assignment_type=assignment_type)
def _test_assignment_type_dashboard_details(self, assignment_type: AssignmentType):
# GIVEN
assignment, csa = mix_assignment_cocktail(
assignment_type=assignment_type,
deadline_at=datetime(2000, 4, 1),
course_session=self.course_session,
circle=self.circle,
add_competence_certificate=True,
)
create_assignment_completion(
user=self.m1,
assignment=assignment,
course_session=self.course_session,
)
# WHEN
response = self.query(
self.GRAPHQL_QUERY, variables={"course_id": str(self.course.id)}
)
# THEN
self.assertResponseNoErrors(response)
dashboard = response.json()["data"]["course_statistics"]
records = dashboard["assignments"]["records"]
self.assertEqual(len(records), 1)
record = records[0]
if isinstance(csa, CourseSessionAssignment):
due_date = csa.submission_deadline
else:
due_date = csa.deadline
self.assertEqual(record["course_session_id"], str(self.course_session.id))
self.assertEqual(record["course_session_assignment_id"], str(csa.id))
self.assertEqual(record["generation"], str(self.course_session.generation))
self.assertEqual(record["circle_id"], str(self.circle.id))
self.assertEqual(record["details_url"], due_date.url_expert)
self.assertEqual(datetime.fromisoformat(record["deadline"]), due_date.start)
self.assertEqual(
record["assignment_title"],
csa.learning_content.content_assignment.title,
)
self.assertEqual(
record["assignment_type_translation_key"],
due_date.assignment_type_translation_key,
)
def _test_assignment_type_not_in_dashboard(self, assignment_type: AssignmentType):
_, csa = mix_assignment_cocktail(
assignment_type=assignment_type,
course_session=self.course_session,
circle=self.circle,
)
# WHEN
response = self.query(
self.GRAPHQL_QUERY, variables={"course_id": str(self.course.id)}
)
# THEN
self.assertResponseNoErrors(response)
dashboard = response.json()["data"]["course_statistics"]
records = dashboard["assignments"]["records"]
self.assertEqual(len(records), 0)
def test_metrics_summary(self):
# GIVEN
assignment_1, _ = mix_assignment_cocktail(
deadline_at=datetime(1990, 4, 1),
assignment_type=AssignmentType.CASEWORK,
course_session=self.course_session,
circle=self.circle,
add_competence_certificate=True,
)
assignment_2, _ = mix_assignment_cocktail(
deadline_at=datetime(2000, 4, 1),
assignment_type=AssignmentType.EDONIQ_TEST,
course_session=self.course_session,
circle=self.circle,
add_competence_certificate=True,
)
assignment_3, _ = mix_assignment_cocktail(
deadline_at=datetime(2010, 4, 1),
assignment_type=AssignmentType.CASEWORK,
course_session=self.course_session,
circle=self.circle,
add_competence_certificate=True,
)
# no completions for this assignment yet
assignment_4, _ = mix_assignment_cocktail(
deadline_at=datetime(2020, 4, 1),
assignment_type=AssignmentType.EDONIQ_TEST,
course_session=self.course_session,
circle=self.circle,
add_competence_certificate=False,
)
# assignment 1
assigment_1_results = [
(self.m1, True), # passed
(self.m2, False), # failed
(self.m3, None), # unranked
]
for user, has_passed in assigment_1_results:
if has_passed is None:
continue
create_assignment_completion(
user=user,
assignment=assignment_1,
course_session=self.course_session,
has_passed=has_passed,
)
# assignment 2
assignment_2_results = [
(self.m1, True), # passed
(self.m2, True), # passed
(self.m3, False), # failed
]
for user, has_passed in assignment_2_results:
create_assignment_completion(
user=user,
assignment=assignment_2,
course_session=self.course_session,
has_passed=has_passed,
)
# assignment 3
assignment_3_results = [
(self.m1, True), # passed
(self.m2, True), # passed
(self.m3, True), # passed
]
for user, has_passed in assignment_3_results:
create_assignment_completion(
user=user,
assignment=assignment_3,
course_session=self.course_session,
has_passed=has_passed,
)
# WHEN
response = self.query(
self.GRAPHQL_QUERY, variables={"course_id": str(self.course.id)}
)
# THEN
self.assertResponseNoErrors(response)
dashboard = response.json()["data"]["course_statistics"]
# 1 -> incomplete (not counted for average)
# 2 -> complete 66% passed ...
# 3 -> complete 100% passed --> 67%
# 4 -> not included in certificate
summary = dashboard["assignments"]["summary"]
self.assertEqual(summary["completed_count"], 3)
self.assertEqual(summary["average_passed"], 67.0)
records = dashboard["assignments"]["records"]
self.assertEqual(len(records), 3)
# 1 -> assigment_1_results (oldest)
assignment_1_metrics = records[0]["metrics"]
self.assertEqual(assignment_1_metrics["passed_count"], 1)
self.assertEqual(assignment_1_metrics["failed_count"], 1)
self.assertEqual(assignment_1_metrics["unranked_count"], 1)
self.assertEqual(assignment_1_metrics["ranking_completed"], True)
self.assertEqual(assignment_1_metrics["average_passed"], 34)
# 2 -> assignment_2_results
assignment_2_metrics = records[1]["metrics"]
self.assertEqual(assignment_2_metrics["passed_count"], 2)
self.assertEqual(assignment_2_metrics["failed_count"], 1)
self.assertEqual(assignment_2_metrics["unranked_count"], 0)
self.assertEqual(assignment_2_metrics["ranking_completed"], True)
self.assertEqual(assignment_2_metrics["average_passed"], 67)
# 3 -> assignment_3_results
assignment_3_metrics = records[2]["metrics"]
self.assertEqual(assignment_3_metrics["passed_count"], 3)
self.assertEqual(assignment_3_metrics["failed_count"], 0)
self.assertEqual(assignment_3_metrics["unranked_count"], 0)
self.assertEqual(assignment_3_metrics["ranking_completed"], True)
self.assertEqual(assignment_3_metrics["average_passed"], 100)
class ExpertAssignmentTestCase(AssignmentTestCase):
def setUp(self):
self.course, self.course_page = create_course("Test Course")
self.course_session = create_course_session(course=self.course, title=":)")
course_session = create_course_session(course=self.course, title=":(")
self.circle, _ = create_circle(title="Circle", course_page=self.course_page)
circle, _ = create_circle(title="Other Circle", course_page=self.course_page)
create_course_session_group(course_session=self.course_session)
self.m1 = create_user("member_1")
add_course_session_user(
course_session=self.course_session,
user=self.m1,
role=CourseSessionUser.Role.MEMBER,
)
self.m2 = create_user("member_2")
add_course_session_user(
course_session=self.course_session,
user=self.m2,
role=CourseSessionUser.Role.MEMBER,
)
self.m3 = create_user("member_3")
add_course_session_user(
course_session=self.course_session,
user=self.m3,
role=CourseSessionUser.Role.MEMBER,
)
self.e1 = create_user("expert_1")
csu = add_course_session_user(
course_session=self.course_session,
user=self.e1,
role=CourseSessionUser.Role.EXPERT,
)
csu.expert.add(self.circle)
m4 = create_user("member_4")
add_course_session_user(
course_session=course_session,
user=m4,
role=CourseSessionUser.Role.MEMBER,
)
m5 = create_user("member_5")
add_course_session_user(
course_session=course_session,
user=m5,
role=CourseSessionUser.Role.MEMBER,
)
# assignments in the other course session should not be included
assignment_1, _ = mix_assignment_cocktail(
deadline_at=datetime(1990, 4, 1),
assignment_type=AssignmentType.CASEWORK,
course_session=course_session,
circle=self.circle,
add_competence_certificate=True,
)
assignment_2, _ = mix_assignment_cocktail(
deadline_at=datetime(2000, 4, 1),
assignment_type=AssignmentType.EDONIQ_TEST,
course_session=course_session,
circle=self.circle,
add_competence_certificate=True,
)
create_assignment_completion(
user=m4,
assignment=assignment_1,
course_session=course_session,
has_passed=True,
)
create_assignment_completion(
user=m5,
assignment=assignment_2,
course_session=course_session,
)
# assignments in the same course session, but in a different circle. Should not be included
assignment_3, _ = mix_assignment_cocktail(
deadline_at=datetime(1990, 4, 1),
assignment_type=AssignmentType.CASEWORK,
course_session=self.course_session,
circle=circle,
add_competence_certificate=True,
)
assignment_4, _ = mix_assignment_cocktail(
deadline_at=datetime(2000, 4, 1),
assignment_type=AssignmentType.EDONIQ_TEST,
course_session=self.course_session,
circle=circle,
add_competence_certificate=True,
)
create_assignment_completion(
user=self.m1,
assignment=assignment_1,
course_session=self.course_session,
has_passed=True,
)
create_assignment_completion(
user=self.m1,
assignment=assignment_2,
course_session=self.course_session,
)
self.client.force_login(self.e1)
def mix_assignment_cocktail(
assignment_type: AssignmentType,
course_session: CourseSession,
circle: Circle,
add_competence_certificate: bool = False,
deadline_at: datetime | None = None,
) -> Tuple[Assignment, CourseSessionAssignment | CourseSessionEdoniqTest]:
"""
Little test helper to create a course session assignment or edoniq test based
on the given assignment type.
"""
assignment = create_assignment(
course=course_session.course,
assignment_type=assignment_type,
)
if add_competence_certificate:
certificate = create_competence_certificate(course=course_session.course)
assignment.competence_certificate = certificate
assignment.save()
if assignment_type == AssignmentType.EDONIQ_TEST:
cset = create_course_session_edoniq_test(
deadline_at=deadline_at,
course_session=course_session,
learning_content_edoniq_test=create_assignment_learning_content(
circle=circle,
assignment=assignment,
),
)
return assignment, cset
else:
csa = create_course_session_assignment(
deadline_at=deadline_at,
course_session=course_session,
learning_content_assignment=create_assignment_learning_content(
circle=circle,
assignment=assignment,
),
)
return assignment, csa