From eda9829b36c74ff314cea0e49fbdd926ad071358 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Tue, 9 Apr 2024 08:59:44 +0200 Subject: [PATCH] Add mentor components and basic styling --- .../src/components/dashboard/CoursePanel.vue | 32 +++++++--- .../dashboard/MentorCompetenceSummary.vue | 38 +++++++++++ .../dashboard/MentorMenteeCount.vue | 29 +++++++++ .../dashboard/MentorOpenTasksCount.vue | 29 +++++++++ client/src/gql/gql.ts | 5 ++ client/src/gql/graphql.ts | 8 +++ client/src/graphql/queries.ts | 16 +++++ client/src/pages/dashboard/DashboardPage.vue | 2 - client/src/services/dashboard.ts | 23 +++++-- server/config/urls.py | 12 ++-- server/vbv_lernwelt/dashboard/views.py | 63 +++++++++++++++++-- 11 files changed, 232 insertions(+), 25 deletions(-) create mode 100644 client/src/components/dashboard/MentorCompetenceSummary.vue create mode 100644 client/src/components/dashboard/MentorMenteeCount.vue create mode 100644 client/src/components/dashboard/MentorOpenTasksCount.vue diff --git a/client/src/components/dashboard/CoursePanel.vue b/client/src/components/dashboard/CoursePanel.vue index 40d133ff..dc5d960b 100644 --- a/client/src/components/dashboard/CoursePanel.vue +++ b/client/src/components/dashboard/CoursePanel.vue @@ -1,9 +1,12 @@ + + diff --git a/client/src/components/dashboard/MentorMenteeCount.vue b/client/src/components/dashboard/MentorMenteeCount.vue new file mode 100644 index 00000000..25c8a953 --- /dev/null +++ b/client/src/components/dashboard/MentorMenteeCount.vue @@ -0,0 +1,29 @@ + + + diff --git a/client/src/components/dashboard/MentorOpenTasksCount.vue b/client/src/components/dashboard/MentorOpenTasksCount.vue new file mode 100644 index 00000000..bb310a0f --- /dev/null +++ b/client/src/components/dashboard/MentorOpenTasksCount.vue @@ -0,0 +1,29 @@ + + + diff --git a/client/src/gql/gql.ts b/client/src/gql/gql.ts index 3d3454b4..28ab3396 100644 --- a/client/src/gql/gql.ts +++ b/client/src/gql/gql.ts @@ -25,6 +25,7 @@ const documents = { "\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n competence {\n _id\n total_count\n success_count\n fail_count\n }\n assignment {\n _id\n total_count\n points_max_count\n points_achieved_count\n }\n }\n }\n": types.DashboardProgressDocument, "\n query dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n }\n }\n": types.DashboardCourseDataDocument, "\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n": types.CourseStatisticsDocument, + "\n query mentorCourseStatistics($courseId: ID!) {\n mentor_course_statistics(course_id: $courseId) {\n _id\n assignments {\n _id\n summary {\n _id\n total_passed\n total_failed\n }\n }\n }\n }\n": types.MentorCourseStatisticsDocument, "\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $learningContentType: String!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n learning_content_type: $learningContentType\n data: $data\n submitted: $submitted\n ) {\n feedback_response {\n id\n data\n submitted\n }\n errors {\n field\n messages\n }\n }\n }\n": types.SendFeedbackMutationDocument, }; @@ -90,6 +91,10 @@ export function graphql(source: "\n query dashboardCourseData($courseId: ID!) { * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n"): (typeof documents)["\n query courseStatistics($courseId: ID!) {\n course_statistics(course_id: $courseId) {\n _id\n course_id\n course_title\n course_slug\n course_session_properties {\n _id\n sessions {\n id\n name\n }\n generations\n circles {\n id\n name\n }\n }\n course_session_selection_ids\n course_session_selection_metrics {\n _id\n session_count\n participant_count\n expert_count\n }\n attendance_day_presences {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n due_date\n participants_present\n participants_total\n details_url\n }\n summary {\n _id\n days_completed\n participants_present\n }\n }\n feedback_responses {\n _id\n records {\n _id\n course_session_id\n generation\n circle_id\n experts\n satisfaction_average\n satisfaction_max\n details_url\n }\n summary {\n _id\n satisfaction_average\n satisfaction_max\n total_responses\n }\n }\n assignments {\n _id\n summary {\n _id\n completed_count\n average_passed\n }\n records {\n _id\n course_session_id\n course_session_assignment_id\n circle_id\n generation\n assignment_title\n assignment_type_translation_key\n details_url\n deadline\n metrics {\n _id\n passed_count\n failed_count\n unranked_count\n ranking_completed\n average_passed\n }\n }\n }\n competences {\n _id\n summary {\n _id\n success_total\n fail_total\n }\n records {\n _id\n course_session_id\n generation\n circle_id\n title\n success_count\n fail_count\n details_url\n }\n }\n }\n }\n"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "\n query mentorCourseStatistics($courseId: ID!) {\n mentor_course_statistics(course_id: $courseId) {\n _id\n assignments {\n _id\n summary {\n _id\n total_passed\n total_failed\n }\n }\n }\n }\n"): (typeof documents)["\n query mentorCourseStatistics($courseId: ID!) {\n mentor_course_statistics(course_id: $courseId) {\n _id\n assignments {\n _id\n summary {\n _id\n total_passed\n total_failed\n }\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/client/src/gql/graphql.ts b/client/src/gql/graphql.ts index 4186cb4c..c3e481af 100644 --- a/client/src/gql/graphql.ts +++ b/client/src/gql/graphql.ts @@ -1304,6 +1304,13 @@ export type CourseStatisticsQueryVariables = Exact<{ export type CourseStatisticsQuery = { __typename?: 'Query', course_statistics?: { __typename?: 'CourseStatisticsType', _id: string, course_id: string, course_title: string, course_slug: string, course_session_selection_ids: Array, course_session_properties: { __typename?: 'StatisticsCourseSessionPropertiesType', _id: string, generations: Array, sessions: Array<{ __typename?: 'StatisticsCourseSessionDataType', id: string, name: string }>, circles: Array<{ __typename?: 'StatisticsCircleDataType', id: string, name: string }> }, course_session_selection_metrics: { __typename?: 'StatisticsCourseSessionsSelectionMetricType', _id: string, session_count: number, participant_count: number, expert_count: number }, attendance_day_presences: { __typename?: 'AttendanceDayPresencesStatisticsType', _id: string, records: Array<{ __typename?: 'PresenceRecordStatisticsType', _id: string, course_session_id: string, generation: string, circle_id: string, due_date: string, participants_present: number, participants_total: number, details_url: string }>, summary: { __typename?: 'AttendanceSummaryStatisticsType', _id: string, days_completed: number, participants_present: number } }, feedback_responses: { __typename?: 'FeedbackStatisticsResponsesType', _id: string, records: Array<{ __typename?: 'FeedbackStatisticsRecordType', _id: string, course_session_id: string, generation: string, circle_id: string, experts: string, satisfaction_average: number, satisfaction_max: number, details_url: string }>, summary: { __typename?: 'FeedbackStatisticsSummaryType', _id: string, satisfaction_average: number, satisfaction_max: number, total_responses: number } }, assignments: { __typename?: 'AssignmentsStatisticsType', _id: string, summary: { __typename?: 'AssignmentStatisticsSummaryType', _id: string, completed_count: number, average_passed: number }, records: Array<{ __typename?: 'AssignmentStatisticsRecordType', _id: string, course_session_id: string, course_session_assignment_id: string, circle_id: string, generation: string, assignment_title: string, assignment_type_translation_key: string, details_url: string, deadline: string, metrics: { __typename?: 'AssignmentCompletionMetricsType', _id: string, passed_count: number, failed_count: number, unranked_count: number, ranking_completed: boolean, average_passed: number } }> }, competences: { __typename?: 'CompetencesStatisticsType', _id: string, summary: { __typename?: 'CompetencePerformanceStatisticsSummaryType', _id: string, success_total: number, fail_total: number }, records: Array<{ __typename?: 'CompetenceRecordStatisticsType', _id: string, course_session_id: string, generation: string, circle_id: string, title: string, success_count: number, fail_count: number, details_url: string }> } } | null }; +export type MentorCourseStatisticsQueryVariables = Exact<{ + courseId: Scalars['ID']['input']; +}>; + + +export type MentorCourseStatisticsQuery = { __typename?: 'Query', mentor_course_statistics?: { __typename?: 'CourseStatisticsType', _id: string, assignments: { __typename?: 'AssignmentsStatisticsType', _id: string, summary: { __typename?: 'AssignmentStatisticsSummaryType', _id: string, total_passed: number, total_failed: number } } } | null }; + export type SendFeedbackMutationMutationVariables = Exact<{ courseSessionId: Scalars['ID']['input']; learningContentId: Scalars['ID']['input']; @@ -1327,4 +1334,5 @@ export const DashboardConfigDocument = {"kind":"Document","definitions":[{"kind" export const DashboardProgressDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"dashboardProgress"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_progress"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_id"}},{"kind":"Field","name":{"kind":"Name","value":"session_to_continue_id"}},{"kind":"Field","name":{"kind":"Name","value":"competence"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"total_count"}},{"kind":"Field","name":{"kind":"Name","value":"success_count"}},{"kind":"Field","name":{"kind":"Name","value":"fail_count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"total_count"}},{"kind":"Field","name":{"kind":"Name","value":"points_max_count"}},{"kind":"Field","name":{"kind":"Name","value":"points_achieved_count"}}]}}]}}]}}]} as unknown as DocumentNode; export const DashboardCourseDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"dashboardCourseData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_progress"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_id"}},{"kind":"Field","name":{"kind":"Name","value":"session_to_continue_id"}}]}}]}}]} as unknown as DocumentNode; export const CourseStatisticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseStatistics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course_statistics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_title"}},{"kind":"Field","name":{"kind":"Name","value":"course_slug"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_properties"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"sessions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"generations"}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"course_session_selection_ids"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_selection_metrics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"session_count"}},{"kind":"Field","name":{"kind":"Name","value":"participant_count"}},{"kind":"Field","name":{"kind":"Name","value":"expert_count"}}]}},{"kind":"Field","name":{"kind":"Name","value":"attendance_day_presences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"records"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_id"}},{"kind":"Field","name":{"kind":"Name","value":"generation"}},{"kind":"Field","name":{"kind":"Name","value":"circle_id"}},{"kind":"Field","name":{"kind":"Name","value":"due_date"}},{"kind":"Field","name":{"kind":"Name","value":"participants_present"}},{"kind":"Field","name":{"kind":"Name","value":"participants_total"}},{"kind":"Field","name":{"kind":"Name","value":"details_url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"days_completed"}},{"kind":"Field","name":{"kind":"Name","value":"participants_present"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"feedback_responses"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"records"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_id"}},{"kind":"Field","name":{"kind":"Name","value":"generation"}},{"kind":"Field","name":{"kind":"Name","value":"circle_id"}},{"kind":"Field","name":{"kind":"Name","value":"experts"}},{"kind":"Field","name":{"kind":"Name","value":"satisfaction_average"}},{"kind":"Field","name":{"kind":"Name","value":"satisfaction_max"}},{"kind":"Field","name":{"kind":"Name","value":"details_url"}}]}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"satisfaction_average"}},{"kind":"Field","name":{"kind":"Name","value":"satisfaction_max"}},{"kind":"Field","name":{"kind":"Name","value":"total_responses"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"assignments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"completed_count"}},{"kind":"Field","name":{"kind":"Name","value":"average_passed"}}]}},{"kind":"Field","name":{"kind":"Name","value":"records"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_assignment_id"}},{"kind":"Field","name":{"kind":"Name","value":"circle_id"}},{"kind":"Field","name":{"kind":"Name","value":"generation"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_title"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type_translation_key"}},{"kind":"Field","name":{"kind":"Name","value":"details_url"}},{"kind":"Field","name":{"kind":"Name","value":"deadline"}},{"kind":"Field","name":{"kind":"Name","value":"metrics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"passed_count"}},{"kind":"Field","name":{"kind":"Name","value":"failed_count"}},{"kind":"Field","name":{"kind":"Name","value":"unranked_count"}},{"kind":"Field","name":{"kind":"Name","value":"ranking_completed"}},{"kind":"Field","name":{"kind":"Name","value":"average_passed"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"competences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"success_total"}},{"kind":"Field","name":{"kind":"Name","value":"fail_total"}}]}},{"kind":"Field","name":{"kind":"Name","value":"records"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"course_session_id"}},{"kind":"Field","name":{"kind":"Name","value":"generation"}},{"kind":"Field","name":{"kind":"Name","value":"circle_id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"success_count"}},{"kind":"Field","name":{"kind":"Name","value":"fail_count"}},{"kind":"Field","name":{"kind":"Name","value":"details_url"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const MentorCourseStatisticsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"mentorCourseStatistics"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mentor_course_statistics"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"assignments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"total_passed"}},{"kind":"Field","name":{"kind":"Name","value":"total_failed"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const SendFeedbackMutationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SendFeedbackMutation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"learningContentType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GenericScalar"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"send_feedback"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"course_session_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"courseSessionId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_page_id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"learning_content_type"},"value":{"kind":"Variable","name":{"kind":"Name","value":"learningContentType"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"submitted"},"value":{"kind":"Variable","name":{"kind":"Name","value":"submitted"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"feedback_response"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"submitted"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"field"}},{"kind":"Field","name":{"kind":"Name","value":"messages"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/client/src/graphql/queries.ts b/client/src/graphql/queries.ts index 5ce0b221..076d2f44 100644 --- a/client/src/graphql/queries.ts +++ b/client/src/graphql/queries.ts @@ -452,3 +452,19 @@ export const DASHBOARD_COURSE_STATISTICS = graphql(` } } `); + +export const DASHBOARD_MENTOR_COMPETENCE_SUMMARY = graphql(` + query mentorCourseStatistics($courseId: ID!) { + mentor_course_statistics(course_id: $courseId) { + _id + assignments { + _id + summary { + _id + total_passed + total_failed + } + } + } + } +`); diff --git a/client/src/pages/dashboard/DashboardPage.vue b/client/src/pages/dashboard/DashboardPage.vue index c06a6289..93bc5898 100644 --- a/client/src/pages/dashboard/DashboardPage.vue +++ b/client/src/pages/dashboard/DashboardPage.vue @@ -65,10 +65,8 @@ function newDashboardConfigForId(id: string) { @update:model-value="dashboardStore.switchAndLoadDashboardConfig" > -
  • -

    {{ newDashboardConfigForId(config.id) }}

diff --git a/client/src/services/dashboard.ts b/client/src/services/dashboard.ts index 834ca6de..7b18955e 100644 --- a/client/src/services/dashboard.ts +++ b/client/src/services/dashboard.ts @@ -1,13 +1,14 @@ import { graphqlClient } from "@/graphql/client"; import { DASHBOARD_CONFIG, - DASHBOARD_COURSE_DATA, DASHBOARD_COURSE_SESSION_PROGRESS, DASHBOARD_COURSE_STATISTICS, + DASHBOARD_MENTOR_COMPETENCE_SUMMARY, } from "@/graphql/queries"; import { itGetCached } from "@/fetchHelpers"; import type { + AssignmentsStatisticsType, CourseProgressType, CourseStatisticsType, DashboardConfigType, @@ -120,18 +121,18 @@ export const fetchDashboardConfig = async (): Promise => { +): Promise => { try { - const res = await graphqlClient.query(DASHBOARD_COURSE_DATA, { + const res = await graphqlClient.query(DASHBOARD_MENTOR_COMPETENCE_SUMMARY, { courseId, }); if (res.error) { console.error("Error fetching data for course ID:", courseId, res.error); } - return res.data?.course_progress || null; + return res.data?.mentor_course_statistics?.assignments || null; } catch (error) { console.error(`Error fetching data for course ID: ${courseId}`, error); return null; @@ -145,3 +146,15 @@ export async function fetchDashboardPersons() { export async function fetchDashboardConfigv2() { return await itGetCached("/api/dashboard/config/"); } + +export async function fetchMenteeCount(courseId: string) { + return await itGetCached<{ mentee_count: number }>( + `/api/dashboard/course/${courseId}/mentees/` + ); +} + +export async function fetchOpenTasksCount(courseId: string) { + return await itGetCached<{ open_task_count: number }>( + `/api/dashboard/course/${courseId}/open_tasks/` + ); +} diff --git a/server/config/urls.py b/server/config/urls.py index b1a3ba78..582eced0 100644 --- a/server/config/urls.py +++ b/server/config/urls.py @@ -10,6 +10,9 @@ from django.views import defaults as default_views from django.views.decorators.csrf import csrf_exempt from django_ratelimit.exceptions import Ratelimited from graphene_django.views import GraphQLView +from wagtail import urls as wagtail_urls +from wagtail.admin import urls as wagtailadmin_urls +from wagtail.documents import urls as media_library_urls from vbv_lernwelt.api.directory import list_entities from vbv_lernwelt.api.user import get_profile, me_user_view, post_avatar @@ -39,7 +42,8 @@ from vbv_lernwelt.course.views import ( request_course_completion_for_user, ) from vbv_lernwelt.course_session.views import get_course_session_documents -from vbv_lernwelt.dashboard.views import get_dashboard_config, get_dashboard_persons +from vbv_lernwelt.dashboard.views import get_dashboard_config, get_dashboard_persons, get_mentee_count, \ + get_mentor_open_tasks_count from vbv_lernwelt.edoniq_test.views import ( export_students, export_students_and_trainers, @@ -58,9 +62,6 @@ from vbv_lernwelt.importer.views import ( ) from vbv_lernwelt.media_files.views import user_image from vbv_lernwelt.notify.views import email_notification_settings -from wagtail import urls as wagtail_urls -from wagtail.admin import urls as wagtailadmin_urls -from wagtail.documents import urls as media_library_urls class SignedIntConverter(IntConverter): @@ -120,6 +121,9 @@ urlpatterns = [ # dashboard path(r"api/dashboard/persons/", get_dashboard_persons, name="get_dashboard_persons"), path(r"api/dashboard/config/", get_dashboard_config, name="get_dashboard_config"), + path(r"api/dashboard/course//mentees/", get_mentee_count, name="get_mentee_count"), + path(r"api/dashboard/course//open_tasks/", get_mentor_open_tasks_count, + name="get_mentor_open_tasks_count"), # course path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"), diff --git a/server/vbv_lernwelt/dashboard/views.py b/server/vbv_lernwelt/dashboard/views.py index 1b9ec323..7b489862 100644 --- a/server/vbv_lernwelt/dashboard/views.py +++ b/server/vbv_lernwelt/dashboard/views.py @@ -6,11 +6,13 @@ from rest_framework.decorators import api_view from rest_framework.exceptions import PermissionDenied from rest_framework.response import Response +from vbv_lernwelt.assignment.models import AssignmentCompletion, AssignmentCompletionStatus from vbv_lernwelt.core.models import User -from vbv_lernwelt.course.models import CourseSession, CourseSessionUser +from vbv_lernwelt.course.models import CourseSession, CourseSessionUser, CourseConfiguration from vbv_lernwelt.course.views import logger from vbv_lernwelt.course_session_group.models import CourseSessionGroup from vbv_lernwelt.learning_mentor.models import LearningMentor +from vbv_lernwelt.self_evaluation_feedback.models import SelfEvaluationFeedback class WidgetType(Enum): @@ -242,7 +244,7 @@ def get_widgets_for_course( return widgets -def get_role_and_mentor_key( +def get_role_key_and_mentor( course_sessions: List[CourseSessionWithRoles], is_uk: bool, is_vv: bool ) -> tuple[RoleKeyType, bool]: roles = set() @@ -266,7 +268,7 @@ def get_role_and_mentor_key( return role, is_mentor -def sort_course_sessions_by_course( +def collect_course_sessions_by_course( course_sessions: List[CourseSessionWithRoles], ) -> dict: course_sessions_by_course = {} @@ -298,11 +300,11 @@ def get_course_config( course_sessions: List[CourseSessionWithRoles], ) -> List[CourseConfig]: course_configs = [] - cs_by_course = sort_course_sessions_by_course(course_sessions) + cs_by_course = collect_course_sessions_by_course(course_sessions) for _id, cs_in_course in cs_by_course.items(): is_uk = cs_in_course[0].course.configuration.is_uk is_vv = cs_in_course[0].course.configuration.is_vv - role_key, is_mentor = get_role_and_mentor_key(cs_in_course, is_uk, is_vv) + role_key, is_mentor = get_role_key_and_mentor(cs_in_course, is_uk, is_vv) session_to_continue = get_newest_cs(cs_in_course) course_configs.append( CourseConfig( @@ -339,3 +341,54 @@ def get_dashboard_config(request): except Exception as e: logger.error(e, exc_info=True) return Response({"error": str(e)}, status=404) + + +@api_view(["GET"]) +def get_mentee_count(request, course_id: str): + try: + count = CourseSessionUser.objects.filter( + participants__mentor=request.user, course_session__course__id=course_id + ).count() + + return Response( + status=200, + data={"mentee_count": count}, + ) + except PermissionDenied as e: + raise e + except Exception as e: + logger.error(e, exc_info=True) + return Response({"error": str(e)}, status=404) + + +@api_view(["GET"]) +def get_mentor_open_tasks_count(request, course_id: str): + try: + open_assigment_count = 0 + open_feedback_count = 0 + + course_configuration = CourseConfiguration.objects.get(course_id=course_id) + + if course_configuration.is_vv: + open_assigment_count = AssignmentCompletion.objects.filter( + course_session__course__id=course_id, + completion_status=AssignmentCompletionStatus.SUBMITTED.value, + evaluation_user=request.user, # noqa + assignment_user__coursesessionuser__participants__mentor=request.user, + ).count() + + open_feedback_count = SelfEvaluationFeedback.objects.filter( + feedback_provider_user=request.user, # noqa + feedback_requester_user__coursesessionuser__participants__mentor=request.user, + feedback_submitted=False, + ).count() + + return Response( + status=200, + data={"open_task_count": open_assigment_count + open_feedback_count}, + ) + except PermissionDenied as e: + raise e + except Exception as e: + logger.error(e, exc_info=True) + return Response({"error": str(e)}, status=404)