feat: course_progress assignment and competence
This commit is contained in:
parent
571b6b347b
commit
0f80fe5104
|
|
@ -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
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue