Refactor code to use `useCourseSessionDetailQuery`

This commit is contained in:
Daniel Egger 2023-10-06 15:43:21 +02:00
parent 06d284b1ce
commit f75590dd0b
19 changed files with 151 additions and 191 deletions

View File

@ -48,12 +48,30 @@ export function useCourseSessionDetailQuery(courSessionId?: string | number) {
return queryResult.data.value?.course_session as CourseSessionDetail | undefined;
});
function findAssignmentDetail(assignmentId: string) {
function findAssignmentByAssignmentId(assignmentId: string) {
return (courseSessionDetail.value?.assignments ?? []).find((a) => {
return a.learning_content?.content_assignment?.id === assignmentId;
});
}
function findAssignment(learningContentId: string) {
return (courseSessionDetail.value?.assignments ?? []).find((a) => {
return a.learning_content.id === learningContentId;
});
}
function findEdoniqTest(learningContentId: string) {
return (courseSessionDetail.value?.edoniq_tests ?? []).find((e) => {
return e.learning_content.id === learningContentId;
});
}
function findAttendanceCourse(learningContentId: string) {
return (courseSessionDetail.value?.attendance_courses ?? []).find((e) => {
return e.learning_content.id === learningContentId;
});
}
function findUser(userId: string) {
return (courseSessionDetail.value?.users ?? []).find((u) => {
return u.user_id === userId;
@ -72,6 +90,12 @@ export function useCourseSessionDetailQuery(courSessionId?: string | number) {
});
}
function filterCircleExperts(circleSlug: string) {
return (courseSessionDetail.value?.users ?? []).filter((u) => {
return u.role === "EXPERT" && u.circles.map((c) => c.slug).includes(circleSlug);
});
}
const dataLoaded = ref(false);
function waitForData() {
@ -89,9 +113,13 @@ export function useCourseSessionDetailQuery(courSessionId?: string | number) {
...queryResult,
courseSessionDetail,
waitForData,
findAssignmentDetail,
findAssignmentByAssignmentId,
findAssignment,
findEdoniqTest,
findAttendanceCourse,
findUser,
findCurrentUser,
filterMembers,
filterCircleExperts,
};
}

View File

@ -20,7 +20,7 @@ const documents = {
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
"\n query courseQuery($courseId: ID!) {\n course(id: $courseId) {\n id\n slug\n title\n category_name\n learning_path {\n id\n }\n }\n }\n": types.CourseQueryDocument,
"\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 title\n id\n slug\n content_type\n frontend_url\n circle {\n ...CoursePageFields\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 }\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 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,
};
@ -69,7 +69,7 @@ export function graphql(source: "\n query competenceCertificateQuery($courseSlu
/**
* 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 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 }\n }\n }\n }\n"): (typeof documents)["\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 }\n }\n }\n }\n"];
export function graphql(source: "\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"): (typeof documents)["\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"];
/**
* 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

View File

@ -197,6 +197,11 @@ export const COURSE_SESSION_DETAIL_QUERY = graphql(`
learning_content {
id
title
content_assignment {
id
title
assignment_type
}
}
}
}

View File

@ -61,7 +61,9 @@ function editTask(task: AssignmentEvaluationTask) {
const courseSessionDetailResult = useCourseSessionDetailQuery();
const assignmentDetail = computed(() => {
return courseSessionDetailResult.findAssignmentDetail(props.assignment.id.toString());
return courseSessionDetailResult.findAssignmentByAssignmentId(
props.assignment.id.toString()
);
});
const dueDate = computed(() =>

View File

@ -33,8 +33,8 @@ const state = reactive({
});
const assignmentDetail = computed(() => {
return courseSessionDetailResult.findAssignmentDetail(
props.learningContentAssignment.content_assignment_id.toString()
return courseSessionDetailResult.findAssignment(
props.learningContentAssignment.id.toString()
);
});

View File

@ -1,6 +1,5 @@
<script setup lang="ts">
import { useCircleStore } from "@/stores/circle";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import type { CourseSessionUser } from "@/types";
import { humanizeDuration } from "@/utils/humanizeDuration";
import sumBy from "lodash/sumBy";
@ -11,6 +10,7 @@ import CircleDiagram from "./CircleDiagram.vue";
import CircleOverview from "./CircleOverview.vue";
import DocumentSection from "./DocumentSection.vue";
import LearningSequence from "./LearningSequence.vue";
import { useCourseSessionDetailQuery } from "@/composables";
export interface Props {
courseSlug: string;
@ -20,7 +20,8 @@ export interface Props {
}
const route = useRoute();
const courseSessionsStore = useCourseSessionsStore();
const courseSessionDetailResult = useCourseSessionDetailQuery();
const circleStore = useCircleStore();
const props = withDefaults(defineProps<Props>(), {
readonly: false,
@ -29,7 +30,12 @@ const props = withDefaults(defineProps<Props>(), {
log.debug("CirclePage created", props.readonly, props.profileUser);
const circleStore = useCircleStore();
const circleExperts = computed(() => {
if (circleStore.circle) {
return courseSessionDetailResult.filterCircleExperts(circleStore.circle.slug);
}
return [];
});
const duration = computed(() => {
if (circleStore.circle) {
@ -190,10 +196,7 @@ onMounted(async () => {
})
}}
</div>
<div
v-for="expert in courseSessionsStore.circleExperts"
:key="expert.user_id"
>
<div v-for="expert in circleExperts" :key="expert.user_id">
<div class="mb-2 mt-2 flex flex-row items-center">
<img
class="mr-2 h-[45px] rounded-full"
@ -205,8 +208,8 @@ onMounted(async () => {
</div>
</div>
<a
v-if="courseSessionsStore.circleExperts.length > 0"
:href="'mailto:' + courseSessionsStore.circleExperts[0].email"
v-if="circleExperts.length > 0"
:href="'mailto:' + circleExperts[0].email"
class="btn-secondary mt-4 text-xl"
>
{{ $t("circlePage.contactExpertButton") }}

View File

@ -2,19 +2,19 @@
import DateEmbedding from "@/components/dueDates/DateEmbedding.vue";
import type { Assignment } from "@/types";
import { useRouteQuery } from "@vueuse/router";
import type { Dayjs } from "dayjs";
import log from "loglevel";
import dayjs from "dayjs";
interface Props {
assignment: Assignment;
dueDate?: Dayjs;
submissionDeadlineStart?: string;
}
const props = withDefaults(defineProps<Props>(), {
dueDate: undefined,
submissionDeadlineStart: "",
});
log.debug("AssignmentIntroductionView created", props.assignment, props.dueDate);
log.debug("AssignmentIntroductionView created", props.assignment);
const step = useRouteQuery("step");
</script>
@ -40,9 +40,10 @@ const step = useRouteQuery("step");
</ul>
<h3 class="mb-4 mt-8">{{ $t("assignment.dueDateSubmission") }}</h3>
<p v-if="props.dueDate?.toString() === 'Invalid Date'" class="text-large">
<p v-if="submissionDeadlineStart" class="text-large">
{{ $t("assignment.dueDateIntroduction") }}
<DateEmbedding :single-date="dueDate"></DateEmbedding>
<DateEmbedding :single-date="dayjs(submissionDeadlineStart)"></DateEmbedding>
</p>
<p v-else class="text-large">
{{ $t("assignment.dueDateNotSet") }}

View File

@ -3,34 +3,36 @@ import DateEmbedding from "@/components/dueDates/DateEmbedding.vue";
import ItButton from "@/components/ui/ItButton.vue";
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue";
import { useCurrentCourseSession } from "@/composables";
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
import { bustItGetCache } from "@/fetchHelpers";
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
import AssignmentSubmissionResponses from "@/pages/learningPath/learningContentPage/assignment/AssignmentSubmissionResponses.vue";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useUserStore } from "@/stores/user";
import type { Assignment, AssignmentCompletion, AssignmentTask } from "@/types";
import { useMutation } from "@urql/vue";
import type { Dayjs } from "dayjs";
import log from "loglevel";
import { computed, reactive } from "vue";
import { useTranslation } from "i18next-vue";
import eventBus from "@/utils/eventBus";
import dayjs from "dayjs";
import { useCircleStore } from "@/stores/circle";
const props = defineProps<{
assignment: Assignment;
learningContentId: number;
assignmentCompletion?: AssignmentCompletion;
courseSessionId: number;
dueDate: Dayjs;
submissionDeadlineStart?: string;
}>();
const emit = defineEmits<{
(e: "editTask", task: AssignmentTask): void;
}>();
const courseSessionsStore = useCourseSessionsStore();
const courseSession = useCurrentCourseSession();
const courseSessionDetailResult = useCourseSessionDetailQuery();
const circleStore = useCircleStore();
const { t } = useTranslation();
const state = reactive({
@ -38,8 +40,15 @@ const state = reactive({
confirmPerson: false,
});
const circleExperts = computed(() => {
if (circleStore.circle) {
return courseSessionDetailResult.filterCircleExperts(circleStore.circle.slug);
}
return [];
});
const circleExpert = computed(() => {
return courseSessionsStore.circleExperts[0];
return circleExperts.value[0];
});
const circleExpertName = computed(() => {
@ -138,9 +147,11 @@ const onSubmit = async () => {
{{ $t("assignment.showAssessmentDocument") }}
</a>
</div>
<p v-if="isCasework" class="pt-6">
<p v-if="isCasework && props.submissionDeadlineStart" class="pt-6">
{{ $t("assignment.dueDateSubmission") }}
<DateEmbedding :single-date="dueDate"></DateEmbedding>
<DateEmbedding
:single-date="dayjs(props.submissionDeadlineStart)"
></DateEmbedding>
</p>
<ItButton
class="mt-6"

View File

@ -1,26 +1,22 @@
<script setup lang="ts">
import { useCurrentCourseSession } from "@/composables";
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
import { UPSERT_ASSIGNMENT_COMPLETION_MUTATION } from "@/graphql/mutations";
import { ASSIGNMENT_COMPLETION_QUERY } from "@/graphql/queries";
import AssignmentIntroductionView from "@/pages/learningPath/learningContentPage/assignment/AssignmentIntroductionView.vue";
import AssignmentSubmissionView from "@/pages/learningPath/learningContentPage/assignment/AssignmentSubmissionView.vue";
import AssignmentTaskView from "@/pages/learningPath/learningContentPage/assignment/AssignmentTaskView.vue";
import LearningContentMultiLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentMultiLayout.vue";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useUserStore } from "@/stores/user";
import type {
Assignment,
AssignmentCompletion,
AssignmentTask,
CourseSessionAssignment,
CourseSessionUser,
LearningContentAssignment,
} from "@/types";
import { useMutation, useQuery } from "@urql/vue";
import { useRouteQuery } from "@vueuse/router";
import dayjs from "dayjs";
import * as log from "loglevel";
import { computed, onMounted, reactive, ref, watchEffect } from "vue";
import { computed, onMounted, ref, watchEffect } from "vue";
import { useTranslation } from "i18next-vue";
import { bustItGetCache } from "@/fetchHelpers";
import { learningContentTypeData } from "@/utils/typeMaps";
@ -30,14 +26,6 @@ const { t } = useTranslation();
const courseSession = useCurrentCourseSession();
const userStore = useUserStore();
interface State {
courseSessionAssignment: CourseSessionAssignment | undefined;
}
const state: State = reactive({
courseSessionAssignment: undefined,
});
const props = defineProps<{
learningContent: LearningContentAssignment;
}>();
@ -52,10 +40,17 @@ const queryResult = useQuery({
pause: true,
});
const courseSessionDetailResult = useCourseSessionDetailQuery();
const upsertAssignmentCompletionMutation = useMutation(
UPSERT_ASSIGNMENT_COMPLETION_MUTATION
);
const submissionDeadline = computed(() => {
return courseSessionDetailResult.findAssignment(props.learningContent.id.toString())
?.submission_deadline;
});
// FIXME daniel: `useRouteQuery` from usevue is currently the reason that we have to
// fix the version of @vueuse/router and @vueuse/core to 10.1.0
// it fails with version 10.2.0. I have a reminder to check out the situation
@ -104,10 +99,6 @@ onMounted(async () => {
props.learningContent
);
state.courseSessionAssignment = useCourseSessionsStore().findCourseSessionAssignment(
props.learningContent.id
);
// create initial `AssignmentCompletion` first, so that it exists and we don't
// have reactivity problem accessing it.
await initUpsertAssignmentCompletion();
@ -120,9 +111,7 @@ const numPages = computed(() => {
});
const showPreviousButton = computed(() => stepIndex.value != 0);
const showNextButton = computed(() => stepIndex.value + 1 < numPages.value);
const dueDate = computed(() =>
dayjs(state.courseSessionAssignment?.submission_deadline_start)
);
const currentTask = computed(() => {
if (stepIndex.value > 0 && stepIndex.value <= numTasks.value) {
return assignment.value?.tasks[stepIndex.value - 1];
@ -194,9 +183,9 @@ const subTitle = computed(() => {
});
const assignmentUser = computed(() => {
return courseSession.value.users.find(
(user) => user.user_id === userStore.id
) as CourseSessionUser;
return (courseSessionDetailResult.courseSessionDetail.value?.users ?? []).find(
(u) => u.user_id === userStore.id
);
});
</script>
@ -204,7 +193,7 @@ const assignmentUser = computed(() => {
<div v-if="queryResult.fetching.value"></div>
<div v-else-if="queryResult.error.value">{{ queryResult.error.value }}</div>
<div v-else>
<div v-if="assignment && assignmentCompletion">
<div v-if="assignment && assignmentCompletion && assignmentUser">
<div class="flex flex-col lg:flex-row">
<div
v-if="assignmentCompletion?.completion_status === 'EVALUATION_SUBMITTED'"
@ -239,8 +228,8 @@ const assignmentUser = computed(() => {
<div>
<AssignmentIntroductionView
v-if="stepIndex === 0"
:due-date="dueDate"
:assignment="assignment"
:submission-deadline-start="submissionDeadline?.start"
></AssignmentIntroductionView>
<AssignmentTaskView
v-else-if="currentTask"
@ -251,11 +240,11 @@ const assignmentUser = computed(() => {
></AssignmentTaskView>
<AssignmentSubmissionView
v-else-if="stepIndex + 1 === numPages"
:due-date="dueDate"
:assignment="assignment"
:assignment-completion="assignmentCompletion"
:learning-content-id="props.learningContent.id"
:course-session-id="courseSession.id"
:submission-deadline-start="submissionDeadline?.start"
@edit-task="jumpToTask($event)"
></AssignmentSubmissionView>
</div>

View File

@ -3,7 +3,12 @@
<it-icon-calendar-light class="w-[60px] grid-in-icon" />
<h2 class="text-large font-bold grid-in-title">{{ $t("a.Datum") }}</h2>
<p class="grid-in-value">
{{ formatDueDate(props.attendanceCourse.start, props.attendanceCourse.end) }}
{{
formatDueDate(
props.attendanceCourse.due_date.start,
props.attendanceCourse.due_date.end
)
}}
</p>
</div>
<div class="mb-12 grid grid-cols-icon-card gap-x-4 grid-areas-icon-card">
@ -24,8 +29,6 @@
<script setup lang="ts">
import { formatDueDate } from "@/components/dueDates/dueDatesUtils";
import type { CourseSessionAttendanceCourse } from "@/types";
import dayjs from "dayjs";
import LocalizedFormat from "dayjs/plugin/localizedFormat";
import { computed } from "vue";
export interface Props {
@ -34,7 +37,6 @@ export interface Props {
const props = defineProps<Props>();
dayjs.extend(LocalizedFormat);
const location = computed(() => props.attendanceCourse.location);
const trainer = computed(() => props.attendanceCourse.trainer);
</script>

View File

@ -1,18 +1,18 @@
<script setup lang="ts">
import AttendanceCourse from "@/pages/learningPath/learningContentPage/attendanceCourse/AttendanceCourse.vue";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import type { LearningContentAttendanceCourse } from "@/types";
import { computed } from "vue";
import LearningContentSimpleLayout from "../layouts/LearningContentSimpleLayout.vue";
const courseSessionsStore = useCourseSessionsStore();
import { useCourseSessionDetailQuery } from "@/composables";
const props = defineProps<{
content: LearningContentAttendanceCourse;
}>();
const courseSessionDetailResult = useCourseSessionDetailQuery();
const courseSessionAttendanceCourse = computed(() => {
return courseSessionsStore.findAttendanceCourse(props.content.id);
return courseSessionDetailResult.findAttendanceCourse(props.content.id.toString());
});
</script>

View File

@ -8,8 +8,7 @@ import * as log from "loglevel";
import { itPost } from "@/fetchHelpers";
import { useQuery } from "@urql/vue";
import { ASSIGNMENT_COMPLETION_QUERY } from "@/graphql/queries";
import { useCurrentCourseSession } from "@/composables";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
import dayjs from "dayjs";
import ItSuccessAlert from "@/components/ui/ItSuccessAlert.vue";
import {
@ -24,10 +23,10 @@ const props = defineProps<{
}>();
const courseSession = useCurrentCourseSession();
const courseSessionsStore = useCourseSessionsStore();
const courseSessionDetailResult = useCourseSessionDetailQuery();
const courseSessionEdoniqTest = computed(() => {
return courseSessionsStore.findCourseSessionEdoniqTest(props.content.id);
return courseSessionDetailResult.findEdoniqTest(props.content.id.toString());
});
const queryResult = useQuery({
@ -53,7 +52,7 @@ const extendedTimeTest = ref(false);
const deadlineInPast = computed(() => {
// with 16 minutes buffer
return dayjs(courseSessionEdoniqTest.value?.deadline_start)
return dayjs(courseSessionEdoniqTest.value?.deadline.start)
.add(16, "minute")
.isBefore(dayjs());
});
@ -90,7 +89,7 @@ async function startTest() {
<p class="mt-2 text-lg">
{{
$t("edoniqTest.submitDateDescription", {
x: formatDueDate(courseSessionEdoniqTest.deadline_start),
x: formatDueDate(courseSessionEdoniqTest.deadline.start),
})
}}
</p>
@ -158,7 +157,7 @@ async function startTest() {
</div>
<div v-else>
{{ $t("a.Abgabetermin") }}:
{{ getDateString(dayjs(courseSessionEdoniqTest?.deadline_start)) }}
{{ getDateString(dayjs(courseSessionEdoniqTest?.deadline.start)) }}
</div>
</div>
</div>

View File

@ -10,21 +10,21 @@ import {
} from "@/pages/learningPath/learningContentPage/feedback/feedback.constants";
import LearningContentMultiLayout from "@/pages/learningPath/learningContentPage/layouts/LearningContentMultiLayout.vue";
import { useCircleStore } from "@/stores/circle";
import { useCourseSessionsStore } from "@/stores/courseSessions";
import type { LearningContentFeedback } from "@/types";
import { useMutation } from "@urql/vue";
import { useRouteQuery } from "@vueuse/router";
import log from "loglevel";
import { computed, onMounted, reactive, ref } from "vue";
import { useTranslation } from "i18next-vue";
import { useCurrentCourseSession } from "@/composables";
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
const props = defineProps<{
content: LearningContentFeedback;
}>();
const courseSessionsStore = useCourseSessionsStore();
const courseSession = useCurrentCourseSession();
const circleStore = useCircleStore();
const courseSessionDetailResult = useCourseSessionDetailQuery();
const { t } = useTranslation();
const stepNo = useRouteQuery("step", "0", { transform: Number, mode: "push" });
@ -33,6 +33,13 @@ const title = computed(
() => `«${circleStore.circle?.title}»: ${t("feedback.areYouSatisfied")}`
);
const circleExperts = computed(() => {
if (circleStore.circle) {
return courseSessionDetailResult.filterCircleExperts(circleStore.circle.slug);
}
return [];
});
const stepLabels = [
t("general.introduction"),
t("feedback.satisfactionLabel"),
@ -244,7 +251,7 @@ onMounted(async () => {
<p v-if="stepNo === 0" class="mt-10">
{{
$t("feedback.intro", {
name: `${courseSessionsStore.circleExperts[0]?.first_name} ${courseSessionsStore.circleExperts[0]?.last_name}`,
name: `${circleExperts[0]?.first_name} ${circleExperts[0]?.last_name}`,
})
}}
</p>
@ -265,10 +272,10 @@ onMounted(async () => {
</div>
<FeedbackCompletition
v-if="stepNo === 11"
:avatar-url="courseSessionsStore.circleExperts[0].avatar_url"
:avatar-url="circleExperts[0].avatar_url"
:title="
$t('feedback.completionTitle', {
name: `${courseSessionsStore.circleExperts[0].first_name} ${courseSessionsStore.circleExperts[0].last_name}`,
name: `${circleExperts[0].first_name} ${circleExperts[0].last_name}`,
})
"
:description="$t('feedback.completionDescription')"

View File

@ -147,23 +147,6 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
return Boolean(isCourseExpert && (inLearningPath() || inCompetenceProfile()));
});
// const circleExperts = computed(() => {
// const circleStore = useCircleStore();
// const circleTranslationKey = circleStore.circle?.translation_key;
//
// if (currentCourseSession.value && circleTranslationKey) {
// return currentCourseSession.value.users.filter((u) => {
// if (u.role === "EXPERT") {
// return (u as ExpertSessionUser).circles
// .map((c) => c.translation_key)
// .includes(circleTranslationKey);
// }
// return false;
// }) as ExpertSessionUser[];
// }
// return [];
// });
// const canUploadCircleDocuments = computed(() => {
// const userStore = useUserStore();
// return (
@ -249,36 +232,6 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
);
}
// function findAttendanceCourse(
// contentId: number
// ): CourseSessionAttendanceCourse | undefined {
// if (currentCourseSession.value) {
// return currentCourseSession.value.attendance_courses.find(
// (attendanceCourse) => attendanceCourse.learning_content_id === contentId
// );
// }
// }
// function findCourseSessionAssignment(
// contentId?: number
// ): CourseSessionAssignment | undefined {
// if (contentId && currentCourseSession.value) {
// return currentCourseSession.value.assignments.find(
// (a) => a.learning_content_id === contentId
// );
// }
// }
//
// function findCourseSessionEdoniqTest(
// contentId?: number
// ): CourseSessionEdoniqTest | undefined {
// if (contentId && currentCourseSession.value) {
// return currentCourseSession.value.edoniq_tests.find(
// (a) => a.learning_content_id === contentId
// );
// }
// }
return {
uniqueCourseSessionsByCourse,
allCurrentCourseSessions,

View File

@ -496,9 +496,15 @@ export interface CourseSessionAssignment {
export interface CourseSessionEdoniqTest {
id: number;
course_session_id: number;
learning_content_id: number;
deadline_id: number;
deadline_start: string;
deadline: SimpleDueDate;
learning_content: {
id: string;
content_assignment: {
id: string;
title: string;
assignment_type: AssignmentType;
};
};
}
export interface CourseSession {
@ -559,6 +565,7 @@ export interface CourseSessionDetail {
};
assignments: CourseSessionAssignment[];
attendance_courses: CourseSessionAttendanceCourse[];
edoniq_tests: CourseSessionEdoniqTest[];
users: CourseSessionUser[];
}

View File

@ -7,8 +7,13 @@ from graphene_django import DjangoObjectType
from graphql import GraphQLError
from rest_framework.exceptions import PermissionDenied
from vbv_lernwelt.course.models import Course, CourseBasePage, CoursePage, \
CourseSession, CourseSessionUser
from vbv_lernwelt.course.models import (
Course,
CourseBasePage,
CoursePage,
CourseSession,
CourseSessionUser,
)
from vbv_lernwelt.course.permissions import has_course_access
from vbv_lernwelt.course_session.graphql.types import (
CourseSessionAttendanceCourseObjectType,
@ -134,9 +139,7 @@ class CourseSessionUserObjectsType(DjangoObjectType):
return self.role
def resolve_circles(self, info):
return self.expert.all().values(
"id", "title", "slug", "translation_key"
)
return self.expert.all().values("id", "title", "slug", "translation_key")
class CourseSessionObjectType(DjangoObjectType):
@ -165,10 +168,8 @@ class CourseSessionObjectType(DjangoObjectType):
def resolve_assignments(self, info):
return CourseSessionAssignment.objects.filter(course_session=self)
def resolve_edoniq_test(self, info):
def resolve_edoniq_tests(self, info):
return CourseSessionEdoniqTest.objects.filter(course_session=self)
def resolve_users(self, info):
return CourseSessionUser.objects.filter(
course_session_id=self.id
).distinct()
return CourseSessionUser.objects.filter(course_session_id=self.id).distinct()

View File

@ -7,16 +7,6 @@ from vbv_lernwelt.course.models import (
CourseCompletion,
CourseSession,
)
from vbv_lernwelt.course_session.models import (
CourseSessionAssignment,
CourseSessionAttendanceCourse,
CourseSessionEdoniqTest,
)
from vbv_lernwelt.course_session.serializers import (
CourseSessionAssignmentSerializer,
CourseSessionAttendanceCourseSerializer,
CourseSessionEdoniqTestSerializer,
)
from vbv_lernwelt.duedate.models import DueDate
from vbv_lernwelt.duedate.serializers import DueDateSerializer
@ -73,39 +63,12 @@ class CourseSessionSerializer(serializers.ModelSerializer):
def get_course_url(self, obj):
return obj.course.get_course_url()
def get_learning_path_url(self, obj):
return obj.course.get_learning_path_url()
def get_cockpit_url(self, obj):
return obj.course.get_cockpit_url()
def get_media_library_url(self, obj):
return obj.course.get_media_library_url()
def get_competence_url(self, obj):
return obj.course.get_competence_url()
def get_documents(self, obj):
documents = CircleDocument.objects.filter(
course_session=obj, file__upload_finished_at__isnull=False
)
return CircleDocumentSerializer(documents, many=True).data
def get_attendance_courses(self, obj):
return CourseSessionAttendanceCourseSerializer(
CourseSessionAttendanceCourse.objects.filter(course_session=obj), many=True
).data
def get_assignments(self, obj):
return CourseSessionAssignmentSerializer(
CourseSessionAssignment.objects.filter(course_session=obj), many=True
).data
def get_edoniq_tests(self, obj):
return CourseSessionEdoniqTestSerializer(
CourseSessionEdoniqTest.objects.filter(course_session=obj), many=True
).data
def get_due_dates(self, obj):
due_dates = DueDate.objects.filter(course_session=obj)
return DueDateSerializer(due_dates, many=True).data
@ -120,16 +83,7 @@ class CourseSessionSerializer(serializers.ModelSerializer):
"title",
"start_date",
"end_date",
# "additional_json_data",
# "attendance_courses",
# "assignments",
# "edoniq_tests",
# "learning_path_url",
# "cockpit_url",
# "competence_url",
# "media_library_url",
"course_url",
# "documents",
"due_dates",
]

View File

@ -5,6 +5,7 @@ from vbv_lernwelt.course.permissions import is_course_session_expert
from vbv_lernwelt.course_session.models import (
CourseSessionAttendanceCourse,
CourseSessionAssignment,
CourseSessionEdoniqTest,
)
from vbv_lernwelt.course_session.services.attendance import AttendanceUserStatus
from vbv_lernwelt.duedate.graphql.types import DueDateObjectType
@ -39,7 +40,6 @@ class CourseSessionAttendanceCourseObjectType(DjangoObjectType):
fields = (
"id",
"course_session_id",
"learning_content_id",
"learning_content",
"location",
"trainer",
@ -64,10 +64,9 @@ class CourseSessionAssignmentObjectType(DjangoObjectType):
fields = (
"id",
"course_session_id",
"learning_content_id",
"learning_content",
"submission_deadline",
"evaluation_deadline",
"learning_content",
)
@ -78,11 +77,10 @@ class CourseSessionEdoniqTestObjectType(DjangoObjectType):
learning_content = graphene.Field(LearningContentEdoniqTestObjectType)
class Meta:
model = CourseSessionAssignment
model = CourseSessionEdoniqTest
fields = (
"id",
"course_session_id",
"learning_content_id",
"deadline",
"learning_content",
"deadline",
)