feat: add dashboard page variants
This commit is contained in:
parent
2908e3cbf0
commit
9d6a0a561b
|
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import DueDatesList from "@/components/dueDates/DueDatesList.vue";
|
||||||
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
|
|
||||||
|
const courseSessionsStore = useCourseSessionsStore();
|
||||||
|
const allDueDates = courseSessionsStore.allDueDates();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h3 class="mb-6">Termine</h3>
|
||||||
|
<DueDatesList
|
||||||
|
:due-dates="allDueDates"
|
||||||
|
:max-count="13"
|
||||||
|
:show-top-border="true"
|
||||||
|
:show-all-due-dates-link="true"
|
||||||
|
:show-bottom-border="true"
|
||||||
|
:show-course-session="true"
|
||||||
|
></DueDatesList>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -21,7 +21,8 @@ const documents = {
|
||||||
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
|
"\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
|
||||||
"\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument,
|
"\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument,
|
||||||
"\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\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 }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\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 courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\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 }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\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 name\n dashboard_type\n }\n }\n": types.DashboardConfigDocument,
|
"\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n }\n }\n": types.DashboardConfigDocument,
|
||||||
|
"\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n id\n session_to_continue_id\n }\n }\n": types.DashboardProgressDocument,
|
||||||
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\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,
|
"\n mutation SendFeedbackMutation(\n $courseSessionId: ID!\n $learningContentId: ID!\n $data: GenericScalar!\n $submitted: Boolean\n ) {\n send_feedback(\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -74,7 +75,11 @@ export function graphql(source: "\n query courseQuery($slug: String!) {\n co
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* 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 dashboardConfig {\n dashboard_config {\n id\n name\n dashboard_type\n }\n }\n"): (typeof documents)["\n query dashboardConfig {\n dashboard_config {\n id\n name\n dashboard_type\n }\n }\n"];
|
export function graphql(source: "\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n }\n }\n"): (typeof documents)["\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\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 dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n id\n session_to_continue_id\n }\n }\n"): (typeof documents)["\n query dashboardProgress($courseId: ID!) {\n course_progress(course_id: $courseId) {\n id\n session_to_continue_id\n }\n }\n"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,6 @@
|
||||||
type Query {
|
type Query {
|
||||||
course_statistics(course_id: ID!): CourseStatisticsType
|
course_statistics(course_id: ID!): CourseStatisticsType
|
||||||
|
course_progress(course_id: ID!): CourseProgressType
|
||||||
dashboard_config: [DashboardConfigType!]!
|
dashboard_config: [DashboardConfigType!]!
|
||||||
learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType
|
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_session_attendance_course(id: ID!, assignment_user_id: ID): CourseSessionAttendanceCourseObjectType
|
||||||
|
|
@ -150,9 +151,15 @@ type CompletionSummary {
|
||||||
fail_total: Int!
|
fail_total: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CourseProgressType {
|
||||||
|
id: ID!
|
||||||
|
session_to_continue_id: ID!
|
||||||
|
}
|
||||||
|
|
||||||
type DashboardConfigType {
|
type DashboardConfigType {
|
||||||
id: ID!
|
id: ID!
|
||||||
name: String!
|
name: String!
|
||||||
|
slug: String!
|
||||||
dashboard_type: DashboardType!
|
dashboard_type: DashboardType!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export const CoreUserLanguageChoices = "CoreUserLanguageChoices";
|
||||||
export const CourseCourseSessionUserRoleChoices = "CourseCourseSessionUserRoleChoices";
|
export const CourseCourseSessionUserRoleChoices = "CourseCourseSessionUserRoleChoices";
|
||||||
export const CourseObjectType = "CourseObjectType";
|
export const CourseObjectType = "CourseObjectType";
|
||||||
export const CoursePageInterface = "CoursePageInterface";
|
export const CoursePageInterface = "CoursePageInterface";
|
||||||
|
export const CourseProgressType = "CourseProgressType";
|
||||||
export const CourseSessionAssignmentObjectType = "CourseSessionAssignmentObjectType";
|
export const CourseSessionAssignmentObjectType = "CourseSessionAssignmentObjectType";
|
||||||
export const CourseSessionAttendanceCourseObjectType = "CourseSessionAttendanceCourseObjectType";
|
export const CourseSessionAttendanceCourseObjectType = "CourseSessionAttendanceCourseObjectType";
|
||||||
export const CourseSessionData = "CourseSessionData";
|
export const CourseSessionData = "CourseSessionData";
|
||||||
|
|
|
||||||
|
|
@ -273,8 +273,18 @@ export const DASHBOARD_CONFIG = graphql(`
|
||||||
query dashboardConfig {
|
query dashboardConfig {
|
||||||
dashboard_config {
|
dashboard_config {
|
||||||
id
|
id
|
||||||
|
slug
|
||||||
name
|
name
|
||||||
dashboard_type
|
dashboard_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
export const DASHBOARD_COURSE_SESSION_PROGRESS = graphql(`
|
||||||
|
query dashboardProgress($courseId: ID!) {
|
||||||
|
course_progress(course_id: $courseId) {
|
||||||
|
id
|
||||||
|
session_to_continue_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Component } from "vue";
|
||||||
|
import { onMounted } from "vue";
|
||||||
|
import StatisticPage from "@/pages/dashboard/StatisticPage.vue";
|
||||||
|
import ProgressPage from "@/pages/dashboard/ProgressPage.vue";
|
||||||
|
import SimpleDates from "@/components/dashboard/SimpleDates.vue";
|
||||||
|
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||||
|
import { useDashboardStore } from "@/stores/dashboard";
|
||||||
|
import type { DashboardType } from "@/gql/graphql";
|
||||||
|
import SimpleListPage from "@/pages/dashboard/SimpleListPage.vue";
|
||||||
|
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
|
||||||
|
interface DashboardPage {
|
||||||
|
main: Component;
|
||||||
|
aside: Component;
|
||||||
|
}
|
||||||
|
|
||||||
|
const boards: Record<DashboardType, DashboardPage> = {
|
||||||
|
PROGRESS_DASHBOARD: { main: ProgressPage, aside: SimpleDates },
|
||||||
|
SIMPLE_LIST_DASHBOARD: { main: SimpleListPage, aside: SimpleDates },
|
||||||
|
STATISTICS_DASHBOARD: { main: StatisticPage, aside: SimpleDates },
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(dashboardStore.loadDashboardConfig);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="dashboardStore.currentDashboardConfig" class="flex flex-col lg:flex-row">
|
||||||
|
<main class="grow bg-gray-200 lg:order-2">
|
||||||
|
<div class="m-8">
|
||||||
|
<div class="mb-10 flex items-center justify-between">
|
||||||
|
<h1 data-cy="welcome-message">Dashboard</h1>
|
||||||
|
<ItDropdownSelect
|
||||||
|
:model-value="dashboardStore.currentDashboardConfig"
|
||||||
|
class="mt-4 w-full lg:mt-0 lg:w-96"
|
||||||
|
:items="dashboardStore.dashboardConfigs"
|
||||||
|
@update:model-value="dashboardStore.setCurrentDashboardConfig"
|
||||||
|
></ItDropdownSelect>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<component
|
||||||
|
:is="boards[dashboardStore.currentDashboardConfig.dashboard_type].main"
|
||||||
|
></component>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<aside class="m-8 lg:order-1 lg:w-[343px]">
|
||||||
|
<component
|
||||||
|
:is="boards[dashboardStore.currentDashboardConfig.dashboard_type].aside"
|
||||||
|
></component>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useDashboardStore } from "@/stores/dashboard";
|
||||||
|
import { DASHBOARD_COURSE_SESSION_PROGRESS } from "@/graphql/queries";
|
||||||
|
import { useQuery } from "@urql/vue";
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { getLearningPathUrl } from "@/utils/utils";
|
||||||
|
import LearningPathDiagram from "@/components/learningPath/LearningPathDiagram.vue";
|
||||||
|
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
const courseId = computed(() => dashboardStore.currentDashboardConfig?.id);
|
||||||
|
const courseSlug = computed(() => dashboardStore.currentDashboardConfig?.slug || "");
|
||||||
|
|
||||||
|
const progressQuery = useQuery({
|
||||||
|
query: DASHBOARD_COURSE_SESSION_PROGRESS,
|
||||||
|
variables: {
|
||||||
|
// @ts-ignore
|
||||||
|
courseId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const progress = progressQuery.data;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="progress?.course_progress && dashboardStore.currentDashboardConfig">
|
||||||
|
<div class="mb-14">
|
||||||
|
<h2 class="mb-3 mt-12">{{ $t("dashboard.courses") }}</h2>
|
||||||
|
|
||||||
|
<div class="bg-white p-6 md:h-full">
|
||||||
|
<h3 class="mb-4">{{ dashboardStore.currentDashboardConfig.name }}</h3>
|
||||||
|
<div>
|
||||||
|
<LearningPathDiagram
|
||||||
|
:key="courseSlug"
|
||||||
|
class="mb-4"
|
||||||
|
:course-slug="courseSlug"
|
||||||
|
:course-session-id="progress.course_progress.session_to_continue_id"
|
||||||
|
diagram-type="horizontalSmall"
|
||||||
|
></LearningPathDiagram>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<router-link
|
||||||
|
class="btn-blue"
|
||||||
|
:to="getLearningPathUrl(dashboardStore.currentDashboardConfig.slug)"
|
||||||
|
:data-cy="`continue-course-${dashboardStore.currentDashboardConfig.id}`"
|
||||||
|
>
|
||||||
|
{{ $t("general.nextStep") }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getCockpitUrl } from "@/utils/utils";
|
||||||
|
import { useDashboardStore } from "@/stores/dashboard";
|
||||||
|
|
||||||
|
const dashboardStore = useDashboardStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="dashboardStore.currentDashboardConfig">
|
||||||
|
<div class="mb-14">
|
||||||
|
<div class="h-full bg-white p-6">
|
||||||
|
<h3 class="mb-4">{{ dashboardStore.currentDashboardConfig.name }}</h3>
|
||||||
|
<div>
|
||||||
|
<router-link
|
||||||
|
class="btn-blue"
|
||||||
|
:to="getCockpitUrl(dashboardStore.currentDashboardConfig.slug)"
|
||||||
|
:data-cy="`continue-course-${dashboardStore.currentDashboardConfig.id}`"
|
||||||
|
>
|
||||||
|
{{ $t("a.Cockpit anschauen") }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h4>Stats</h4>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import DashboardPage from "@/pages/DashboardPage.vue";
|
import DashboardPage from "@/pages/dashboard/DashboardPage.vue";
|
||||||
import LoginPage from "@/pages/LoginPage.vue";
|
import LoginPage from "@/pages/LoginPage.vue";
|
||||||
import {
|
import {
|
||||||
handleCourseSessionAsQueryParam,
|
handleCourseSessionAsQueryParam,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import type { DashboardConfigType } from "@/gql/graphql";
|
||||||
|
import { graphqlClient } from "@/graphql/client";
|
||||||
|
import { DASHBOARD_CONFIG } from "@/graphql/queries";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import type { Ref } from "vue";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
export const useDashboardStore = defineStore("dashboard", () => {
|
||||||
|
const dashboardConfigs: Ref<DashboardConfigType[]> = ref([]);
|
||||||
|
const currentDashboardConfig: Ref<DashboardConfigType | undefined> = ref();
|
||||||
|
|
||||||
|
const setCurrentDashboardConfig = (config: DashboardConfigType) => {
|
||||||
|
currentDashboardConfig.value = config;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadDashboardConfig = async () => {
|
||||||
|
const res = await graphqlClient.query(DASHBOARD_CONFIG, {});
|
||||||
|
if (res.data) {
|
||||||
|
dashboardConfigs.value = res.data.dashboard_config;
|
||||||
|
currentDashboardConfig.value = res.data.dashboard_config[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
dashboardConfigs,
|
||||||
|
currentDashboardConfig,
|
||||||
|
setCurrentDashboardConfig,
|
||||||
|
loadDashboardConfig,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
from typing import Set, Dict, List, Tuple
|
from typing import Dict, List, Set, Tuple
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
|
|
||||||
from vbv_lernwelt.core.admin import User
|
from vbv_lernwelt.core.admin import User
|
||||||
from vbv_lernwelt.course.models import Course, CourseSession
|
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
|
||||||
from vbv_lernwelt.course.models import CourseSessionUser
|
|
||||||
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
from vbv_lernwelt.course_session_group.models import CourseSessionGroup
|
||||||
from vbv_lernwelt.dashboard.graphql.types.dashboard import (
|
from vbv_lernwelt.dashboard.graphql.types.dashboard import (
|
||||||
|
CourseProgressType,
|
||||||
CourseStatisticsType,
|
CourseStatisticsType,
|
||||||
DashboardConfigType,
|
DashboardConfigType,
|
||||||
DashboardType, CourseProgressType,
|
DashboardType,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.iam.permissions import (
|
from vbv_lernwelt.iam.permissions import (
|
||||||
can_view_course_session,
|
can_view_course_session,
|
||||||
can_view_course_session_group_statistics, can_view_course_session_progress,
|
can_view_course_session_group_statistics,
|
||||||
|
can_view_course_session_progress,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -46,14 +47,26 @@ class DashboardQuery(graphene.ObjectType):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return CourseStatisticsType(
|
return CourseStatisticsType(
|
||||||
id=course.id, # noqa
|
id=course.id, # noqa
|
||||||
course_title=course.title, # noqa
|
course_title=course.title, # noqa
|
||||||
course_session_selection_ids=list(course_session_ids), # noqa
|
course_session_selection_ids=list(course_session_ids), # noqa
|
||||||
)
|
)
|
||||||
|
|
||||||
def resolve_dashboard_config(root, info): # noqa
|
def resolve_dashboard_config(root, info): # noqa
|
||||||
user = info.context.user
|
user = info.context.user
|
||||||
|
|
||||||
|
if user.is_superuser:
|
||||||
|
courses = Course.objects.all().values("id", "title", "slug")
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"id": c["id"],
|
||||||
|
"name": c["title"],
|
||||||
|
"slug": c["slug"],
|
||||||
|
"dashboard_type": DashboardType.SIMPLE_LIST_DASHBOARD,
|
||||||
|
}
|
||||||
|
for c in courses
|
||||||
|
]
|
||||||
|
|
||||||
(
|
(
|
||||||
statistic_dashboards,
|
statistic_dashboards,
|
||||||
statistics_dashboard_course_ids,
|
statistics_dashboard_course_ids,
|
||||||
|
|
@ -65,7 +78,7 @@ class DashboardQuery(graphene.ObjectType):
|
||||||
|
|
||||||
return statistic_dashboards + course_session_dashboards
|
return statistic_dashboards + course_session_dashboards
|
||||||
|
|
||||||
def resolve_course_progress(root, info, course_id: str): # noqa
|
def resolve_course_progress(root, info, course_id: str): # noqa
|
||||||
"""
|
"""
|
||||||
Slightly fragile but could be good enough: most only have one
|
Slightly fragile but could be good enough: most only have one
|
||||||
course session per course anyway but if there are multiple, we
|
course session per course anyway but if there are multiple, we
|
||||||
|
|
@ -76,17 +89,21 @@ class DashboardQuery(graphene.ObjectType):
|
||||||
newest: CourseSession | None = None
|
newest: CourseSession | None = None
|
||||||
|
|
||||||
for course_session in CourseSession.objects.filter(course_id=course_id):
|
for course_session in CourseSession.objects.filter(course_id=course_id):
|
||||||
if can_view_course_session_progress(user=user, course_session=course_session):
|
if can_view_course_session_progress(
|
||||||
|
user=user, course_session=course_session
|
||||||
|
):
|
||||||
generation_newest = newest.generation if newest else None
|
generation_newest = newest.generation if newest else None
|
||||||
if generation_newest is None or course_session.generation > generation_newest:
|
if (
|
||||||
|
generation_newest is None
|
||||||
|
or course_session.generation > generation_newest
|
||||||
|
):
|
||||||
newest = course_session
|
newest = course_session
|
||||||
|
|
||||||
if not newest:
|
if not newest:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return CourseProgressType(
|
return CourseProgressType(
|
||||||
id=course_id, # noqa
|
id=course_id, session_to_continue_id=newest.id # noqa # noqa
|
||||||
session_to_continue_id=newest.id # noqa
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -111,7 +128,7 @@ def get_user_statistics_dashboards(user: User) -> Tuple[List[Dict[str, str]], Se
|
||||||
|
|
||||||
|
|
||||||
def get_user_course_session_dashboards(
|
def get_user_course_session_dashboards(
|
||||||
user: User, exclude_course_ids: Set[int]
|
user: User, exclude_course_ids: Set[int]
|
||||||
) -> List[Dict[str, str]]:
|
) -> List[Dict[str, str]]:
|
||||||
"""
|
"""
|
||||||
Edge case: what do we show to users with access to multiple
|
Edge case: what do we show to users with access to multiple
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue