From 49fdbd9648b0933093019809f343152298a4c329 Mon Sep 17 00:00:00 2001 From: Livio Bieri Date: Wed, 25 Oct 2023 10:46:58 +0200 Subject: [PATCH] fix: expose id on top-level; use name for dashboard config --- .../vbv_lernwelt/dashboard/graphql/queries.py | 30 ++++++---- .../dashboard/graphql/types/competence.py | 18 +++--- .../dashboard/graphql/types/dashboard.py | 57 +++++++++++-------- .../dashboard/graphql/types/feedback.py | 20 +++---- .../tests/graphql/test_competence.py | 2 +- .../dashboard/tests/graphql/test_dashboard.py | 18 +++--- .../dashboard/tests/graphql/test_feedback.py | 2 +- server/vbv_lernwelt/iam/permissions.py | 8 +++ 8 files changed, 89 insertions(+), 66 deletions(-) diff --git a/server/vbv_lernwelt/dashboard/graphql/queries.py b/server/vbv_lernwelt/dashboard/graphql/queries.py index ae4c31cf..59d102d6 100644 --- a/server/vbv_lernwelt/dashboard/graphql/queries.py +++ b/server/vbv_lernwelt/dashboard/graphql/queries.py @@ -2,16 +2,14 @@ import graphene from vbv_lernwelt.course.models import CourseSession, Course from vbv_lernwelt.course_session_group.models import CourseSessionGroup -from vbv_lernwelt.dashboard.graphql.types.dashboard import CourseStatisticsType, DashboardConfigType -from vbv_lernwelt.iam.permissions import can_view_course_session_group_statistics, can_view_course_session +from vbv_lernwelt.dashboard.graphql.types.dashboard import CourseStatisticsType, DashboardConfigType, DashboardType +from vbv_lernwelt.iam.permissions import can_view_course_session_group_statistics, can_view_course_session, \ + can_view_course_session_progress class DashboardQuery(graphene.ObjectType): course_statistics = graphene.Field(CourseStatisticsType, course_id=graphene.ID(required=True)) - - dashboard_config = graphene.List( - DashboardConfigType - ) + dashboard_config = graphene.List(DashboardConfigType, required=False) def resolve_course_statistics(root, info, course_id: str): # noqa user = info.context.user @@ -27,7 +25,7 @@ class DashboardQuery(graphene.ObjectType): return None - return CourseStatisticsType(course_id=course.id, course_title=course.title, # noqa + return CourseStatisticsType(id=course.id, course_title=course.title, # noqa course_session_selection_ids=list(course_session_ids)) # noqa @@ -45,19 +43,27 @@ class DashboardQuery(graphene.ObjectType): dashboards.append( { "id": str(course.id), - "title": course.title, - "dashboard_type": "StatisticsDashboard", + "name": course.title, + "dashboard_type": DashboardType.STATISTICS_DASHBOARD } ) for course_session in CourseSession.objects.exclude(course__in=course_index): + course = course_session.course if can_view_course_session(user=user, course_session=course_session): - course = course_session.course dashboards.append( { "id": str(course.id), - "title": course.title, - "dashboard_type": "SimpleDashboard", + "name": course.title, + "dashboard_type": DashboardType.SIMPLE_LIST_DASHBOARD + } + ) + if can_view_course_session_progress(user=user, course_session=course_session): + dashboards.append( + { + "id": str(course.id), + "name": course.title, + "dashboard_type": DashboardType.PROGRESS_DASHBOARD } ) diff --git a/server/vbv_lernwelt/dashboard/graphql/types/competence.py b/server/vbv_lernwelt/dashboard/graphql/types/competence.py index 42826e0a..469bce3a 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/competence.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/competence.py @@ -4,21 +4,21 @@ from vbv_lernwelt.course.models import CourseCompletion, CourseCompletionStatus class CompletionSummary(graphene.ObjectType): - success_total = graphene.Int() - fail_total = graphene.Int() + success_total = graphene.Int(required=True) + fail_total = graphene.Int(required=True) class CompetencePerformance(graphene.ObjectType): - course_session_id = graphene.ID() - generation = graphene.String() - circle_id = graphene.ID() - success_count = graphene.Int() - fail_count = graphene.Int() + course_session_id = graphene.ID(required=True) + generation = graphene.String(required=True) + circle_id = graphene.ID(required=True) + success_count = graphene.Int(required=True) + fail_count = graphene.Int(required=True) class Competences(graphene.ObjectType): - performances = graphene.List(CompetencePerformance) - summary = graphene.Field(CompletionSummary) + performances = graphene.List(CompetencePerformance, required=True) + summary = graphene.Field(CompletionSummary, required=True) def competences( diff --git a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py index df652341..eb2a8ee9 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py @@ -1,4 +1,5 @@ import graphene +from graphene import Enum from vbv_lernwelt.course.models import CourseSession, CourseSessionUser from vbv_lernwelt.dashboard.graphql.types.assignment import Assignments, assignments @@ -15,14 +16,14 @@ from vbv_lernwelt.learnpath.models import Circle class CourseSessionData(graphene.ObjectType): - session_id = graphene.ID() - session_title = graphene.String() + session_id = graphene.ID(required=True) + session_title = graphene.String(required=True) class CircleData(graphene.ObjectType): - circle_id = graphene.ID() - circle_title = graphene.String() - experts = graphene.List(graphene.String) + circle_id = graphene.ID(required=True) + circle_title = graphene.String(required=True) + experts = graphene.List(graphene.String, required=True) class CourseSessionsSelectionMetrics(graphene.ObjectType): @@ -32,27 +33,35 @@ class CourseSessionsSelectionMetrics(graphene.ObjectType): class CourseSessionProperties(graphene.ObjectType): - sessions = graphene.List(CourseSessionData) - generations = graphene.List(graphene.String) - circles = graphene.List(CircleData) + sessions = graphene.List(CourseSessionData, required=True) + generations = graphene.List(graphene.String, required=True) + circles = graphene.List(CircleData, required=True) + + +class DashboardType(Enum): + STATISTICS_DASHBOARD = "StatisticsDashboard" + PROGRESS_DASHBOARD = "ProgressDashboard" + SIMPLE_LIST_DASHBOARD = "SimpleListDashboard" class DashboardConfigType(graphene.ObjectType): - id = graphene.ID() - title = graphene.String() - dashboard_type = graphene.String() + id = graphene.ID(required=True) + name = graphene.String(required=True) + dashboard_type = graphene.Field(DashboardType, required=True) class CourseStatisticsType(graphene.ObjectType): - course_id = graphene.ID() - course_title = graphene.String() - course_session_properties = graphene.Field(CourseSessionProperties) - course_session_selection_ids = graphene.List(graphene.ID) - course_session_selection_metrics = graphene.Field(CourseSessionsSelectionMetrics) - attendance_day_presences = graphene.Field(AttendanceDayPresences) - feedback_responses = graphene.Field(FeedbackResponses) - assignments = graphene.Field(Assignments) - competences = graphene.Field(Competences) + id = graphene.ID(required=True) + course_title = graphene.String(required=True) + course_session_properties = graphene.Field(CourseSessionProperties, required=True) + course_session_selection_ids = graphene.List(graphene.ID, required=True) + course_session_selection_metrics = graphene.Field( + CourseSessionsSelectionMetrics, required=True + ) + attendance_day_presences = graphene.Field(AttendanceDayPresences, required=True) + feedback_responses = graphene.Field(FeedbackResponses, required=True) + assignments = graphene.Field(Assignments, required=True) + competences = graphene.Field(Competences, required=True) def resolve_attendance_day_presences(root, info) -> AttendanceDayPresences: return attendance_day_presences(root.course_session_selection_ids) @@ -71,18 +80,18 @@ class CourseStatisticsType(graphene.ObjectType): ) -> CourseSessionsSelectionMetrics: course_session_count = CourseSession.objects.filter( id__in=root.course_session_selection_ids, - course_id=root.course_id, + course_id=root.id, ).count() expert_count = CourseSession.objects.filter( id__in=root.course_session_selection_ids, - course_id=root.course_id, + course_id=root.id, coursesessionuser__role=CourseSessionUser.Role.EXPERT, ).count() participant_count = CourseSession.objects.filter( id__in=root.course_session_selection_ids, - course_id=root.course_id, + course_id=root.id, coursesessionuser__role=CourseSessionUser.Role.MEMBER, ).count() @@ -99,7 +108,7 @@ class CourseStatisticsType(graphene.ObjectType): course_sessions = CourseSession.objects.filter( id__in=root.course_session_selection_ids, - course_id=root.course_id, + course_id=root.id, ) for course_session in course_sessions: diff --git a/server/vbv_lernwelt/dashboard/graphql/types/feedback.py b/server/vbv_lernwelt/dashboard/graphql/types/feedback.py index 3f078208..d768685c 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/feedback.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/feedback.py @@ -8,22 +8,22 @@ from vbv_lernwelt.feedback.utils import feedback_users class FeedbackSummary(graphene.ObjectType): - satisfaction_average = graphene.Float() - satisfaction_max = graphene.Int() - total_responses = graphene.Int() + satisfaction_average = graphene.Float(required=True) + satisfaction_max = graphene.Int(required=True) + total_responses = graphene.Int(required=True) class FeedbackRecord(graphene.ObjectType): - course_session_id = graphene.ID() - generation = graphene.String() - circle_id = graphene.ID() - satisfaction_average = graphene.Float() - satisfaction_max = graphene.Int() + course_session_id = graphene.ID(required=True) + generation = graphene.String(required=True) + circle_id = graphene.ID(required=True) + satisfaction_average = graphene.Float(required=True) + satisfaction_max = graphene.Int(required=True) class FeedbackResponses(graphene.ObjectType): - records = graphene.List(FeedbackRecord) - summary = graphene.Field(FeedbackSummary) + records = graphene.List(FeedbackRecord, required=True) + summary = graphene.Field(FeedbackSummary, required=True) def feedback_responses( diff --git a/server/vbv_lernwelt/dashboard/tests/graphql/test_competence.py b/server/vbv_lernwelt/dashboard/tests/graphql/test_competence.py index 0dc423d1..f2ce8f03 100644 --- a/server/vbv_lernwelt/dashboard/tests/graphql/test_competence.py +++ b/server/vbv_lernwelt/dashboard/tests/graphql/test_competence.py @@ -64,7 +64,7 @@ class DashboardCompetenceTestCase(GraphQLTestCase): query = f"""query($course_id: ID!) {{ course_statistics(course_id: $course_id) {{ - course_id + id competences {{ performances {{ course_session_id diff --git a/server/vbv_lernwelt/dashboard/tests/graphql/test_dashboard.py b/server/vbv_lernwelt/dashboard/tests/graphql/test_dashboard.py index 238f11c9..ceb28fd7 100644 --- a/server/vbv_lernwelt/dashboard/tests/graphql/test_dashboard.py +++ b/server/vbv_lernwelt/dashboard/tests/graphql/test_dashboard.py @@ -39,7 +39,7 @@ class DashboardTestCase(GraphQLTestCase): query = """query { dashboard_config { id - title + name dashboard_type } } @@ -51,21 +51,21 @@ class DashboardTestCase(GraphQLTestCase): self.assertResponseNoErrors(response) dashboard_config = response.json()["data"]["dashboard_config"] - self.assertEqual(len(dashboard_config), 2) + self.assertEqual(len(dashboard_config), 3) course_1_config = find_dashboard_config_by_course_id( dashboard_config, course_1.id ) self.assertIsNotNone(course_1_config) - self.assertEqual(course_1_config["title"], course_1.title) - self.assertEqual(course_1_config["dashboard_type"], "SimpleDashboard") + self.assertEqual(course_1_config["name"], course_1.title) + self.assertEqual(course_1_config["dashboard_type"], "SIMPLE_LIST_DASHBOARD") course_2_config = find_dashboard_config_by_course_id( dashboard_config, course_2.id ) self.assertIsNotNone(course_2_config) - self.assertEqual(course_2_config["title"], course_2.title) - self.assertEqual(course_2_config["dashboard_type"], "StatisticsDashboard") + self.assertEqual(course_2_config["name"], course_2.title) + self.assertEqual(course_2_config["dashboard_type"], "STATISTICS_DASHBOARD") def test_course_statistics_deny_not_allowed_user(self): # GIVEN @@ -77,7 +77,7 @@ class DashboardTestCase(GraphQLTestCase): query = f"""query($course_id: ID!) {{ course_statistics(course_id: $course_id) {{ - course_id + id }} }} """ @@ -111,7 +111,7 @@ class DashboardTestCase(GraphQLTestCase): query = f"""query($course_id: ID!) {{ course_statistics(course_id: $course_id) {{ - course_id + id course_title }} }} @@ -126,7 +126,7 @@ class DashboardTestCase(GraphQLTestCase): course_statistics = response.json()["data"]["course_statistics"] - self.assertEqual(course_statistics["course_id"], str(course_2.id)) + self.assertEqual(course_statistics["id"], str(course_2.id)) self.assertEqual(course_statistics["course_title"], course_2.title) diff --git a/server/vbv_lernwelt/dashboard/tests/graphql/test_feedback.py b/server/vbv_lernwelt/dashboard/tests/graphql/test_feedback.py index 5ab9c9dd..0cb100e6 100644 --- a/server/vbv_lernwelt/dashboard/tests/graphql/test_feedback.py +++ b/server/vbv_lernwelt/dashboard/tests/graphql/test_feedback.py @@ -71,7 +71,7 @@ class DashboardFeedbackTestCase(GraphQLTestCase): query = f"""query($course_id: ID!) {{ course_statistics(course_id: $course_id) {{ - course_id + id feedback_responses {{ records {{ course_session_id diff --git a/server/vbv_lernwelt/iam/permissions.py b/server/vbv_lernwelt/iam/permissions.py index fb75507e..2c41e131 100644 --- a/server/vbv_lernwelt/iam/permissions.py +++ b/server/vbv_lernwelt/iam/permissions.py @@ -74,6 +74,14 @@ def can_view_course_session_group_statistics( return user in group.supervisor.all() +def can_view_course_session_progress(user: User, course_session: CourseSession) -> bool: + return CourseSessionUser.objects.filter( + course_session=course_session, + user=user, + role=CourseSessionUser.Role.MEMBER, + ).exists() + + def can_view_course_session(user: User, course_session: CourseSession) -> bool: if user.is_superuser: return True