From 6edb5be0930d588c57a85553e53420c32331ba61 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Tue, 2 Apr 2024 13:03:29 +0200 Subject: [PATCH 01/82] WIP: Add own components --- .../dashboard/CompetenceSummary.vue | 36 +++++++++++++ .../src/components/dashboard/CoursePanel.vue | 50 +++++++++++++++++++ client/src/gql/gql.ts | 5 ++ client/src/gql/graphql.ts | 28 +++++++++-- client/src/gql/schema.graphql | 14 +++++- client/src/gql/typenames.ts | 1 + client/src/graphql/queries.ts | 11 ++++ client/src/pages/dashboard/DashboardPage.vue | 7 +++ client/src/router/guards.ts | 2 + client/src/router/index.ts | 21 +++++++- client/src/services/dashboard.ts | 19 +++++++ .../vbv_lernwelt/dashboard/graphql/queries.py | 50 ++++++++++++++++++- .../dashboard/graphql/types/dashboard.py | 18 ++++++- 13 files changed, 252 insertions(+), 10 deletions(-) create mode 100644 client/src/components/dashboard/CompetenceSummary.vue create mode 100644 client/src/components/dashboard/CoursePanel.vue diff --git a/client/src/components/dashboard/CompetenceSummary.vue b/client/src/components/dashboard/CompetenceSummary.vue new file mode 100644 index 00000000..ba701185 --- /dev/null +++ b/client/src/components/dashboard/CompetenceSummary.vue @@ -0,0 +1,36 @@ + + + diff --git a/client/src/components/dashboard/CoursePanel.vue b/client/src/components/dashboard/CoursePanel.vue new file mode 100644 index 00000000..19cadce3 --- /dev/null +++ b/client/src/components/dashboard/CoursePanel.vue @@ -0,0 +1,50 @@ + + + diff --git a/client/src/gql/gql.ts b/client/src/gql/gql.ts index eb6023e4..66a37920 100644 --- a/client/src/gql/gql.ts +++ b/client/src/gql/gql.ts @@ -23,6 +23,7 @@ const documents = { "\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.CourseQueryDocument, "\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n }\n": types.DashboardConfigDocument, "\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 widgets\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 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, }; @@ -81,6 +82,10 @@ export function graphql(source: "\n query dashboardConfig {\n dashboard_conf * 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 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"): (typeof 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"]; +/** + * 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 dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n widgets\n }\n }\n"): (typeof documents)["\n query dashboardCourseData($courseId: ID!) {\n course_progress(course_id: $courseId) {\n _id\n course_id\n session_to_continue_id\n widgets\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 e6086ccb..37d161ab 100644 --- a/client/src/gql/graphql.ts +++ b/client/src/gql/graphql.ts @@ -361,10 +361,11 @@ export type CoursePageInterface = { export type CourseProgressType = { __typename?: 'CourseProgressType'; _id: Scalars['ID']['output']; - assignment: ProgressDashboardAssignmentType; - competence: ProgressDashboardCompetenceType; + assignment?: Maybe; + competence?: Maybe; course_id: Scalars['ID']['output']; session_to_continue_id?: Maybe; + widgets: Array; }; export type CourseSessionAssignmentObjectType = { @@ -907,6 +908,7 @@ export type Query = { course_session_attendance_course?: Maybe; course_statistics?: Maybe; dashboard_config: Array; + dashboard_data?: Maybe; learning_content_assignment?: Maybe; learning_content_attendance_course?: Maybe; learning_content_document_list?: Maybe; @@ -978,6 +980,11 @@ export type QueryCourseStatisticsArgs = { }; +export type QueryDashboardDataArgs = { + course_id: Scalars['ID']['input']; +}; + + export type QueryLearningPathArgs = { course_id?: InputMaybe; course_slug?: InputMaybe; @@ -1046,6 +1053,13 @@ export type UserObjectType = { username: Scalars['String']['output']; }; +export type WidgetType = + | 'COMPETENCE_CERTIFICATE_WIDGET' + | 'COMPETENCE_WIDGET' + | 'MENTEE_WIDGET' + | 'MENTOR_TASKS_WIDGET' + | 'PROGRESS_WIDGET'; + export type AttendanceCheckMutationMutationVariables = Exact<{ attendanceCourseId: Scalars['ID']['input']; attendanceUserList: Array> | InputMaybe; @@ -1278,7 +1292,14 @@ export type DashboardProgressQueryVariables = Exact<{ }>; -export type DashboardProgressQuery = { __typename?: 'Query', course_progress?: { __typename?: 'CourseProgressType', _id: string, course_id: string, session_to_continue_id?: string | null, competence: { __typename?: 'ProgressDashboardCompetenceType', _id: string, total_count: number, success_count: number, fail_count: number }, assignment: { __typename?: 'ProgressDashboardAssignmentType', _id: string, total_count: number, points_max_count: number, points_achieved_count: number } } | null }; +export type DashboardProgressQuery = { __typename?: 'Query', course_progress?: { __typename?: 'CourseProgressType', _id: string, course_id: string, session_to_continue_id?: string | null, competence?: { __typename?: 'ProgressDashboardCompetenceType', _id: string, total_count: number, success_count: number, fail_count: number } | null, assignment?: { __typename?: 'ProgressDashboardAssignmentType', _id: string, total_count: number, points_max_count: number, points_achieved_count: number } | null } | null }; + +export type DashboardCourseDataQueryVariables = Exact<{ + courseId: Scalars['ID']['input']; +}>; + + +export type DashboardCourseDataQuery = { __typename?: 'Query', course_progress?: { __typename?: 'CourseProgressType', _id: string, course_id: string, session_to_continue_id?: string | null, widgets: Array } | null }; export type CourseStatisticsQueryVariables = Exact<{ courseId: Scalars['ID']['input']; @@ -1308,5 +1329,6 @@ export const CourseSessionDetailDocument = {"kind":"Document","definitions":[{"k export const CourseQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"category_name"}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"enable_circle_documents"}},{"kind":"Field","name":{"kind":"Name","value":"enable_learning_mentor"}},{"kind":"Field","name":{"kind":"Name","value":"enable_competence_certificates"}}]}},{"kind":"Field","name":{"kind":"Name","value":"action_competences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"performance_criteria"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_id"}},{"kind":"Field","name":{"kind":"Name","value":"learning_unit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"evaluate_url"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_path"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"topics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"is_visible"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"goals"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"learning_sequences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"learning_units"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"evaluate_url"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"performance_criteria"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_contents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"can_user_self_toggle_course_completion"}},{"kind":"Field","name":{"kind":"Name","value":"content_url"}},{"kind":"Field","name":{"kind":"Name","value":"minutes"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentAssignmentObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"content_assignment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentEdoniqTestObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"checkbox_text"}},{"kind":"Field","name":{"kind":"Name","value":"has_extended_time_test"}},{"kind":"Field","name":{"kind":"Name","value":"content_assignment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentRichTextObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}}]}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode; export const DashboardConfigDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"dashboardConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dashboard_config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dashboard_type"}},{"kind":"Field","name":{"kind":"Name","value":"course_configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"enable_circle_documents"}},{"kind":"Field","name":{"kind":"Name","value":"enable_learning_mentor"}},{"kind":"Field","name":{"kind":"Name","value":"enable_competence_certificates"}}]}}]}}]}}]} as unknown as DocumentNode; 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"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"}}]}}]}}]} 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 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/gql/schema.graphql b/client/src/gql/schema.graphql index 4c421b9b..7bda685a 100644 --- a/client/src/gql/schema.graphql +++ b/client/src/gql/schema.graphql @@ -2,6 +2,7 @@ type Query { course_statistics(course_id: ID!): CourseStatisticsType course_progress(course_id: ID!): CourseProgressType dashboard_config: [DashboardConfigType!]! + dashboard_data(course_id: ID!): DashboardConfigType learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType course_session_attendance_course(id: ID!, assignment_user_id: ID): CourseSessionAttendanceCourseObjectType course(id: ID, slug: String): CourseObjectType @@ -177,8 +178,9 @@ type CourseProgressType { _id: ID! course_id: ID! session_to_continue_id: ID - competence: ProgressDashboardCompetenceType! - assignment: ProgressDashboardAssignmentType! + competence: ProgressDashboardCompetenceType + assignment: ProgressDashboardAssignmentType + widgets: [WidgetType!]! } type ProgressDashboardCompetenceType { @@ -195,6 +197,14 @@ type ProgressDashboardAssignmentType { points_achieved_count: Int! } +enum WidgetType { + PROGRESS_WIDGET + COMPETENCE_WIDGET + MENTEE_WIDGET + MENTOR_TASKS_WIDGET + COMPETENCE_CERTIFICATE_WIDGET +} + type DashboardConfigType { id: ID! name: String! diff --git a/client/src/gql/typenames.ts b/client/src/gql/typenames.ts index 5fb963dc..732cb885 100644 --- a/client/src/gql/typenames.ts +++ b/client/src/gql/typenames.ts @@ -84,3 +84,4 @@ export const String = "String"; export const TopicObjectType = "TopicObjectType"; export const UUID = "UUID"; export const UserObjectType = "UserObjectType"; +export const WidgetType = "WidgetType"; diff --git a/client/src/graphql/queries.ts b/client/src/graphql/queries.ts index df6f5259..15dc2766 100644 --- a/client/src/graphql/queries.ts +++ b/client/src/graphql/queries.ts @@ -331,6 +331,17 @@ export const DASHBOARD_COURSE_SESSION_PROGRESS = graphql(` } `); +export const DASHBOARD_COURSE_DATA = graphql(` + query dashboardCourseData($courseId: ID!) { + course_progress(course_id: $courseId) { + _id + course_id + session_to_continue_id + widgets + } + } +`); + export const DASHBOARD_COURSE_STATISTICS = graphql(` query courseStatistics($courseId: ID!) { course_statistics(course_id: $courseId) { diff --git a/client/src/pages/dashboard/DashboardPage.vue b/client/src/pages/dashboard/DashboardPage.vue index a882818e..cbe936fd 100644 --- a/client/src/pages/dashboard/DashboardPage.vue +++ b/client/src/pages/dashboard/DashboardPage.vue @@ -12,6 +12,7 @@ import LoadingSpinner from "@/components/ui/LoadingSpinner.vue"; import CourseDetailDates from "@/components/dashboard/CourseDetailDates.vue"; import NoCourseSession from "@/components/dashboard/NoCourseSession.vue"; import MentorPage from "@/pages/dashboard/MentorPage.vue"; +import CoursePanel from "@/components/dashboard/CoursePanel.vue"; const dashboardStore = useDashboardStore(); @@ -50,6 +51,12 @@ onMounted(dashboardStore.loadDashboardDetails); > +
    +
  • +

    {{ config }}

    + +
  • +
diff --git a/client/src/router/guards.ts b/client/src/router/guards.ts index 089ac20a..8ee4825a 100644 --- a/client/src/router/guards.ts +++ b/client/src/router/guards.ts @@ -62,6 +62,7 @@ export async function handleCurrentCourseSession(to: RouteLocationNormalized) { courseSessionsStore._currentCourseSlug = ""; } if (!courseSessionsStore.loaded) { + console.log("handleCurrentCourseSession: loadCourseSessionsData"); await courseSessionsStore.loadCourseSessionsData(); } } @@ -77,6 +78,7 @@ export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalize if (userStore.loggedIn) { const courseSessionsStore = useCourseSessionsStore(); if (!courseSessionsStore.loaded) { + console.log("handleCourseSessionAsQueryParam: loadCourseSessionsData"); await courseSessionsStore.loadCourseSessionsData(); } diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 52a39878..14c9eb43 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -387,8 +387,25 @@ router.beforeEach(updateLoggedIn); router.beforeEach(redirectToLoginIfRequired); // register after login hooks -router.beforeEach(handleCurrentCourseSession); -router.beforeEach(handleCourseSessionAsQueryParam); +router.beforeEach( + async (to, from) => + await ignoreGuardsForHomeRoute(to, from, handleCurrentCourseSession) +); + +router.beforeEach( + async (to, from) => + await ignoreGuardsForHomeRoute(to, from, handleCourseSessionAsQueryParam) +); + +async function ignoreGuardsForHomeRoute( + to: RouteLocationNormalized, + from: RouteLocationNormalized, + guard: NavigationGuardNext +) { + if (to.name !== "home") { + return await guard(to, from); + } +} router.beforeEach(addToHistory); diff --git a/client/src/services/dashboard.ts b/client/src/services/dashboard.ts index a320774f..b4232d64 100644 --- a/client/src/services/dashboard.ts +++ b/client/src/services/dashboard.ts @@ -1,6 +1,7 @@ import { graphqlClient } from "@/graphql/client"; import { DASHBOARD_CONFIG, + DASHBOARD_COURSE_DATA, DASHBOARD_COURSE_SESSION_PROGRESS, DASHBOARD_COURSE_STATISTICS, } from "@/graphql/queries"; @@ -61,3 +62,21 @@ export const fetchDashboardConfig = async (): Promise => { + try { + const res = await graphqlClient.query(DASHBOARD_COURSE_DATA, { + courseId, + }); + + if (res.error) { + console.error("Error fetching data for course ID:", courseId, res.error); + } + return res.data?.course_progress || null; + } catch (error) { + console.error(`Error fetching data for course ID: ${courseId}`, error); + return null; + } +}; diff --git a/server/vbv_lernwelt/dashboard/graphql/queries.py b/server/vbv_lernwelt/dashboard/graphql/queries.py index 38c3c71f..72ee4234 100644 --- a/server/vbv_lernwelt/dashboard/graphql/queries.py +++ b/server/vbv_lernwelt/dashboard/graphql/queries.py @@ -17,6 +17,7 @@ from vbv_lernwelt.dashboard.graphql.types.dashboard import ( DashboardType, ProgressDashboardAssignmentType, ProgressDashboardCompetenceType, + WidgetType, ) from vbv_lernwelt.iam.permissions import ( can_view_course_session, @@ -39,6 +40,10 @@ class DashboardQuery(graphene.ObjectType): graphene.NonNull(DashboardConfigType), required=True ) + dashboard_data = graphene.Field( + DashboardConfigType, course_id=graphene.ID(required=True) + ) + def resolve_course_statistics(root, info, course_id: str): # noqa user = info.context.user course = Course.objects.get(id=course_id) @@ -164,8 +169,12 @@ class DashboardQuery(graphene.ObjectType): points_max_count=int(points_max_count), # noqa points_achieved_count=int(points_achieved_count), # noqa ), + widgets=get_widget_for_course(course_id, user), # noqa ) + def resolve_dashboard_data(root, info, course_id: str): + return get_widget_for_course(course_id, info.context.user) + def get_user_statistics_dashboards(user: User) -> Tuple[List[Dict[str, str]], Set[int]]: course_ids = set() @@ -222,7 +231,6 @@ def get_user_course_session_dashboards( sessions of a course, but with varying permissions? -> We just show the simple list dashboard for now. """ - dashboards = [] course_ids = set() @@ -246,6 +254,7 @@ def get_user_course_session_dashboards( resolved_dashboard_type = DashboardType.SIMPLE_DASHBOARD elif course_role == CourseSessionUser.Role.MEMBER: resolved_dashboard_type = DashboardType.PROGRESS_DASHBOARD + else: # fallback: just go with simple list dashboard resolved_dashboard_type = DashboardType.SIMPLE_DASHBOARD @@ -263,3 +272,42 @@ def get_user_course_session_dashboards( ) return dashboards, course_ids + + +def get_widget_for_course(course_id: str, user: User) -> List[WidgetType]: + widgets = [] + + course_sessions = CourseSession.objects.filter(course__id=course_id).prefetch_related( + "course", + "course__configuration", + ) + roles_by_course: Dict[Course, Set[DashboardType]] = {} + + learning_mentors = LearningMentor.objects.filter(mentor=user).values_list( + "course_session__course__id", "id" + ) + mentor_course_ids = set([mentor[0] for mentor in learning_mentors]) + + # duplicate code + for course_session in course_sessions: + if can_view_course_session(user=user, course_session=course_session): + role = CourseSessionUser.objects.get( + course_session=course_session, user=user + ).role + roles_by_course.setdefault(course_session.course, set()) + roles_by_course[course_session.course].add(role) + + for course, roles in roles_by_course.items(): + if len(roles) == 1: + course_role = roles.pop() + + # test widgets + if course_role == CourseSessionUser.Role.MEMBER: + widgets.append(WidgetType.PROGRESS_WIDGET) + widgets.append(WidgetType.COMPETENCE_WIDGET) + if course.configuration.enable_competence_certificates: + widgets.append(WidgetType.COMPETENCE_CERTIFICATE_WIDGET) + if course.id in mentor_course_ids: + widgets.append(WidgetType.MENTOR_TASKS_WIDGET) + # add KN if has KN + return widgets diff --git a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py index 2ad4d5de..35648f76 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py @@ -56,6 +56,14 @@ class DashboardType(Enum): MENTOR_DASHBOARD = "MentorDashboard" +class WidgetType(Enum): + PROGRESS_WIDGET = "ProgressWidget" + COMPETENCE_WIDGET = "CompetenceWidget" + MENTEE_WIDGET = "MenteeWidget" + MENTOR_TASKS_WIDGET = "MentorTasksWidget" + COMPETENCE_CERTIFICATE_WIDGET = "CompetenceCertificateWidget" + + class DashboardConfigType(graphene.ObjectType): id = graphene.ID(required=True) name = graphene.String(required=True) @@ -64,6 +72,11 @@ class DashboardConfigType(graphene.ObjectType): course_configuration = graphene.Field(CourseConfigurationObjectType, required=True) +class DashboardDataType(graphene.ObjectType): + id = graphene.ID(required=True) + widgets = graphene.List(graphene.NonNull(WidgetType), required=True) + + class ProgressDashboardCompetenceType(graphene.ObjectType): _id = graphene.ID(required=True) total_count = graphene.Int(required=True) @@ -82,8 +95,9 @@ class CourseProgressType(graphene.ObjectType): _id = graphene.ID(required=True) course_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) + competence = graphene.Field(ProgressDashboardCompetenceType, required=False) + assignment = graphene.Field(ProgressDashboardAssignmentType, required=False) + widgets = graphene.List(graphene.NonNull(WidgetType), required=True) class CourseStatisticsType(graphene.ObjectType): From 16ebd23edfd8fe12bd38081e3da60fc9e55e0ed5 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 3 Apr 2024 06:48:24 +0200 Subject: [PATCH 02/82] Add roles to course_session_permissions --- server/vbv_lernwelt/iam/permissions.py | 98 +++++++++++--------------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/server/vbv_lernwelt/iam/permissions.py b/server/vbv_lernwelt/iam/permissions.py index b2210c7c..b9b141b0 100644 --- a/server/vbv_lernwelt/iam/permissions.py +++ b/server/vbv_lernwelt/iam/permissions.py @@ -48,9 +48,9 @@ def has_course_session_preview(user, course_session_id: int): if is_course_session_member(user, course_session_id): return False - return is_learning_mentor(user, course_session_id) or is_course_session_expert( + return is_course_session_learning_mentor( user, course_session_id - ) + ) or is_course_session_expert(user, course_session_id) def has_media_library(user, course_session_id: int): @@ -66,7 +66,7 @@ def has_media_library(user, course_session_id: int): ).exists() -def is_learning_mentor(mentor: User, course_session_id: int): +def is_course_session_learning_mentor(mentor: User, course_session_id: int): course_session = CourseSession.objects.get(id=course_session_id) if course_session is None: @@ -92,6 +92,15 @@ def is_learning_mentor_for_user( ).exists() +def is_course_session_supervisor(user, course_session_id: int): + if user.is_superuser: + return True + + return CourseSessionGroup.objects.filter( + supervisor=user, course_session=course_session_id + ).exists() + + def is_course_session_expert(user, course_session_id: int): if user.is_superuser: return True @@ -244,40 +253,6 @@ def has_appointments(user: User, course_session_id: int) -> bool: return CourseSessionUser.objects.filter(course_session=course_session_id).exists() -def has_learning_mentor(user: User, course_session_id: int) -> bool: - course_session = CourseSession.objects.get(id=course_session_id) - - if course_session is None: - return False - - if not course_session.course.configuration.enable_learning_mentor: - return False - - if is_learning_mentor(user, course_session_id): - return True - - if is_course_session_member(user, course_session_id): - return True - - return False - - -def can_edit_mentors(user: User, course_session_id: int) -> bool: - if not has_learning_mentor(user, course_session_id): - return False - - # limit further, since has_learning_mentor is too broad - return is_course_session_member(user, course_session_id) - - -def can_guide_members(user: User, course_session_id: int) -> bool: - if not has_learning_mentor(user, course_session_id): - return False - - # limit further, since has_learning_mentor is too broad - return is_learning_mentor(user, course_session_id) - - def can_view_profile(user: User, profile_user: CourseSessionUser) -> bool: if user.is_superuser: return True @@ -311,29 +286,40 @@ def can_view_course_completions( ) -def can_complete_learning_content(user: User, course_session_id: int) -> bool: - return is_course_session_member( - user, course_session_id - ) or is_course_session_expert(user, course_session_id) - - def course_session_permissions(user: User, course_session_id: int) -> list[str]: + course_session = CourseSession.objects.get(id=course_session_id) + + is_supervisor = is_course_session_supervisor(user, course_session_id) + is_expert = is_course_session_expert(user, course_session_id) + is_member = is_course_session_member(user, course_session_id) + is_learning_mentor = is_course_session_learning_mentor(user, course_session_id) + + course_has_learning_mentor = ( + course_session.course.configuration.enable_learning_mentor + ) + has_learning_mentor = course_has_learning_mentor and ( + is_member or is_expert or is_learning_mentor + ) + return _action_list( { - "learning-mentor": has_learning_mentor(user, course_session_id), - "learning-mentor::edit-mentors": can_edit_mentors(user, course_session_id), - "learning-mentor::guide-members": can_guide_members( - user, course_session_id - ), + # roles + "is_supervisor": is_supervisor, + "is_expert": is_expert, + "is_member": is_member, + "is_learning_mentor": is_learning_mentor, + # actions + "learning-mentor": has_learning_mentor, + "learning-mentor::edit-mentors": has_learning_mentor and is_member, + "learning-mentor::guide-members": course_has_learning_mentor + and is_learning_mentor, "preview": has_course_session_preview(user, course_session_id), - "media-library": has_media_library(user, course_session_id), - "appointments": has_appointments(user, course_session_id), - "expert-cockpit": is_course_session_expert(user, course_session_id), - "learning-path": is_course_session_member(user, course_session_id), - "competence-navi": is_course_session_member(user, course_session_id), - "complete-learning-content": can_complete_learning_content( - user, course_session_id - ), + "media-library": is_supervisor or is_expert or is_member, + "appointments": is_supervisor or is_expert or is_member, + "expert-cockpit": is_expert, + "learning-path": is_member, + "competence-navi": is_member, + "complete-learning-content": is_expert or is_member, } ) From 2f77bf77341e05600d36ba4bb0469702f59199bf Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Wed, 3 Apr 2024 11:14:44 +0200 Subject: [PATCH 03/82] WIP: Add Praxisbildner switch, move code to type --- .../dashboard/AssignmentSummary.vue | 41 +++++ .../dashboard/CompetenceSummary.vue | 4 +- .../src/components/dashboard/CoursePanel.vue | 53 +++++-- client/src/gql/graphql.ts | 10 +- client/src/gql/schema.graphql | 5 +- client/src/pages/dashboard/DashboardPage.vue | 10 ++ .../vbv_lernwelt/dashboard/graphql/queries.py | 52 +------ .../dashboard/graphql/types/dashboard.py | 143 +++++++++++++++++- 8 files changed, 244 insertions(+), 74 deletions(-) create mode 100644 client/src/components/dashboard/AssignmentSummary.vue diff --git a/client/src/components/dashboard/AssignmentSummary.vue b/client/src/components/dashboard/AssignmentSummary.vue new file mode 100644 index 00000000..5ce253e3 --- /dev/null +++ b/client/src/components/dashboard/AssignmentSummary.vue @@ -0,0 +1,41 @@ + + + diff --git a/client/src/components/dashboard/CompetenceSummary.vue b/client/src/components/dashboard/CompetenceSummary.vue index ba701185..9d2a9164 100644 --- a/client/src/components/dashboard/CompetenceSummary.vue +++ b/client/src/components/dashboard/CompetenceSummary.vue @@ -18,8 +18,8 @@ const competenceCriteriaUrl = computed(() => { }); onMounted(async () => { - const some = await fetchProgressData(props.courseId); - competence.value = some.competence; + const data = await fetchProgressData(props.courseId); + competence.value = data.competence; }); diff --git a/client/src/components/dashboard/CoursePanel.vue b/client/src/components/dashboard/CoursePanel.vue index 19cadce3..82faeea2 100644 --- a/client/src/components/dashboard/CoursePanel.vue +++ b/client/src/components/dashboard/CoursePanel.vue @@ -1,10 +1,18 @@ + + diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 52a39878..78704b5e 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -60,6 +60,10 @@ const router = createRouter({ name: "home", component: DashboardPage, }, + { + path: "/dashboard/persons", + component: () => import("@/pages/dashboard/DashboardPersonsPage.vue"), + }, { path: "/course/:courseSlug/media", props: true, diff --git a/client/src/services/dashboard.ts b/client/src/services/dashboard.ts index a320774f..e628b7fe 100644 --- a/client/src/services/dashboard.ts +++ b/client/src/services/dashboard.ts @@ -5,12 +5,37 @@ import { DASHBOARD_COURSE_STATISTICS, } from "@/graphql/queries"; +import { itGetCached } from "@/fetchHelpers"; import type { CourseProgressType, CourseStatisticsType, DashboardConfigType, } from "@/gql/graphql"; +export type DashboardPersonRoleType = + | "SUPERVISOR" + | "EXPERT" + | "MEMBER" + | "LEARNING_MENTOR" + | "LEARNING_MENTEE"; + +export type DashboardPersonCourseSessionType = { + id: number; + session_title: string; + course_id: number; + course_title: string; + user_role: DashboardPersonRoleType; + my_role: DashboardPersonRoleType; +}; + +export type DashboardPersonType = { + user_id: string; + first_name: string; + last_name: string; + email: string; + course_sessions: DashboardPersonCourseSessionType[]; +}; + export const fetchStatisticData = async ( courseId: string ): Promise => { @@ -47,6 +72,7 @@ export const fetchProgressData = async ( return null; } }; + export const fetchDashboardConfig = async (): Promise => { try { const res = await graphqlClient.query(DASHBOARD_CONFIG, {}); @@ -61,3 +87,7 @@ export const fetchDashboardConfig = async (): Promise("/api/dashboard/persons/"); +} From 15d6baac81aa35b79997596f3cdd37c9ea7ba84b Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 4 Apr 2024 11:54:07 +0200 Subject: [PATCH 08/82] Display persons --- .../pages/dashboard/DashboardPersonsPage.vue | 55 ++++++++++++++++++- client/src/services/dashboard.ts | 2 + server/vbv_lernwelt/dashboard/views.py | 15 +++-- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/client/src/pages/dashboard/DashboardPersonsPage.vue b/client/src/pages/dashboard/DashboardPersonsPage.vue index 9cf37ccb..483b5091 100644 --- a/client/src/pages/dashboard/DashboardPersonsPage.vue +++ b/client/src/pages/dashboard/DashboardPersonsPage.vue @@ -15,7 +15,60 @@ const { loading: loadingPersons, dashboardPersons } = useDashboardPersons();
- {{ dashboardPersons }} +
+
+
+
+ +
+
+ {{ person.first_name }} + {{ person.last_name }} +
+
{{ person.email }}
+
+
+
+ +
+
+
+
+
{{ cs.course_title }}
+
{{ cs.session_title }}
+
+
+
{{ cs.user_role }}
+
+
+
action
+
+
+
+
+ + + + + + + + +
+
diff --git a/client/src/services/dashboard.ts b/client/src/services/dashboard.ts index e628b7fe..66c9f831 100644 --- a/client/src/services/dashboard.ts +++ b/client/src/services/dashboard.ts @@ -26,6 +26,8 @@ export type DashboardPersonCourseSessionType = { course_title: string; user_role: DashboardPersonRoleType; my_role: DashboardPersonRoleType; + is_uk: boolean; + is_vv: boolean; }; export type DashboardPersonType = { diff --git a/server/vbv_lernwelt/dashboard/views.py b/server/vbv_lernwelt/dashboard/views.py index 7c5342de..123bb354 100644 --- a/server/vbv_lernwelt/dashboard/views.py +++ b/server/vbv_lernwelt/dashboard/views.py @@ -101,6 +101,8 @@ def get_dashboard_persons(request): "course_title": cs.course.title, "user_role": csu.role, "my_role": my_role, + "is_uk": cs.course.configuration.is_uk, + "is_vv": cs.course.configuration.is_vv, } ], } @@ -120,6 +122,8 @@ def get_dashboard_persons(request): "course_title": cs.course.title, "user_role": "LEARNING_MENTEE", "my_role": "LEARNING_MENTOR", + "is_uk": cs.course.configuration.is_uk, + "is_vv": cs.course.configuration.is_vv, } if participant.user.id not in result_persons: @@ -141,13 +145,16 @@ def get_dashboard_persons(request): participants__user=request.user ).prefetch_related("mentor", "course_session") for mentor_relation in mentor_relation_qs: + cs = mentor_relation.course_session course_session_entry = { - "id": mentor_relation.course_session.id, - "session_title": mentor_relation.course_session.title, - "course_id": mentor_relation.course_session.course.id, - "course_title": mentor_relation.course_session.course.title, + "id": cs.id, + "session_title": cs.title, + "course_id": cs.course.id, + "course_title": cs.course.title, "user_role": "LEARNING_MENTOR", "my_role": "LEARNING_MENTEE", + "is_uk": cs.course.configuration.is_uk, + "is_vv": cs.course.configuration.is_vv, } if mentor_relation.mentor.id not in result_persons: From 91ee3e605b9c5a7be6a8574fe6a6954b4943d280 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 4 Apr 2024 12:17:40 +0200 Subject: [PATCH 09/82] Add action link --- client/src/composables.ts | 4 ++-- .../src/pages/dashboard/DashboardPersonsPage.vue | 16 +++++++++++++++- client/src/router/guards.ts | 2 +- client/src/services/dashboard.ts | 1 + server/vbv_lernwelt/dashboard/views.py | 5 ++++- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/client/src/composables.ts b/client/src/composables.ts index cbf6dc97..d778f90c 100644 --- a/client/src/composables.ts +++ b/client/src/composables.ts @@ -5,7 +5,7 @@ import { COURSE_QUERY, COURSE_SESSION_DETAIL_QUERY } from "@/graphql/queries"; import { circleFlatChildren, circleFlatLearningContents, - circleFlatLearningUnits + circleFlatLearningUnits, } from "@/services/circle"; import type { DashboardPersonType } from "@/services/dashboard"; import { fetchDashboardPersons } from "@/services/dashboard"; @@ -25,7 +25,7 @@ import type { LearningMentor, LearningPathType, LearningUnitPerformanceCriteria, - PerformanceCriteria + PerformanceCriteria, } from "@/types"; import { useQuery } from "@urql/vue"; import orderBy from "lodash/orderBy"; diff --git a/client/src/pages/dashboard/DashboardPersonsPage.vue b/client/src/pages/dashboard/DashboardPersonsPage.vue index 483b5091..6813947c 100644 --- a/client/src/pages/dashboard/DashboardPersonsPage.vue +++ b/client/src/pages/dashboard/DashboardPersonsPage.vue @@ -54,7 +54,21 @@ const { loading: loadingPersons, dashboardPersons } = useDashboardPersons();
{{ cs.user_role }}
-
action
+
+ + {{ $t("a.Profil anzeigen") }} + +
diff --git a/client/src/router/guards.ts b/client/src/router/guards.ts index 089ac20a..b60e527f 100644 --- a/client/src/router/guards.ts +++ b/client/src/router/guards.ts @@ -89,7 +89,7 @@ export async function handleCourseSessionAsQueryParam(to: RouteLocationNormalize return { path: to.path, query: restOfQuery, - replace: true, + // replace: true, }; } else { // courseSessionId is invalid for current user -> redirect to home diff --git a/client/src/services/dashboard.ts b/client/src/services/dashboard.ts index 66c9f831..a1ccaa0e 100644 --- a/client/src/services/dashboard.ts +++ b/client/src/services/dashboard.ts @@ -24,6 +24,7 @@ export type DashboardPersonCourseSessionType = { session_title: string; course_id: number; course_title: string; + course_slug: string; user_role: DashboardPersonRoleType; my_role: DashboardPersonRoleType; is_uk: boolean; diff --git a/server/vbv_lernwelt/dashboard/views.py b/server/vbv_lernwelt/dashboard/views.py index 123bb354..0f44b479 100644 --- a/server/vbv_lernwelt/dashboard/views.py +++ b/server/vbv_lernwelt/dashboard/views.py @@ -99,6 +99,7 @@ def get_dashboard_persons(request): "session_title": cs.title, "course_id": cs.course.id, "course_title": cs.course.title, + "course_slug": cs.course.slug, "user_role": csu.role, "my_role": my_role, "is_uk": cs.course.configuration.is_uk, @@ -117,9 +118,10 @@ def get_dashboard_persons(request): for participant in lm.participants.all(): course_session_entry = { "id": cs.id, - "course_id": cs.course.id, "session_title": cs.title, + "course_id": cs.course.id, "course_title": cs.course.title, + "course_slug": cs.course.slug, "user_role": "LEARNING_MENTEE", "my_role": "LEARNING_MENTOR", "is_uk": cs.course.configuration.is_uk, @@ -151,6 +153,7 @@ def get_dashboard_persons(request): "session_title": cs.title, "course_id": cs.course.id, "course_title": cs.course.title, + "course_slug": cs.course.slug, "user_role": "LEARNING_MENTOR", "my_role": "LEARNING_MENTEE", "is_uk": cs.course.configuration.is_uk, From 7473923dacbc02bd581b235a4fa6b72815a6c6c5 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 5 Apr 2024 15:19:14 +0200 Subject: [PATCH 10/82] Action link not for all --- client/src/pages/dashboard/DashboardPersonsPage.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/src/pages/dashboard/DashboardPersonsPage.vue b/client/src/pages/dashboard/DashboardPersonsPage.vue index 6813947c..b4a96cc3 100644 --- a/client/src/pages/dashboard/DashboardPersonsPage.vue +++ b/client/src/pages/dashboard/DashboardPersonsPage.vue @@ -52,9 +52,17 @@ const { loading: loadingPersons, dashboardPersons } = useDashboardPersons();
{{ cs.user_role }}
+
my role: {{ cs.my_role }}
-
+

{{ courseName }}

+

{{ data.ui_config.role_key }}

; course_id: Scalars['ID']['output']; session_to_continue_id?: Maybe; - widgets: Array; + ui_config?: Maybe; }; export type CourseSessionAssignmentObjectType = { @@ -453,6 +453,7 @@ export type CourseStatisticsType = { course_slug: Scalars['String']['output']; course_title: Scalars['String']['output']; feedback_responses: FeedbackStatisticsResponsesType; + user_selection_ids?: Maybe>>; }; export type DashboardConfigType = { @@ -922,6 +923,7 @@ export type Query = { learning_content_test?: Maybe; learning_content_video?: Maybe; learning_path?: Maybe; + mentor_course_statistics?: Maybe; }; @@ -987,6 +989,18 @@ export type QueryLearningPathArgs = { slug?: InputMaybe; }; + +export type QueryMentorCourseStatisticsArgs = { + course_id: Scalars['ID']['input']; +}; + +export type RoleKeyType = + | 'MEMBER' + | 'MENTOR_UK' + | 'MENTOR_VV' + | 'SUPERVISOR' + | 'TRAINER'; + export type SendFeedbackMutation = { __typename?: 'SendFeedbackMutation'; /** May contain more than one error for same field. */ @@ -1036,6 +1050,14 @@ export type TopicObjectType = CoursePageInterface & { translation_key: Scalars['String']['output']; }; +export type UiConfigType = { + __typename?: 'UIConfigType'; + _id: Scalars['ID']['output']; + has_preview?: Maybe; + role_key: RoleKeyType; + widgets: Array; +}; + export type UserObjectType = { __typename?: 'UserObjectType'; avatar_url?: Maybe; @@ -1295,7 +1317,7 @@ export type DashboardCourseDataQueryVariables = Exact<{ }>; -export type DashboardCourseDataQuery = { __typename?: 'Query', course_progress?: { __typename?: 'CourseProgressType', _id: string, course_id: string, session_to_continue_id?: string | null, widgets: Array } | null }; +export type DashboardCourseDataQuery = { __typename?: 'Query', course_progress?: { __typename?: 'CourseProgressType', _id: string, course_id: string, session_to_continue_id?: string | null, ui_config?: { __typename?: 'UIConfigType', _id: string, role_key: RoleKeyType, widgets: Array, has_preview?: boolean | null } | null } | null }; export type CourseStatisticsQueryVariables = Exact<{ courseId: Scalars['ID']['input']; @@ -1325,6 +1347,6 @@ export const CourseSessionDetailDocument = {"kind":"Document","definitions":[{"k export const CourseQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"courseQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"course"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"category_name"}},{"kind":"Field","name":{"kind":"Name","value":"configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"enable_circle_documents"}},{"kind":"Field","name":{"kind":"Name","value":"enable_learning_mentor"}},{"kind":"Field","name":{"kind":"Name","value":"enable_competence_certificates"}}]}},{"kind":"Field","name":{"kind":"Name","value":"action_competences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"performance_criteria"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"competence_id"}},{"kind":"Field","name":{"kind":"Name","value":"learning_unit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"evaluate_url"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_path"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"topics"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"is_visible"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"circles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"goals"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"learning_sequences"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"learning_units"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"evaluate_url"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"Field","name":{"kind":"Name","value":"performance_criteria"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"learning_contents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"can_user_self_toggle_course_completion"}},{"kind":"Field","name":{"kind":"Name","value":"content_url"}},{"kind":"Field","name":{"kind":"Name","value":"minutes"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentAssignmentObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}},{"kind":"Field","name":{"kind":"Name","value":"content_assignment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentEdoniqTestObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"checkbox_text"}},{"kind":"Field","name":{"kind":"Name","value":"has_extended_time_test"}},{"kind":"Field","name":{"kind":"Name","value":"content_assignment"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"assignment_type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"competence_certificate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CoursePageFields"}}]}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LearningContentRichTextObjectType"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}}]}}]}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CoursePageFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CoursePageInterface"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"content_type"}},{"kind":"Field","name":{"kind":"Name","value":"frontend_url"}}]}}]} as unknown as DocumentNode; export const DashboardConfigDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"dashboardConfig"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dashboard_config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"dashboard_type"}},{"kind":"Field","name":{"kind":"Name","value":"course_configuration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"enable_circle_documents"}},{"kind":"Field","name":{"kind":"Name","value":"enable_learning_mentor"}},{"kind":"Field","name":{"kind":"Name","value":"enable_competence_certificates"}}]}}]}}]}}]} as unknown as DocumentNode; 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"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"}}]}}]}}]} 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"}},{"kind":"Field","name":{"kind":"Name","value":"ui_config"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"_id"}},{"kind":"Field","name":{"kind":"Name","value":"role_key"}},{"kind":"Field","name":{"kind":"Name","value":"widgets"}},{"kind":"Field","name":{"kind":"Name","value":"has_preview"}}]}}]}}]}}]} 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 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/gql/schema.graphql b/client/src/gql/schema.graphql index fafe2c15..cb6827be 100644 --- a/client/src/gql/schema.graphql +++ b/client/src/gql/schema.graphql @@ -1,5 +1,6 @@ type Query { course_statistics(course_id: ID!): CourseStatisticsType + mentor_course_statistics(course_id: ID!): CourseStatisticsType course_progress(course_id: ID!): CourseProgressType dashboard_config: [DashboardConfigType!]! learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType @@ -31,6 +32,7 @@ type CourseStatisticsType { course_slug: String! course_session_properties: StatisticsCourseSessionPropertiesType! course_session_selection_ids: [ID]! + user_selection_ids: [ID] course_session_selection_metrics: StatisticsCourseSessionsSelectionMetricType! attendance_day_presences: AttendanceDayPresencesStatisticsType! feedback_responses: FeedbackStatisticsResponsesType! @@ -179,7 +181,7 @@ type CourseProgressType { session_to_continue_id: ID competence: ProgressDashboardCompetenceType assignment: ProgressDashboardAssignmentType - widgets: [WidgetType!]! + ui_config: UIConfigType } type ProgressDashboardCompetenceType { @@ -196,6 +198,21 @@ type ProgressDashboardAssignmentType { points_achieved_count: Int! } +type UIConfigType { + _id: ID! + role_key: RoleKeyType! + widgets: [WidgetType!]! + has_preview: Boolean +} + +enum RoleKeyType { + MEMBER + MENTOR_VV + MENTOR_UK + SUPERVISOR + TRAINER +} + enum WidgetType { PROGRESS_WIDGET COMPETENCE_WIDGET diff --git a/client/src/gql/typenames.ts b/client/src/gql/typenames.ts index 732cb885..026956b6 100644 --- a/client/src/gql/typenames.ts +++ b/client/src/gql/typenames.ts @@ -75,6 +75,7 @@ export const PresenceRecordStatisticsType = "PresenceRecordStatisticsType"; export const ProgressDashboardAssignmentType = "ProgressDashboardAssignmentType"; export const ProgressDashboardCompetenceType = "ProgressDashboardCompetenceType"; export const Query = "Query"; +export const RoleKeyType = "RoleKeyType"; export const SendFeedbackMutation = "SendFeedbackMutation"; export const StatisticsCircleDataType = "StatisticsCircleDataType"; export const StatisticsCourseSessionDataType = "StatisticsCourseSessionDataType"; @@ -82,6 +83,7 @@ export const StatisticsCourseSessionPropertiesType = "StatisticsCourseSessionPro export const StatisticsCourseSessionsSelectionMetricType = "StatisticsCourseSessionsSelectionMetricType"; export const String = "String"; export const TopicObjectType = "TopicObjectType"; +export const UIConfigType = "UIConfigType"; export const UUID = "UUID"; export const UserObjectType = "UserObjectType"; export const WidgetType = "WidgetType"; diff --git a/client/src/graphql/queries.ts b/client/src/graphql/queries.ts index 15dc2766..29f174da 100644 --- a/client/src/graphql/queries.ts +++ b/client/src/graphql/queries.ts @@ -337,7 +337,12 @@ export const DASHBOARD_COURSE_DATA = graphql(` _id course_id session_to_continue_id - widgets + ui_config { + _id + role_key + widgets + has_preview + } } } `); diff --git a/client/src/pages/dashboard/DashboardPage.vue b/client/src/pages/dashboard/DashboardPage.vue index 6031c402..8d57ac50 100644 --- a/client/src/pages/dashboard/DashboardPage.vue +++ b/client/src/pages/dashboard/DashboardPage.vue @@ -64,7 +64,7 @@ onMounted(dashboardStore.loadDashboardDetails); dashboardStore.currentDashboardConfig.dashboard_type === 'PRAXISBILDNER_DASHBOARD' " - course-config="dashboardStore.currentDashboardConfig" + :course-config="dashboardStore.currentDashboardConfig" /> AssignmentStatisticsSummary def get_assignment_completion_metrics( - course_session: CourseSession, assignment: vbv_lernwelt.assignment.models.Assignment + course_session: CourseSession, assignment: vbv_lernwelt.assignment.models.Assignment, + user_selection_ids: List[str] | None ) -> AssignmentCompletionMetricsType: course_session_users = CourseSessionUser.objects.filter( course_session=course_session, @@ -111,6 +112,7 @@ def get_assignment_completion_metrics( def create_record( course_session_assignment: CourseSessionAssignment | CourseSessionEdoniqTest, + user_selection_ids: List[str] | None ) -> AssignmentStatisticsRecordType: if isinstance(course_session_assignment, CourseSessionAssignment): due_date = course_session_assignment.submission_deadline @@ -133,6 +135,7 @@ def create_record( metrics=get_assignment_completion_metrics( # noqa course_session=course_session_assignment.course_session, # noqa assignment=learning_content.content_assignment, # noqa + user_selection_ids=user_selection_ids, # noqa ), details_url=due_date.url_expert, # noqa deadline=due_date.start, # noqa @@ -142,6 +145,7 @@ def create_record( def assignments( course_id: graphene.ID(required=True), course_session_selection_ids: graphene.List(graphene.ID), + user_selection_ids: List[str] | None = None, ) -> AssignmentsStatisticsType: course_sessions = CourseSession.objects.filter( id__in=course_session_selection_ids, @@ -156,14 +160,14 @@ def assignments( ], learning_content__content_assignment__competence_certificate__isnull=False, ): - record = create_record(course_session_assignment=csa) + record = create_record(course_session_assignment=csa, user_selection_ids=user_selection_ids) records.append(record) for cset in CourseSessionEdoniqTest.objects.filter( course_session=course_session, learning_content__content_assignment__competence_certificate__isnull=False, ): - record = create_record(course_session_assignment=cset) + record = create_record(course_session_assignment=cset, user_selection_ids=user_selection_ids) records.append(record) return AssignmentsStatisticsType( diff --git a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py index c070bc7d..38da733a 100644 --- a/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py +++ b/server/vbv_lernwelt/dashboard/graphql/types/dashboard.py @@ -8,6 +8,7 @@ from vbv_lernwelt.assignment.models import ( AssignmentCompletionStatus, ) from vbv_lernwelt.core.models import User +from vbv_lernwelt.course.consts import UK_COURSE_IDS from vbv_lernwelt.course.graphql.types import CourseConfigurationObjectType from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser from vbv_lernwelt.dashboard.graphql.types.assignment import ( @@ -78,6 +79,14 @@ class WidgetType(Enum): COMPETENCE_CERTIFICATE_WIDGET = "CompetenceCertificateWidget" +class RoleKeyType(Enum): + MEMBER = "Member" + MENTOR_VV = "MentorVV" + MENTOR_UK = "MentorUK" + SUPERVISOR = "Supervisor" + TRAINER = "Trainer" + + class DashboardConfigType(graphene.ObjectType): id = graphene.ID(required=True) name = graphene.String(required=True) @@ -86,11 +95,6 @@ class DashboardConfigType(graphene.ObjectType): course_configuration = graphene.Field(CourseConfigurationObjectType, required=True) -class DashboardDataType(graphene.ObjectType): - id = graphene.ID(required=True) - widgets = graphene.List(graphene.NonNull(WidgetType), required=True) - - class ProgressDashboardCompetenceType(graphene.ObjectType): _id = graphene.ID(required=True) total_count = graphene.Int(required=True) @@ -105,13 +109,20 @@ class ProgressDashboardAssignmentType(graphene.ObjectType): points_achieved_count = graphene.Int(required=True) +class UIConfigType(graphene.ObjectType): + _id = graphene.ID(required=True) + role_key = graphene.Field(RoleKeyType, required=True) + widgets = graphene.List(graphene.NonNull(WidgetType), required=True) + has_preview = graphene.Boolean(required=False) + + class CourseProgressType(graphene.ObjectType): _id = graphene.ID(required=True) course_id = graphene.ID(required=True) session_to_continue_id = graphene.ID(required=False) competence = graphene.Field(ProgressDashboardCompetenceType, required=False) assignment = graphene.Field(ProgressDashboardAssignmentType, required=False) - widgets = graphene.List(graphene.NonNull(WidgetType), required=True) + ui_config = graphene.Field(UIConfigType, required=False) def resolve__id(root, info): return info.context.course.id @@ -163,8 +174,8 @@ class CourseProgressType(graphene.ObjectType): fail_count=fail_total, # noqa ) - def resolve_widgets(root, info): - return get_widgets_for_course(info.context.course, info.context.user) + def resolve_ui_config(root, info): + return get_ui_config_for_course(info.context.course, info.context.user) def _get_newest_cs_and_cs_for_user( root, info, course_id: str, user: User @@ -203,6 +214,7 @@ class CourseStatisticsType(graphene.ObjectType): StatisticsCourseSessionPropertiesType, required=True ) course_session_selection_ids = graphene.List(graphene.ID, required=True) + user_selection_ids = graphene.List(graphene.ID, required=False) course_session_selection_metrics = graphene.Field( StatisticsCourseSessionsSelectionMetricType, required=True ) @@ -229,11 +241,14 @@ class CourseStatisticsType(graphene.ObjectType): ) def resolve_competences(root, info) -> CompetencesStatisticsType: + user_selection_ids = [str(user) for user in + root.user_selection_ids] if root.user_selection_ids else None # noqa records, success_total, fail_total = competences( course_slug=str(root.course_slug), course_session_selection_ids=[ str(cs) for cs in root.course_session_selection_ids # noqa ], + user_selection_ids=user_selection_ids, # noqa ) return CompetencesStatisticsType( _id=root._id, # noqa @@ -246,9 +261,12 @@ class CourseStatisticsType(graphene.ObjectType): ) def resolve_assignments(root, info) -> AssignmentsStatisticsType: + user_selection_ids = [str(user) for user in + root.user_selection_ids] if root.user_selection_ids else None # noqa return assignments( course_id=root.course_id, course_session_selection_ids=root.course_session_selection_ids, + user_selection_ids=user_selection_ids, ) def resolve_course_session_selection_metrics( @@ -320,8 +338,10 @@ class CourseStatisticsType(graphene.ObjectType): ) -def get_widgets_for_course(course: Course, user: User) -> List[WidgetType]: +def get_ui_config_for_course(course: Course, user: User) -> UIConfigType: widgets = [] + role_key = None + has_preview = False course_sessions = CourseSession.objects.filter( course__id=course.id, coursesessionuser__user=user @@ -346,20 +366,32 @@ def get_widgets_for_course(course: Course, user: User) -> List[WidgetType]: roles_by_course.setdefault(course_session.course, set()) roles_by_course[course_session.course].add(role) + # todo: use permissions for course, roles in roles_by_course.items(): if len(roles) == 1: course_role = roles.pop() # members if course_role == CourseSessionUser.Role.MEMBER: + role_key = RoleKeyType.MEMBER widgets.append(WidgetType.PROGRESS_WIDGET) widgets.append(WidgetType.COMPETENCE_WIDGET) if course.configuration.enable_competence_certificates: widgets.append(WidgetType.COMPETENCE_CERTIFICATE_WIDGET) # mentors if course.id in mentor_course_ids: + if not role_key: + role_key = RoleKeyType.MENTOR_UK if course.id in UK_COURSE_IDS else RoleKeyType.MENTOR_VV + has_preview = True widgets.append(WidgetType.MENTOR_TASKS_WIDGET) widgets.append(WidgetType.MENTOR_PERSON_WIDGET) if course.configuration.enable_competence_certificates: widgets.append(WidgetType.MENTOR_COMPETENCE_WIDGET) - return widgets + + # todo: supervisors + return UIConfigType( + _id=f"{course.id}_{user.id}", # noqa + role_key=role_key, # noqa, + widgets=widgets, # noqa + has_preview=has_preview, # noqa + ) From 4a982d8af2e9cfc2236dcd8745b4b880fa7a2243 Mon Sep 17 00:00:00 2001 From: Christian Cueni Date: Mon, 8 Apr 2024 12:47:55 +0200 Subject: [PATCH 12/82] WIP: Use REST endpoint --- .../src/components/dashboard/CoursePanel.vue | 69 ++++---- client/src/pages/dashboard/DashboardPage.vue | 20 ++- client/src/services/dashboard.ts | 32 ++++ server/config/urls.py | 5 +- .../vbv_lernwelt/dashboard/graphql/queries.py | 5 +- .../dashboard/graphql/types/assignment.py | 15 +- .../dashboard/graphql/types/dashboard.py | 20 ++- server/vbv_lernwelt/dashboard/views.py | 161 ++++++++++++++++-- 8 files changed, 258 insertions(+), 69 deletions(-) diff --git a/client/src/components/dashboard/CoursePanel.vue b/client/src/components/dashboard/CoursePanel.vue index a4926ed0..40d133ff 100644 --- a/client/src/components/dashboard/CoursePanel.vue +++ b/client/src/components/dashboard/CoursePanel.vue @@ -1,80 +1,71 @@