diff --git a/server/vbv_lernwelt/dashboard/graphql/queries.py b/server/vbv_lernwelt/dashboard/graphql/queries.py index 1aa58790..11b59c25 100644 --- a/server/vbv_lernwelt/dashboard/graphql/queries.py +++ b/server/vbv_lernwelt/dashboard/graphql/queries.py @@ -2,14 +2,16 @@ from typing import Dict, List, Set, Tuple import graphene +from vbv_lernwelt.assignment.models import AssignmentCompletion, AssignmentCompletionStatus from vbv_lernwelt.core.admin import User from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser from vbv_lernwelt.course_session_group.models import CourseSessionGroup +from vbv_lernwelt.dashboard.graphql.types.competence import competences from vbv_lernwelt.dashboard.graphql.types.dashboard import ( CourseProgressType, CourseStatisticsType, DashboardConfigType, - DashboardType, + DashboardType, ProgressDashboardCompetenceType, ProgressDashboardAssignmentType, ) from vbv_lernwelt.iam.permissions import ( can_view_course_session, @@ -87,12 +89,17 @@ class DashboardQuery(graphene.ObjectType): """ user = info.context.user - newest: CourseSession | None = None + course = Course.objects.get(id=course_id) + newest: CourseSession | None = None + course_session_for_user: List[str] = [] + + # generation for course_session in CourseSession.objects.filter(course_id=course_id): if can_view_course_session_progress( user=user, course_session=course_session ): + course_session_for_user.append(course_session) generation_newest = newest.generation if newest else None if ( generation_newest is None @@ -100,11 +107,36 @@ class DashboardQuery(graphene.ObjectType): ): newest = course_session - if not newest: - return None + # competence + _, success_total, fail_total = competences( + course_slug=str(course.slug), + course_session_selection_ids=course_session_for_user + ) + + # assignment + evaluation_results = AssignmentCompletion.objects.filter( + completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value, + assignment_user=user, + course_session__course=course, + ).values("evaluation_max_points", "evaluation_points") + + evaluation_results = list(evaluation_results) + points_max_count = sum([result.get("evaluation_max_points", 0) for result in evaluation_results]) + points_achieved_count = sum([result.get("evaluation_points", 0) for result in evaluation_results]) return CourseProgressType( - id=course_id, session_to_continue_id=newest.id # noqa # noqa + id=course_id, # noqa + session_to_continue_id=newest.id if newest else None, # noqa + competence=ProgressDashboardCompetenceType( # noqa + total_count=success_total + fail_total, # noqa + success_count=success_total, # noqa + fail_count=fail_total, # noqa + ), + assignment=ProgressDashboardAssignmentType( # noqa + total_count=len(evaluation_results), # noqa + points_max_count=points_max_count, # noqa + points_achieved_count=points_achieved_count, # noqa + ), ) diff --git a/server/vbv_lernwelt/dashboard/graphql/types/competence.py b/server/vbv_lernwelt/dashboard/graphql/types/competence.py index 7a631bf6..80c21465 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/competence.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/competence.py @@ -1,3 +1,5 @@ +from typing import List, Tuple + import graphene from vbv_lernwelt.course.models import CourseCompletion, CourseCompletionStatus @@ -23,16 +25,22 @@ class Competences(graphene.ObjectType): def competences( - course_session_selection_ids: graphene.List(graphene.ID), - course_slug: graphene.String, -) -> Competences: + course_session_selection_ids: List[str], + course_slug: str, + user_selection_ids: List[str] | None = None, +) -> Tuple[List[CompetencePerformance], int, int]: completions = CourseCompletion.objects.filter( course_session_id__in=course_session_selection_ids, page_type="competence.PerformanceCriteria", ) + if user_selection_ids is not None: + completions = completions.filter(user_id__in=user_selection_ids) + competence_performances = {} + # purely for performance reasons, since looking up + # the circle for each completion is expensive :-/ circle_cache = {} for completion in completions: @@ -58,14 +66,8 @@ def competences( elif completion.completion_status == CourseCompletionStatus.FAIL.value: competence_performances[circle.id].fail_count += 1 - return Competences( - performances=competence_performances.values(), # noqa - summary=CompletionSummary( # noqa - success_total=sum( # noqa - [c.success_count for c in competence_performances.values()] - ), - fail_total=sum( # noqa - [c.fail_count for c in competence_performances.values()] - ), - ), - ) + values = list(competence_performances.values()) + success_count = sum([c.success_count for c in values]) + fail_count = sum([c.fail_count for c in values]) + + return values, success_count, fail_count diff --git a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py index 205e38f8..1c880c2e 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py @@ -7,7 +7,11 @@ from vbv_lernwelt.dashboard.graphql.types.attendance import ( attendance_day_presences, AttendanceDayPresences, ) -from vbv_lernwelt.dashboard.graphql.types.competence import competences, Competences +from vbv_lernwelt.dashboard.graphql.types.competence import ( + competences, + Competences, + CompletionSummary, +) from vbv_lernwelt.dashboard.graphql.types.feedback import ( feedback_responses, FeedbackResponses, @@ -51,9 +55,23 @@ class DashboardConfigType(graphene.ObjectType): dashboard_type = graphene.Field(DashboardType, required=True) +class ProgressDashboardCompetenceType(graphene.ObjectType): + total_count = graphene.Int(required=True) + success_count = graphene.Int(required=True) + fail_count = graphene.Int(required=True) + + +class ProgressDashboardAssignmentType(graphene.ObjectType): + total_count = graphene.Int(required=True) + points_max_count = graphene.Int(required=True) + points_achieved_count = graphene.Int(required=True) + + class CourseProgressType(graphene.ObjectType): id = graphene.ID(required=True) # course_id, named id for urql - session_to_continue_id = graphene.ID(required=True) + session_to_continue_id = graphene.ID(required=False) + competence = graphene.Field(ProgressDashboardCompetenceType, required=True) + assignment = graphene.Field(ProgressDashboardAssignmentType, required=True) class CourseStatisticsType(graphene.ObjectType): @@ -82,9 +100,17 @@ class CourseStatisticsType(graphene.ObjectType): ) def resolve_competences(root, info) -> Competences: - return competences( - course_session_selection_ids=root.course_session_selection_ids, - course_slug=root.course_slug, + performances, success_total, fail_total = competences( + course_slug=str(root.course_slug), + course_session_selection_ids=[ + str(cs) for cs in root.course_session_selection_ids # noqa + ], + ) + return Competences( + performances=performances, # noqa + summary=CompletionSummary( # noqa + success_total=success_total, fail_total=fail_total # noqa + ), ) def resolve_assignments(root, info) -> Assignments: diff --git a/server/vbv_lernwelt/dashboard/tests/graphql/test_attendance.py b/server/vbv_lernwelt/dashboard/tests/graphql/test_attendance.py index 3d70af66..e6738c83 100644 --- a/server/vbv_lernwelt/dashboard/tests/graphql/test_attendance.py +++ b/server/vbv_lernwelt/dashboard/tests/graphql/test_attendance.py @@ -114,7 +114,8 @@ class DashboardAttendanceTestCase(GraphQLTestCase): self.assertEqual(record["participants_total"], 3) self.assertEqual( record["details_url"], - f"/course/test-lehrgang/cockpit/attendance?id={attendance_course.learning_content.id}&courseSessionId={course_session.id}", + f"/course/test-lehrgang/cockpit/attendance?id={attendance_course.learning_content.id}" + f"&courseSessionId={course_session.id}", ) summary = attendance_day_presences["summary"] diff --git a/server/vbv_lernwelt/dashboard/tests/graphql/test_dashboard.py b/server/vbv_lernwelt/dashboard/tests/graphql/test_dashboard.py index a4062dd2..7dad179f 100644 --- a/server/vbv_lernwelt/dashboard/tests/graphql/test_dashboard.py +++ b/server/vbv_lernwelt/dashboard/tests/graphql/test_dashboard.py @@ -1,5 +1,6 @@ from graphene_django.utils import GraphQLTestCase +from vbv_lernwelt.assignment.models import AssignmentType from vbv_lernwelt.course.models import CourseSessionUser from vbv_lernwelt.dashboard.tests.graphql.utils import ( add_course_session_group_supervisor, @@ -8,6 +9,8 @@ from vbv_lernwelt.dashboard.tests.graphql.utils import ( create_course_session, create_course_session_group, create_user, + create_assignment_completion, + create_assignment, ) @@ -40,12 +43,44 @@ class DashboardTestCase(GraphQLTestCase): course_session=cs_3, user=member, role=CourseSessionUser.Role.MEMBER ) + create_assignment_completion( + user=member, + assignment=create_assignment( + course=course, assignment_type=AssignmentType.CASEWORK + ), + course_session=cs_1, + has_passed=True, + achieved_points=10, + max_points=10, + ) + + create_assignment_completion( + user=member, + assignment=create_assignment( + course=course, assignment_type=AssignmentType.CASEWORK + ), + course_session=cs_2, + has_passed=False, + achieved_points=10, + max_points=40, + ) + self.client.force_login(member) query = f"""query($course_id: ID!) {{ course_progress(course_id: $course_id) {{ id session_to_continue_id + competence {{ + total_count + success_count + fail_count + }} + assignment {{ + total_count + points_max_count + points_achieved_count + }} }} }} """ @@ -63,6 +98,14 @@ class DashboardTestCase(GraphQLTestCase): self.assertEqual(course_progress["id"], str(course.id)) self.assertEqual(course_progress["session_to_continue_id"], str(cs_2.id)) + competence = course_progress["competence"] + # TODO + + assignment = course_progress["assignment"] + self.assertEqual(assignment["total_count"], 2) + self.assertEqual(assignment["points_max_count"], 50) + self.assertEqual(assignment["points_achieved_count"], 20) + def test_dashboard_config(self): # GIVEN course_1, _ = create_course("Test Course 1") diff --git a/server/vbv_lernwelt/dashboard/tests/graphql/utils.py b/server/vbv_lernwelt/dashboard/tests/graphql/utils.py index 65a5901f..0071c116 100644 --- a/server/vbv_lernwelt/dashboard/tests/graphql/utils.py +++ b/server/vbv_lernwelt/dashboard/tests/graphql/utils.py @@ -181,14 +181,19 @@ def create_assignment_completion( assignment: Assignment, course_session: CourseSession, has_passed: bool | None = None, + max_points: int = 0, + achieved_points: int = 0, + status: AssignmentCompletionStatus = AssignmentCompletionStatus.EVALUATION_SUBMITTED, ) -> AssignmentCompletion: return AssignmentCompletion.objects.create( - completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value, + completion_status=status.value, assignment_user=user, assignment=assignment, evaluation_passed=has_passed, course_session=course_session, completion_data={}, + evaluation_max_points=max_points, + evaluation_points=achieved_points, )