feat: course_progress assignment and competence

This commit is contained in:
Livio Bieri 2023-10-25 18:34:33 +02:00
parent 571b6b347b
commit 0f80fe5104
6 changed files with 135 additions and 26 deletions

View File

@ -2,14 +2,16 @@ from typing import Dict, List, Set, Tuple
import graphene import graphene
from vbv_lernwelt.assignment.models import AssignmentCompletion, AssignmentCompletionStatus
from vbv_lernwelt.core.admin import User from vbv_lernwelt.core.admin import User
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
from vbv_lernwelt.course_session_group.models import CourseSessionGroup 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 ( from vbv_lernwelt.dashboard.graphql.types.dashboard import (
CourseProgressType, CourseProgressType,
CourseStatisticsType, CourseStatisticsType,
DashboardConfigType, DashboardConfigType,
DashboardType, DashboardType, ProgressDashboardCompetenceType, ProgressDashboardAssignmentType,
) )
from vbv_lernwelt.iam.permissions import ( from vbv_lernwelt.iam.permissions import (
can_view_course_session, can_view_course_session,
@ -87,12 +89,17 @@ class DashboardQuery(graphene.ObjectType):
""" """
user = info.context.user 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): for course_session in CourseSession.objects.filter(course_id=course_id):
if can_view_course_session_progress( if can_view_course_session_progress(
user=user, course_session=course_session user=user, course_session=course_session
): ):
course_session_for_user.append(course_session)
generation_newest = newest.generation if newest else None generation_newest = newest.generation if newest else None
if ( if (
generation_newest is None generation_newest is None
@ -100,11 +107,36 @@ class DashboardQuery(graphene.ObjectType):
): ):
newest = course_session newest = course_session
if not newest: # competence
return None _, 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( 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
),
) )

View File

@ -1,3 +1,5 @@
from typing import List, Tuple
import graphene import graphene
from vbv_lernwelt.course.models import CourseCompletion, CourseCompletionStatus from vbv_lernwelt.course.models import CourseCompletion, CourseCompletionStatus
@ -23,16 +25,22 @@ class Competences(graphene.ObjectType):
def competences( def competences(
course_session_selection_ids: graphene.List(graphene.ID), course_session_selection_ids: List[str],
course_slug: graphene.String, course_slug: str,
) -> Competences: user_selection_ids: List[str] | None = None,
) -> Tuple[List[CompetencePerformance], int, int]:
completions = CourseCompletion.objects.filter( completions = CourseCompletion.objects.filter(
course_session_id__in=course_session_selection_ids, course_session_id__in=course_session_selection_ids,
page_type="competence.PerformanceCriteria", page_type="competence.PerformanceCriteria",
) )
if user_selection_ids is not None:
completions = completions.filter(user_id__in=user_selection_ids)
competence_performances = {} competence_performances = {}
# purely for performance reasons, since looking up
# the circle for each completion is expensive :-/
circle_cache = {} circle_cache = {}
for completion in completions: for completion in completions:
@ -58,14 +66,8 @@ def competences(
elif completion.completion_status == CourseCompletionStatus.FAIL.value: elif completion.completion_status == CourseCompletionStatus.FAIL.value:
competence_performances[circle.id].fail_count += 1 competence_performances[circle.id].fail_count += 1
return Competences( values = list(competence_performances.values())
performances=competence_performances.values(), # noqa success_count = sum([c.success_count for c in values])
summary=CompletionSummary( # noqa fail_count = sum([c.fail_count for c in values])
success_total=sum( # noqa
[c.success_count for c in competence_performances.values()] return values, success_count, fail_count
),
fail_total=sum( # noqa
[c.fail_count for c in competence_performances.values()]
),
),
)

View File

@ -7,7 +7,11 @@ from vbv_lernwelt.dashboard.graphql.types.attendance import (
attendance_day_presences, attendance_day_presences,
AttendanceDayPresences, 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 ( from vbv_lernwelt.dashboard.graphql.types.feedback import (
feedback_responses, feedback_responses,
FeedbackResponses, FeedbackResponses,
@ -51,9 +55,23 @@ class DashboardConfigType(graphene.ObjectType):
dashboard_type = graphene.Field(DashboardType, required=True) 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): class CourseProgressType(graphene.ObjectType):
id = graphene.ID(required=True) # course_id, named id for urql 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): class CourseStatisticsType(graphene.ObjectType):
@ -82,9 +100,17 @@ class CourseStatisticsType(graphene.ObjectType):
) )
def resolve_competences(root, info) -> Competences: def resolve_competences(root, info) -> Competences:
return competences( performances, success_total, fail_total = competences(
course_session_selection_ids=root.course_session_selection_ids, course_slug=str(root.course_slug),
course_slug=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: def resolve_assignments(root, info) -> Assignments:

View File

@ -114,7 +114,8 @@ class DashboardAttendanceTestCase(GraphQLTestCase):
self.assertEqual(record["participants_total"], 3) self.assertEqual(record["participants_total"], 3)
self.assertEqual( self.assertEqual(
record["details_url"], 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"] summary = attendance_day_presences["summary"]

View File

@ -1,5 +1,6 @@
from graphene_django.utils import GraphQLTestCase from graphene_django.utils import GraphQLTestCase
from vbv_lernwelt.assignment.models import AssignmentType
from vbv_lernwelt.course.models import CourseSessionUser from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.dashboard.tests.graphql.utils import ( from vbv_lernwelt.dashboard.tests.graphql.utils import (
add_course_session_group_supervisor, add_course_session_group_supervisor,
@ -8,6 +9,8 @@ from vbv_lernwelt.dashboard.tests.graphql.utils import (
create_course_session, create_course_session,
create_course_session_group, create_course_session_group,
create_user, create_user,
create_assignment_completion,
create_assignment,
) )
@ -40,12 +43,44 @@ class DashboardTestCase(GraphQLTestCase):
course_session=cs_3, user=member, role=CourseSessionUser.Role.MEMBER 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) self.client.force_login(member)
query = f"""query($course_id: ID!) {{ query = f"""query($course_id: ID!) {{
course_progress(course_id: $course_id) {{ course_progress(course_id: $course_id) {{
id id
session_to_continue_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["id"], str(course.id))
self.assertEqual(course_progress["session_to_continue_id"], str(cs_2.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): def test_dashboard_config(self):
# GIVEN # GIVEN
course_1, _ = create_course("Test Course 1") course_1, _ = create_course("Test Course 1")

View File

@ -181,14 +181,19 @@ def create_assignment_completion(
assignment: Assignment, assignment: Assignment,
course_session: CourseSession, course_session: CourseSession,
has_passed: bool | None = None, has_passed: bool | None = None,
max_points: int = 0,
achieved_points: int = 0,
status: AssignmentCompletionStatus = AssignmentCompletionStatus.EVALUATION_SUBMITTED,
) -> AssignmentCompletion: ) -> AssignmentCompletion:
return AssignmentCompletion.objects.create( return AssignmentCompletion.objects.create(
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value, completion_status=status.value,
assignment_user=user, assignment_user=user,
assignment=assignment, assignment=assignment,
evaluation_passed=has_passed, evaluation_passed=has_passed,
course_session=course_session, course_session=course_session,
completion_data={}, completion_data={},
evaluation_max_points=max_points,
evaluation_points=achieved_points,
) )