diff --git a/server/vbv_lernwelt/dashboard/graphql/queries.py b/server/vbv_lernwelt/dashboard/graphql/queries.py index 4693c49c..fd508198 100644 --- a/server/vbv_lernwelt/dashboard/graphql/queries.py +++ b/server/vbv_lernwelt/dashboard/graphql/queries.py @@ -22,6 +22,6 @@ class DashboardQuery(graphene.ObjectType): courses = query.distinct() return [ - CourseDashboardType(course_id=course.id, course_title=course.title) + CourseDashboardType(course_id=course.id, course_title=course.title) # noqa for course in courses ] diff --git a/server/vbv_lernwelt/dashboard/graphql/types/attendance.py b/server/vbv_lernwelt/dashboard/graphql/types/attendance.py index 66e38dfa..522b062c 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/attendance.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/attendance.py @@ -31,12 +31,14 @@ class AttendanceDayPresences(graphene.ObjectType): summary = graphene.Field(AttendanceSummary) -def attendance_day_presences(course_id: graphene.String(), user: User): +def attendance_day_presences( + course_id: graphene.String(), user: User +) -> AttendanceDayPresences: completed = CourseSessionAttendanceCourse.objects.filter( + course_session__course_id=course_id, course_session__coursesessionuser__user=user, course_session__coursesessionuser__role=CourseSessionUser.Role.SESSION_SUPERVISOR, due_date__end__lt=datetime.datetime.now(), - course_session__course_id=course_id, ).order_by("-due_date__end") records = [] diff --git a/server/vbv_lernwelt/dashboard/graphql/types/competence.py b/server/vbv_lernwelt/dashboard/graphql/types/competence.py new file mode 100644 index 00000000..a4b2b1e1 --- /dev/null +++ b/server/vbv_lernwelt/dashboard/graphql/types/competence.py @@ -0,0 +1,64 @@ +import graphene + +from vbv_lernwelt.core.models import User +from vbv_lernwelt.course.models import ( + CourseCompletion, + CourseCompletionStatus, + CourseSessionUser, +) + + +class CompletionSummary(graphene.ObjectType): + success_total = graphene.Int() + fail_total = graphene.Int() + + +class CompetencePerformance(graphene.ObjectType): + course_session_id = graphene.ID() + generation = graphene.String() + circle_id = graphene.ID() + success_count = graphene.Int() + fail_count = graphene.Int() + + +class Competences(graphene.ObjectType): + performances = graphene.List(CompetencePerformance) + summary = graphene.Field(CompletionSummary) + + +def competences(course_id: graphene.String(), user: User) -> Competences: + completions = CourseCompletion.objects.filter( + course_session__course_id=course_id, + course_session__coursesessionuser__user=user, + course_session__coursesessionuser__role=CourseSessionUser.Role.SESSION_SUPERVISOR, + page_type="competence.PerformanceCriteria", + ) + + competence_performances = {} + + for c in completions: + circle = c.page.specific.learning_unit.get_circle() + if circle.id not in competence_performances: + competence_performances[circle.id] = CompetencePerformance( + course_session_id=c.course_session.id, # noqa + generation=c.course_session.generation, # noqa + circle_id=circle.id, # noqa + success_count=0, # noqa + fail_count=0, # noqa + ) + if c.completion_status == CourseCompletionStatus.SUCCESS: + competence_performances[circle.id].success_count += 1 + elif c.completion_status == CourseCompletionStatus.FAIL: + 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()] + ), + ), + ) diff --git a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py index bfa683f4..3c342c81 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py @@ -5,6 +5,7 @@ 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.feedback import ( feedback_responses, FeedbackResponses, @@ -35,13 +36,17 @@ class CourseDashboardType(graphene.ObjectType): course_session_properties = graphene.Field(CourseSessionProperties) attendance_day_presences = graphene.Field(AttendanceDayPresences) feedback_responses = graphene.Field(FeedbackResponses) + competences = graphene.Field(Competences) - def resolve_attendance_day_presences(root, info): + def resolve_attendance_day_presences(root, info) -> AttendanceDayPresences: return attendance_day_presences(root.course_id, info.context.user) - def resolve_feedback_responses(root, info): + def resolve_feedback_responses(root, info) -> FeedbackResponses: return feedback_responses(root.course_id, info.context.user) + def resolve_competences(root, info) -> Competences: + return competences(root.course_id, info.context.user) + def resolve_course_session_properties(root, info): course_session_data = [] circle_data = [] diff --git a/server/vbv_lernwelt/dashboard/graphql/types/feedback.py b/server/vbv_lernwelt/dashboard/graphql/types/feedback.py index 1d719d97..231e8803 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/feedback.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/feedback.py @@ -27,7 +27,7 @@ class FeedbackResponses(graphene.ObjectType): summary = graphene.Field(FeedbackSummary) -def feedback_responses(course_id: graphene.String(), user: User): +def feedback_responses(course_id: graphene.String(), user: User) -> FeedbackResponses: # Get all course sessions for this user in the given course course_sessions = CourseSession.objects.filter( coursesessionuser__user=user, diff --git a/server/vbv_lernwelt/dashboard/tests/graphql/test_competence.py b/server/vbv_lernwelt/dashboard/tests/graphql/test_competence.py new file mode 100644 index 00000000..9708564c --- /dev/null +++ b/server/vbv_lernwelt/dashboard/tests/graphql/test_competence.py @@ -0,0 +1,66 @@ +from graphene_django.utils import GraphQLTestCase + +from vbv_lernwelt.course.models import CourseSessionUser +from vbv_lernwelt.dashboard.tests.graphql.utils import ( + add_course_session_user, + create_circle, + create_course, + create_course_session, + create_user, +) + + +class DashboardCompetenceTestCase(GraphQLTestCase): + GRAPHQL_URL = "/server/graphql/" + + def test_competence(self): + # GIVEN + course, course_page = create_course("Test Course") + course_session = create_course_session(course=course, title="Test Bern 2022 a") + + supervisor = create_user("supervisor") + + add_course_session_user( + course_session=course_session, + user=supervisor, + role=CourseSessionUser.Role.SESSION_SUPERVISOR, + ) + + member = create_user("member") + add_course_session_user( + course_session=course_session, + user=member, + role=CourseSessionUser.Role.MEMBER, + ) + + circle1, _ = create_circle(title="Test Circle 1", course_page=course_page) + circle2, _ = create_circle(title="Test Circle 2", course_page=course_page) + + self.client.force_login(supervisor) + + query = f"""query($course_id: ID) {{ + course_dashboard(course_id: $course_id) {{ + course_id + competences {{ + performances {{ + course_session_id + generation + circle_id + success_count + fail_count + }} + summary {{ + success_total + fail_total + }} + }} + }} + }} + """ + variables = {"course_id": str(course.id)} + + # WHEN + response = self.query(query, variables=variables) + + # THEN + self.assertResponseNoErrors(response)