Load CompletionData
This commit is contained in:
parent
627e4f6873
commit
8621d4af07
|
|
@ -11,7 +11,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template #center>
|
<template #center>
|
||||||
<div class="flex w-full justify-between">
|
<div class="flex w-full justify-between">
|
||||||
<div>Circle: {{ feedbacks.circle.title }}</div>
|
<div>{{ $t("a.Circle") }}: {{ feedbacks.circle.title }}</div>
|
||||||
<div>{{ $t("feedback.sentByUsers", { count: feedbacks.count }) }}</div>
|
<div>{{ $t("feedback.sentByUsers", { count: feedbacks.count }) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -34,7 +34,7 @@ import ItRow from "@/components/ui/ItRow.vue";
|
||||||
import { itGet } from "@/fetchHelpers";
|
import { itGet } from "@/fetchHelpers";
|
||||||
import { onMounted, ref, watch } from "vue";
|
import { onMounted, ref, watch } from "vue";
|
||||||
|
|
||||||
import type { Circle } from "@/services/circle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
|
|
||||||
interface FeedbackSummary {
|
interface FeedbackSummary {
|
||||||
circle_id: string;
|
circle_id: string;
|
||||||
|
|
@ -42,12 +42,12 @@ interface FeedbackSummary {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FeedbackDisplaySummary extends FeedbackSummary {
|
interface FeedbackDisplaySummary extends FeedbackSummary {
|
||||||
circle: Circle;
|
circle: OldCircle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeSummary(
|
function makeSummary(
|
||||||
feedbackData: FeedbackSummary[],
|
feedbackData: FeedbackSummary[],
|
||||||
circles: Circle[],
|
circles: OldCircle[],
|
||||||
selectedCircles: string[]
|
selectedCircles: string[]
|
||||||
) {
|
) {
|
||||||
const summary: FeedbackDisplaySummary[] = circles
|
const summary: FeedbackDisplaySummary[] = circles
|
||||||
|
|
@ -64,7 +64,7 @@ function makeSummary(
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
selctedCircles: string[];
|
selctedCircles: string[];
|
||||||
circles: Circle[];
|
circles: OldCircle[];
|
||||||
courseSessionId: string;
|
courseSessionId: string;
|
||||||
url: string;
|
url: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,17 @@
|
||||||
import { COURSE_SESSION_DETAIL_QUERY, LEARNING_PATH_QUERY } from "@/graphql/queries";
|
import { COURSE_SESSION_DETAIL_QUERY, LEARNING_PATH_QUERY } from "@/graphql/queries";
|
||||||
|
import { circleFlatChildren } from "@/services/circle";
|
||||||
|
import { useCompletionStore } from "@/stores/completion";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import type { CourseSession, CourseSessionDetail, LearningPathType } from "@/types";
|
import type {
|
||||||
|
CourseCompletion,
|
||||||
|
CourseCompletionStatus,
|
||||||
|
CourseSession,
|
||||||
|
CourseSessionDetail,
|
||||||
|
LearningContentWithCompletion,
|
||||||
|
LearningPathType,
|
||||||
|
LearningUnitPerformanceCriteria,
|
||||||
|
} from "@/types";
|
||||||
import { useQuery } from "@urql/vue";
|
import { useQuery } from "@urql/vue";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
import type { ComputedRef } from "vue";
|
import type { ComputedRef } from "vue";
|
||||||
|
|
@ -150,3 +160,71 @@ export function useLearningPathQuery(courseSlug: string) {
|
||||||
|
|
||||||
return { ...queryResult, learningPath, circles, findCircle };
|
return { ...queryResult, learningPath, circles, findCircle };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useLearningPathWithCompletion(
|
||||||
|
courseSlug?: string,
|
||||||
|
courseSessionId?: string,
|
||||||
|
userId?: string
|
||||||
|
) {
|
||||||
|
if (!courseSlug) {
|
||||||
|
courseSlug = useCurrentCourseSession().value.course.slug;
|
||||||
|
}
|
||||||
|
if (!userId) {
|
||||||
|
userId = useUserStore().id;
|
||||||
|
}
|
||||||
|
if (!courseSessionId) {
|
||||||
|
courseSessionId = useCurrentCourseSession().value.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lpQueryResult = useLearningPathQuery(courseSlug);
|
||||||
|
const completionStore = useCompletionStore();
|
||||||
|
|
||||||
|
function updateCompletionData() {
|
||||||
|
if (userId && courseSessionId) {
|
||||||
|
completionStore
|
||||||
|
.loadCourseSessionCompletionData(courseSessionId, userId)
|
||||||
|
.then(_parseCompletionData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (lpQueryResult.data.value) {
|
||||||
|
// load completion data when learning path data is loaded
|
||||||
|
updateCompletionData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function _parseCompletionData(completionData: CourseCompletion[]) {
|
||||||
|
if (lpQueryResult.circles.value) {
|
||||||
|
lpQueryResult.circles.value.forEach((circle) => {
|
||||||
|
circleFlatChildren(circle).forEach((lc) => {
|
||||||
|
const pageIndex = completionData.findIndex((e) => {
|
||||||
|
return e.page_id === lc.id;
|
||||||
|
});
|
||||||
|
if (pageIndex >= 0) {
|
||||||
|
lc.completion_status = completionData[pageIndex].completion_status;
|
||||||
|
} else {
|
||||||
|
lc.completion_status = "UNKNOWN";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function markCompletion(
|
||||||
|
page: LearningContentWithCompletion | LearningUnitPerformanceCriteria,
|
||||||
|
completion_status: CourseCompletionStatus = "SUCCESS"
|
||||||
|
) {
|
||||||
|
if (userId && courseSessionId) {
|
||||||
|
page.completion_status = completion_status;
|
||||||
|
const completionData = await completionStore.markPage(
|
||||||
|
page,
|
||||||
|
userId,
|
||||||
|
courseSessionId
|
||||||
|
);
|
||||||
|
_parseCompletionData(completionData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ...lpQueryResult, updateCompletionData, markCompletion };
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
|
"\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 task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
|
||||||
"\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 ...CoursePageFields\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 ...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 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 learningPathQuery($slug: String!) {\n learning_path(slug: $slug) {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n learning_contents {\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 competence_certificate {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.LearningPathQueryDocument,
|
"\n query learningPathQuery($slug: String!) {\n learning_path(slug: $slug) {\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 ...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 competence_certificate {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.LearningPathQueryDocument,
|
||||||
"\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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ export function graphql(source: "\n query courseSessionDetail($courseSessionId:
|
||||||
/**
|
/**
|
||||||
* 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 learningPathQuery($slug: String!) {\n learning_path(slug: $slug) {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n learning_contents {\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 competence_certificate {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query learningPathQuery($slug: String!) {\n learning_path(slug: $slug) {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n learning_contents {\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 competence_certificate {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n }\n }\n }\n"];
|
export function graphql(source: "\n query learningPathQuery($slug: String!) {\n learning_path(slug: $slug) {\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 ...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 competence_certificate {\n ...CoursePageFields\n }\n }\n }\n }\n }\n }\n }\n }\n }\n"): (typeof documents)["\n query learningPathQuery($slug: String!) {\n learning_path(slug: $slug) {\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 ...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 competence_certificate {\n ...CoursePageFields\n }\n }\n }\n }\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.
|
* 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
|
|
@ -93,6 +93,7 @@ type LearningUnitObjectType implements CoursePageInterface {
|
||||||
circle: CircleObjectType
|
circle: CircleObjectType
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
learning_contents: [LearningContentInterface!]!
|
learning_contents: [LearningContentInterface!]!
|
||||||
|
performance_criteria: [PerformanceCriteriaObjectType!]!
|
||||||
evaluate_url: String!
|
evaluate_url: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,8 +108,22 @@ interface LearningContentInterface {
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
|
}
|
||||||
|
|
||||||
|
type PerformanceCriteriaObjectType implements CoursePageInterface {
|
||||||
|
competence_id: String!
|
||||||
|
id: ID!
|
||||||
|
title: String!
|
||||||
|
slug: String!
|
||||||
|
content_type: String!
|
||||||
|
live: Boolean!
|
||||||
|
translation_key: String!
|
||||||
|
frontend_url: String!
|
||||||
|
circle: CircleObjectType
|
||||||
|
course: CourseObjectType
|
||||||
}
|
}
|
||||||
|
|
||||||
type TopicObjectType implements CoursePageInterface {
|
type TopicObjectType implements CoursePageInterface {
|
||||||
|
|
@ -136,8 +151,9 @@ type LearningContentMediaLibraryObjectType implements CoursePageInterface & Lear
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type LearningContentAssignmentObjectType implements CoursePageInterface & LearningContentInterface {
|
type LearningContentAssignmentObjectType implements CoursePageInterface & LearningContentInterface {
|
||||||
|
|
@ -153,9 +169,10 @@ type LearningContentAssignmentObjectType implements CoursePageInterface & Learni
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
competence_certificate: CompetenceCertificateObjectType!
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
|
competence_certificate: CompetenceCertificateObjectType
|
||||||
}
|
}
|
||||||
|
|
||||||
type AssignmentObjectType implements CoursePageInterface {
|
type AssignmentObjectType implements CoursePageInterface {
|
||||||
|
|
@ -330,8 +347,9 @@ type LearningContentAttendanceCourseObjectType implements CoursePageInterface &
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type DueDateObjectType {
|
type DueDateObjectType {
|
||||||
|
|
@ -411,9 +429,10 @@ type LearningContentEdoniqTestObjectType implements CoursePageInterface & Learni
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
competence_certificate: CompetenceCertificateObjectType!
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
|
competence_certificate: CompetenceCertificateObjectType
|
||||||
}
|
}
|
||||||
|
|
||||||
type CourseSessionUserObjectsType {
|
type CourseSessionUserObjectsType {
|
||||||
|
|
@ -504,8 +523,9 @@ type LearningContentFeedbackObjectType implements CoursePageInterface & Learning
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type LearningContentLearningModuleObjectType implements CoursePageInterface & LearningContentInterface {
|
type LearningContentLearningModuleObjectType implements CoursePageInterface & LearningContentInterface {
|
||||||
|
|
@ -519,8 +539,9 @@ type LearningContentLearningModuleObjectType implements CoursePageInterface & Le
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type LearningContentPlaceholderObjectType implements CoursePageInterface & LearningContentInterface {
|
type LearningContentPlaceholderObjectType implements CoursePageInterface & LearningContentInterface {
|
||||||
|
|
@ -534,8 +555,9 @@ type LearningContentPlaceholderObjectType implements CoursePageInterface & Learn
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type LearningContentRichTextObjectType implements CoursePageInterface & LearningContentInterface {
|
type LearningContentRichTextObjectType implements CoursePageInterface & LearningContentInterface {
|
||||||
|
|
@ -549,8 +571,9 @@ type LearningContentRichTextObjectType implements CoursePageInterface & Learning
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type LearningContentVideoObjectType implements CoursePageInterface & LearningContentInterface {
|
type LearningContentVideoObjectType implements CoursePageInterface & LearningContentInterface {
|
||||||
|
|
@ -564,8 +587,9 @@ type LearningContentVideoObjectType implements CoursePageInterface & LearningCon
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type LearningContentDocumentListObjectType implements CoursePageInterface & LearningContentInterface {
|
type LearningContentDocumentListObjectType implements CoursePageInterface & LearningContentInterface {
|
||||||
|
|
@ -579,8 +603,9 @@ type LearningContentDocumentListObjectType implements CoursePageInterface & Lear
|
||||||
circle: CircleObjectType!
|
circle: CircleObjectType!
|
||||||
course: CourseObjectType
|
course: CourseObjectType
|
||||||
minutes: Int
|
minutes: Int
|
||||||
description: String
|
description: String!
|
||||||
content: String
|
content_url: String!
|
||||||
|
can_user_self_toggle_course_completion: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompetenceCertificateListObjectType implements CoursePageInterface {
|
type CompetenceCertificateListObjectType implements CoursePageInterface {
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ export const LearningSequenceObjectType = "LearningSequenceObjectType";
|
||||||
export const LearningUnitObjectType = "LearningUnitObjectType";
|
export const LearningUnitObjectType = "LearningUnitObjectType";
|
||||||
export const LearnpathLearningContentAssignmentAssignmentTypeChoices = "LearnpathLearningContentAssignmentAssignmentTypeChoices";
|
export const LearnpathLearningContentAssignmentAssignmentTypeChoices = "LearnpathLearningContentAssignmentAssignmentTypeChoices";
|
||||||
export const Mutation = "Mutation";
|
export const Mutation = "Mutation";
|
||||||
|
export const PerformanceCriteriaObjectType = "PerformanceCriteriaObjectType";
|
||||||
export const Query = "Query";
|
export const Query = "Query";
|
||||||
export const SendFeedbackMutation = "SendFeedbackMutation";
|
export const SendFeedbackMutation = "SendFeedbackMutation";
|
||||||
export const String = "String";
|
export const String = "String";
|
||||||
|
|
|
||||||
|
|
@ -199,6 +199,8 @@ export const LEARNING_PATH_QUERY = graphql(`
|
||||||
is_visible
|
is_visible
|
||||||
...CoursePageFields
|
...CoursePageFields
|
||||||
circles {
|
circles {
|
||||||
|
description
|
||||||
|
goals
|
||||||
...CoursePageFields
|
...CoursePageFields
|
||||||
learning_sequences {
|
learning_sequences {
|
||||||
icon
|
icon
|
||||||
|
|
@ -206,7 +208,11 @@ export const LEARNING_PATH_QUERY = graphql(`
|
||||||
learning_units {
|
learning_units {
|
||||||
evaluate_url
|
evaluate_url
|
||||||
...CoursePageFields
|
...CoursePageFields
|
||||||
|
performance_criteria {
|
||||||
|
...CoursePageFields
|
||||||
|
}
|
||||||
learning_contents {
|
learning_contents {
|
||||||
|
can_user_self_toggle_course_completion
|
||||||
...CoursePageFields
|
...CoursePageFields
|
||||||
... on LearningContentAssignmentObjectType {
|
... on LearningContentAssignmentObjectType {
|
||||||
assignment_type
|
assignment_type
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ onMounted(async () => {
|
||||||
{{ learningContentAssignment.title }}
|
{{ learningContentAssignment.title }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="pt-1 underline">
|
<div class="pt-1 underline">
|
||||||
Circle «{{ learningContentAssignment.parentCircle.title }}»
|
{{ $t("a.Circle") }} «{{ learningContentAssignment.parentCircle.title }}»
|
||||||
</div>
|
</div>
|
||||||
<div v-if="assignmentDetail">
|
<div v-if="assignmentDetail">
|
||||||
<span v-if="assignmentDetail.submission_deadline?.start">
|
<span v-if="assignmentDetail.submission_deadline?.start">
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,9 @@ const getIconName = (lc: LearningContent) => {
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<h3 class="text-bold flex items-center gap-2">{{ submittable.title }}</h3>
|
<h3 class="text-bold flex items-center gap-2">{{ submittable.title }}</h3>
|
||||||
<p class="text-gray-800">Circle «{{ submittable.circleName }}»</p>
|
<p class="text-gray-800">
|
||||||
|
{{ $t("a.Circle") }} «{{ submittable.circleName }}»
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AssignmentSubmissionProgress
|
<AssignmentSubmissionProgress
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { showIcon } from "@/pages/learningPath/circlePage/learningSequenceUtils";
|
import { showIcon } from "@/pages/learningPath/circlePage/learningSequenceUtils";
|
||||||
import { useCircleStore } from "@/stores/circle";
|
|
||||||
import type { DefaultArcObject } from "d3";
|
import type { DefaultArcObject } from "d3";
|
||||||
import * as d3 from "d3";
|
import * as d3 from "d3";
|
||||||
import pick from "lodash/pick";
|
import pick from "lodash/pick";
|
||||||
|
|
@ -9,26 +8,23 @@ import { computed, onMounted } from "vue";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import colors from "@/colors.json";
|
import colors from "@/colors.json";
|
||||||
import type { LearningSequence } from "@/types";
|
|
||||||
|
|
||||||
const circleStore = useCircleStore();
|
import type { CircleType, LearningSequence } from "@/types";
|
||||||
|
import {
|
||||||
|
allFinishedInLearningSequence,
|
||||||
|
someFinishedInLearningSequence,
|
||||||
|
} from "@/services/circle";
|
||||||
|
|
||||||
function someFinished(learningSequence: LearningSequence) {
|
const props = defineProps<{
|
||||||
if (circleStore.circle) {
|
circle: CircleType;
|
||||||
return circleStore.circle.someFinishedInLearningSequence(
|
}>();
|
||||||
learningSequence.translation_key
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function allFinished(learningSequence: LearningSequence) {
|
function allFinished(learningSequence: LearningSequence) {
|
||||||
if (circleStore.circle) {
|
return allFinishedInLearningSequence(learningSequence);
|
||||||
return circleStore.circle.allFinishedInLearningSequence(
|
}
|
||||||
learningSequence.translation_key
|
|
||||||
);
|
function someFinished(learningSequence: LearningSequence) {
|
||||||
}
|
return someFinishedInLearningSequence(learningSequence);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|
@ -48,14 +44,14 @@ interface CirclePie extends d3.PieArcDatum<number> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const pieData = computed(() => {
|
const pieData = computed(() => {
|
||||||
const circle = circleStore.circle;
|
const circle = props.circle;
|
||||||
|
|
||||||
if (circle) {
|
if (circle) {
|
||||||
const pieWeights = new Array(Math.max(circle.learningSequences.length, 1)).fill(1);
|
const pieWeights = new Array(Math.max(circle.learning_sequences.length, 1)).fill(1);
|
||||||
const pieGenerator = d3.pie();
|
const pieGenerator = d3.pie();
|
||||||
const angles = pieGenerator(pieWeights);
|
const angles = pieGenerator(pieWeights);
|
||||||
let result = angles.map((angle) => {
|
let result = angles.map((angle) => {
|
||||||
const thisLearningSequence = circle.learningSequences[angle.index];
|
const thisLearningSequence = circle.learning_sequences[angle.index];
|
||||||
|
|
||||||
// Rotate the cirlce by PI (180 degrees) normally 0 = 12'o clock, now start at 6 o clock
|
// Rotate the cirlce by PI (180 degrees) normally 0 = 12'o clock, now start at 6 o clock
|
||||||
angle.startAngle += Math.PI;
|
angle.startAngle += Math.PI;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
|
import ItFullScreenModal from "@/components/ui/ItFullScreenModal.vue";
|
||||||
import type { Circle } from "@/services/circle";
|
import type { CircleType } from "@/types";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
circle: Circle | undefined;
|
circle: CircleType;
|
||||||
show: boolean;
|
show: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,10 @@ import { useRoute } from "vue-router";
|
||||||
import CircleDiagram from "./CircleDiagram.vue";
|
import CircleDiagram from "./CircleDiagram.vue";
|
||||||
import CircleOverview from "./CircleOverview.vue";
|
import CircleOverview from "./CircleOverview.vue";
|
||||||
import DocumentSection from "./DocumentSection.vue";
|
import DocumentSection from "./DocumentSection.vue";
|
||||||
import { useCourseSessionDetailQuery, useLearningPathQuery } from "@/composables";
|
import {
|
||||||
|
useCourseSessionDetailQuery,
|
||||||
|
useLearningPathWithCompletion,
|
||||||
|
} from "@/composables";
|
||||||
import { stringifyParse } from "@/utils/utils";
|
import { stringifyParse } from "@/utils/utils";
|
||||||
import { useCircleStore } from "@/stores/circle";
|
import { useCircleStore } from "@/stores/circle";
|
||||||
import LearningSequence from "@/pages/learningPath/circlePage/LearningSequence.vue";
|
import LearningSequence from "@/pages/learningPath/circlePage/LearningSequence.vue";
|
||||||
|
|
@ -29,6 +32,12 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
||||||
log.debug("CirclePage created", stringifyParse(props));
|
log.debug("CirclePage created", stringifyParse(props));
|
||||||
|
|
||||||
|
const lpQueryResult = useLearningPathWithCompletion(props.courseSlug);
|
||||||
|
|
||||||
|
const circle = computed(() => {
|
||||||
|
return lpQueryResult.findCircle(props.circleSlug);
|
||||||
|
});
|
||||||
|
|
||||||
const circleExperts = computed(() => {
|
const circleExperts = computed(() => {
|
||||||
if (circle.value) {
|
if (circle.value) {
|
||||||
return courseSessionDetailResult.filterCircleExperts(circle.value.slug);
|
return courseSessionDetailResult.filterCircleExperts(circle.value.slug);
|
||||||
|
|
@ -51,12 +60,6 @@ const showDuration = computed(() => {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const lpQueryResult = useLearningPathQuery(props.courseSlug);
|
|
||||||
|
|
||||||
const circle = computed(() => {
|
|
||||||
return lpQueryResult.findCircle(props.circleSlug);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => circle.value,
|
() => circle.value,
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -102,7 +105,7 @@ watch(
|
||||||
<div v-if="circle">
|
<div v-if="circle">
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<CircleOverview
|
<CircleOverview
|
||||||
:circle="circleStore.circle"
|
:circle="circle"
|
||||||
:show="circleStore.page === 'OVERVIEW'"
|
:show="circleStore.page === 'OVERVIEW'"
|
||||||
@closemodal="circleStore.page = 'INDEX'"
|
@closemodal="circleStore.page = 'INDEX'"
|
||||||
/>
|
/>
|
||||||
|
|
@ -151,7 +154,7 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-8 w-full">
|
<div class="mt-8 w-full">
|
||||||
<CircleDiagram></CircleDiagram>
|
<CircleDiagram v-if="circle" :circle="circle"></CircleDiagram>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!props.readonly" class="mt-4 border-t-2 lg:hidden">
|
<div v-if="!props.readonly" class="mt-4 border-t-2 lg:hidden">
|
||||||
<div
|
<div
|
||||||
|
|
@ -221,6 +224,7 @@ watch(
|
||||||
:key="learningSequence.id"
|
:key="learningSequence.id"
|
||||||
>
|
>
|
||||||
<LearningSequence
|
<LearningSequence
|
||||||
|
:course-slug="props.courseSlug"
|
||||||
:learning-sequence="learningSequence"
|
:learning-sequence="learningSequence"
|
||||||
:readonly="props.readonly"
|
:readonly="props.readonly"
|
||||||
></LearningSequence>
|
></LearningSequence>
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,28 @@ import LearningContentBadge from "@/pages/learningPath/LearningContentTypeBadge.
|
||||||
import { showIcon } from "@/pages/learningPath/circlePage/learningSequenceUtils";
|
import { showIcon } from "@/pages/learningPath/circlePage/learningSequenceUtils";
|
||||||
import { useCircleStore } from "@/stores/circle";
|
import { useCircleStore } from "@/stores/circle";
|
||||||
import type {
|
import type {
|
||||||
|
CourseCompletionStatus,
|
||||||
LearningContent,
|
LearningContent,
|
||||||
LearningContentAssignment,
|
LearningContentAssignment,
|
||||||
LearningContentEdoniqTest,
|
LearningContentEdoniqTest,
|
||||||
|
LearningContentWithCompletion,
|
||||||
LearningSequence,
|
LearningSequence,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { humanizeDuration } from "../../../utils/humanizeDuration";
|
|
||||||
import {
|
import {
|
||||||
itCheckboxDefaultIconCheckedTailwindClass,
|
itCheckboxDefaultIconCheckedTailwindClass,
|
||||||
itCheckboxDefaultIconUncheckedTailwindClass,
|
itCheckboxDefaultIconUncheckedTailwindClass,
|
||||||
} from "@/constants";
|
} from "@/constants";
|
||||||
|
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
|
||||||
|
import {
|
||||||
|
allFinishedInLearningSequence,
|
||||||
|
calcSelfEvaluationStatus,
|
||||||
|
someFinishedInLearningSequence,
|
||||||
|
} from "@/services/circle";
|
||||||
|
import { useLearningPathWithCompletion } from "@/composables";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
courseSlug: string;
|
||||||
learningSequence: LearningSequence;
|
learningSequence: LearningSequence;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
@ -26,30 +35,28 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
||||||
const circleStore = useCircleStore();
|
const circleStore = useCircleStore();
|
||||||
|
|
||||||
function toggleCompleted(learningContent: LearningContent) {
|
const lpQueryResult = useLearningPathWithCompletion(props.courseSlug);
|
||||||
// let completionStatus: CourseCompletionStatus = "SUCCESS";
|
|
||||||
// if (learningContent.completion_status === "SUCCESS") {
|
function toggleCompleted(learningContent: LearningContentWithCompletion) {
|
||||||
// completionStatus = "FAIL";
|
let completionStatus: CourseCompletionStatus = "SUCCESS";
|
||||||
// }
|
if (learningContent.completion_status === "SUCCESS") {
|
||||||
// circleStore.markCompletion(learningContent, completionStatus);
|
completionStatus = "FAIL";
|
||||||
|
}
|
||||||
|
lpQueryResult.markCompletion(learningContent, completionStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
const someFinished = computed(() => {
|
const someFinished = computed(() => {
|
||||||
// if (props.learningSequence && circleStore.circle) {
|
if (props.learningSequence) {
|
||||||
// return circleStore.circle.someFinishedInLearningSequence(
|
return someFinishedInLearningSequence(props.learningSequence);
|
||||||
// props.learningSequence.translation_key
|
}
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
const allFinished = computed(() => {
|
const allFinished = computed(() => {
|
||||||
// if (props.learningSequence && circleStore.circle) {
|
if (props.learningSequence) {
|
||||||
// return circleStore.circle.allFinishedInLearningSequence(
|
return allFinishedInLearningSequence(props.learningSequence);
|
||||||
// props.learningSequence.translation_key
|
}
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
@ -78,13 +85,13 @@ const continueTranslationKeyTuple = computed(() => {
|
||||||
|
|
||||||
const learningSequenceBorderClass = computed(() => {
|
const learningSequenceBorderClass = computed(() => {
|
||||||
let result: string[] = [];
|
let result: string[] = [];
|
||||||
if (props.learningSequence && circleStore.circle) {
|
if (props.learningSequence) {
|
||||||
if (allFinished.value) {
|
if (allFinished.value) {
|
||||||
result = ["border-l-4", "border-l-green-500"];
|
result = ["border-l-4", "border-l-green-500"];
|
||||||
} else if (someFinished.value) {
|
} else if (someFinished.value) {
|
||||||
result = ["border-l-4", "border-l-sky-500"];
|
result = ["border-l-4", "border-l-sky-500"];
|
||||||
} else {
|
} else {
|
||||||
result = ["border-l-gray-500"];
|
result = ["border-l", "border-l-gray-500"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,9 +132,9 @@ function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
|
||||||
<h3 class="text-large font-semibold">
|
<h3 class="text-large font-semibold">
|
||||||
{{ learningSequence.title }}
|
{{ learningSequence.title }}
|
||||||
</h3>
|
</h3>
|
||||||
<div v-if="learningSequence.minutes > 0">
|
<!-- <div v-if="learningSequence.minutes > 0">-->
|
||||||
{{ humanizeDuration(learningSequence.minutes) }}
|
<!-- {{ humanizeDuration(learningSequence.minutes) }}-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ol class="border bg-white px-4 lg:px-6" :class="learningSequenceBorderClass">
|
<ol class="border bg-white px-4 lg:px-6" :class="learningSequenceBorderClass">
|
||||||
|
|
@ -144,9 +151,9 @@ function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
|
||||||
<div class="font-semibold">
|
<div class="font-semibold">
|
||||||
{{ learningUnit.title }}
|
{{ learningUnit.title }}
|
||||||
</div>
|
</div>
|
||||||
<div v-if="learningUnit.minutes > 0" class="whitespace-nowrap">
|
<!-- <div v-if="learningUnit.minutes > 0" class="whitespace-nowrap">-->
|
||||||
{{ humanizeDuration(learningUnit.minutes) }}
|
<!-- {{ humanizeDuration(learningUnit.minutes) }}-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</div>
|
</div>
|
||||||
<ol>
|
<ol>
|
||||||
<li
|
<li
|
||||||
|
|
@ -254,31 +261,31 @@ function checkboxIconUncheckedTailwindClass(lc: LearningContent) {
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
<!-- <div-->
|
<div
|
||||||
<!-- v-if="learningUnit.children.length"-->
|
v-if="learningUnit.performance_criteria.length"
|
||||||
<!-- :class="{ 'cursor-pointer': !props.readonly }"-->
|
:class="{ 'cursor-pointer': !props.readonly }"
|
||||||
<!-- :data-cy="`${learningUnit.slug}`"-->
|
:data-cy="`${learningUnit.slug}`"
|
||||||
<!-- @click="!props.readonly && circleStore.openSelfEvaluation(learningUnit)"-->
|
@click="!props.readonly && circleStore.openSelfEvaluation(learningUnit)"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- <div-->
|
<div
|
||||||
<!-- v-if="circleStore.calcSelfEvaluationStatus(learningUnit) === 'SUCCESS'"-->
|
v-if="calcSelfEvaluationStatus(learningUnit) === 'SUCCESS'"
|
||||||
<!-- class="self-evaluation-success flex items-center gap-4 pb-6"-->
|
class="self-evaluation-success flex items-center gap-4 pb-6"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- <it-icon-smiley-happy class="mr-4 h-8 w-8 flex-none" data-cy="success" />-->
|
<it-icon-smiley-happy class="mr-4 h-8 w-8 flex-none" data-cy="success" />
|
||||||
<!-- <div>{{ $t("selfEvaluation.selfEvaluationYes") }}</div>-->
|
<div>{{ $t("selfEvaluation.selfEvaluationYes") }}</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div-->
|
<div
|
||||||
<!-- v-else-if="circleStore.calcSelfEvaluationStatus(learningUnit) === 'FAIL'"-->
|
v-else-if="calcSelfEvaluationStatus(learningUnit) === 'FAIL'"
|
||||||
<!-- class="self-evaluation-fail flex items-center gap-4 pb-6"-->
|
class="self-evaluation-fail flex items-center gap-4 pb-6"
|
||||||
<!-- >-->
|
>
|
||||||
<!-- <it-icon-smiley-thinking class="mr-4 h-8 w-8 flex-none" data-cy="fail" />-->
|
<it-icon-smiley-thinking class="mr-4 h-8 w-8 flex-none" data-cy="fail" />
|
||||||
<!-- <div>{{ $t("selfEvaluation.selfEvaluationNo") }}</div>-->
|
<div>{{ $t("selfEvaluation.selfEvaluationNo") }}</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- <div v-else class="self-evaluation-unknown flex items-center gap-4 pb-6">-->
|
<div v-else class="self-evaluation-unknown flex items-center gap-4 pb-6">
|
||||||
<!-- <it-icon-smiley-neutral class="mr-4 h-8 w-8 flex-none" data-cy="unknown" />-->
|
<it-icon-smiley-neutral class="mr-4 h-8 w-8 flex-none" data-cy="unknown" />
|
||||||
<!-- <div>{{ $t("a.Selbsteinschätzung") }}</div>-->
|
<div>{{ $t("a.Selbsteinschätzung") }}</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
<!-- </div>-->
|
</div>
|
||||||
|
|
||||||
<hr v-if="!learningUnit.last" class="-mx-4 text-gray-500" />
|
<hr v-if="!learningUnit.last" class="-mx-4 text-gray-500" />
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
||||||
import LearningPathContinueButton from "@/pages/learningPath/learningPathPage/LearningPathContinueButton.vue";
|
import LearningPathContinueButton from "@/pages/learningPath/learningPathPage/LearningPathContinueButton.vue";
|
||||||
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
|
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
|
||||||
import type { Circle } from "@/services/circle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
import type { LearningPath } from "@/services/learningPath";
|
||||||
import type { Topic } from "@/types";
|
import type { Topic } from "@/types";
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPath: LearningPath | undefined;
|
learningPath: LearningPath | undefined;
|
||||||
circle: Circle;
|
circle: OldCircle;
|
||||||
topic: Topic;
|
topic: Topic;
|
||||||
isFirstCircle: boolean;
|
isFirstCircle: boolean;
|
||||||
isLastCircle: boolean;
|
isLastCircle: boolean;
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
import LearningPathCircle from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
||||||
import LearningPathContinueButton from "@/pages/learningPath/learningPathPage/LearningPathContinueButton.vue";
|
import LearningPathContinueButton from "@/pages/learningPath/learningPathPage/LearningPathContinueButton.vue";
|
||||||
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
|
import { calculateCircleSectorData } from "@/pages/learningPath/learningPathPage/utils";
|
||||||
import type { Circle } from "@/services/circle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
import type { LearningPath } from "@/services/learningPath";
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
learningPath: LearningPath | undefined;
|
learningPath: LearningPath | undefined;
|
||||||
circle: Circle;
|
circle: OldCircle;
|
||||||
isCurrentCircle: boolean;
|
isCurrentCircle: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
|
import LearningPathCircleListTile from "@/pages/learningPath/learningPathPage/LearningPathCircleListTile.vue";
|
||||||
import type { Circle } from "@/services/circle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
import type { LearningPath } from "@/services/learningPath";
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
|
@ -10,7 +10,7 @@ const props = defineProps<{
|
||||||
|
|
||||||
const topics = computed(() => props.learningPath?.topics ?? []);
|
const topics = computed(() => props.learningPath?.topics ?? []);
|
||||||
|
|
||||||
const isCurrentCircle = (circle: Circle) =>
|
const isCurrentCircle = (circle: OldCircle) =>
|
||||||
props.learningPath?.nextLearningContent?.parentCircle === circle;
|
props.learningPath?.nextLearningContent?.parentCircle === circle;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import LearningPathCircleColumn from "@/pages/learningPath/learningPathPage/LearningPathCircleColumn.vue";
|
import LearningPathCircleColumn from "@/pages/learningPath/learningPathPage/LearningPathCircleColumn.vue";
|
||||||
import LearningPathScrollButton from "@/pages/learningPath/learningPathPage/LearningPathScrollButton.vue";
|
import LearningPathScrollButton from "@/pages/learningPath/learningPathPage/LearningPathScrollButton.vue";
|
||||||
import type { Circle } from "@/services/circle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
import type { LearningPath } from "@/services/learningPath";
|
||||||
import { useScroll } from "@vueuse/core";
|
import { useScroll } from "@vueuse/core";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
@ -25,7 +25,7 @@ const isLastCircle = (topicIndex: number, circleIndex: number, numCircles: numbe
|
||||||
topicIndex === (props.learningPath?.topics ?? []).length - 1 &&
|
topicIndex === (props.learningPath?.topics ?? []).length - 1 &&
|
||||||
circleIndex === numCircles - 1;
|
circleIndex === numCircles - 1;
|
||||||
|
|
||||||
const isCurrentCircle = (circle: Circle) =>
|
const isCurrentCircle = (circle: OldCircle) =>
|
||||||
props.learningPath?.nextLearningContent?.parentCircle === circle;
|
props.learningPath?.nextLearningContent?.parentCircle === circle;
|
||||||
|
|
||||||
const scrollRight = () => scrollLearnPathDiagram(scrollIncrement);
|
const scrollRight = () => scrollLearnPathDiagram(scrollIncrement);
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import type {
|
||||||
CircleSectorData,
|
CircleSectorData,
|
||||||
CircleSectorProgress,
|
CircleSectorProgress,
|
||||||
} from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
} from "@/pages/learningPath/learningPathPage/LearningPathCircle.vue";
|
||||||
import type { Circle } from "@/services/circle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
|
|
||||||
export function calculateCircleSectorData(circle: Circle): CircleSectorData[] {
|
export function calculateCircleSectorData(circle: OldCircle): CircleSectorData[] {
|
||||||
return circle.learningSequences.map((ls) => {
|
return circle.learningSequences.map((ls) => {
|
||||||
let progress: CircleSectorProgress = "none";
|
let progress: CircleSectorProgress = "none";
|
||||||
if (circle.allFinishedInLearningSequence(ls.translation_key)) {
|
if (circle.allFinishedInLearningSequence(ls.translation_key)) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import type { WagtailCircle } from "@/types";
|
import type { WagtailCircle } from "@/types";
|
||||||
import { describe, it } from "vitest";
|
import { describe, it } from "vitest";
|
||||||
import { Circle } from "../circle";
|
import { OldCircle } from "../oldCircle";
|
||||||
import data from "./learning_path_json.json";
|
import data from "./learning_path_json.json";
|
||||||
|
|
||||||
describe("Circle.parseJson", () => {
|
describe("Circle.parseJson", () => {
|
||||||
|
|
@ -8,7 +8,7 @@ describe("Circle.parseJson", () => {
|
||||||
const cirleData = data.children.find(
|
const cirleData = data.children.find(
|
||||||
(c) => c.slug === "test-lehrgang-lp-circle-fahrzeug"
|
(c) => c.slug === "test-lehrgang-lp-circle-fahrzeug"
|
||||||
) as unknown as WagtailCircle;
|
) as unknown as WagtailCircle;
|
||||||
const circle = Circle.fromJson(cirleData, undefined);
|
const circle = OldCircle.fromJson(cirleData, undefined);
|
||||||
expect(circle.learningSequences.length).toBe(3);
|
expect(circle.learningSequences.length).toBe(3);
|
||||||
expect(circle.flatLearningContents.length).toBe(9);
|
expect(circle.flatLearningContents.length).toBe(9);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,268 +1,70 @@
|
||||||
import type { LearningPath } from "@/services/learningPath";
|
|
||||||
import type {
|
import type {
|
||||||
CircleChild,
|
CircleType,
|
||||||
CircleGoal,
|
CourseCompletionStatus,
|
||||||
CircleJobSituation,
|
|
||||||
CourseCompletion,
|
|
||||||
LearningContent,
|
|
||||||
LearningContentInterface,
|
|
||||||
LearningSequence,
|
LearningSequence,
|
||||||
LearningUnit,
|
LearningUnit,
|
||||||
LearningUnitPerformanceCriteria,
|
|
||||||
WagtailCircle,
|
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
import groupBy from "lodash/groupBy";
|
|
||||||
import partition from "lodash/partition";
|
|
||||||
import values from "lodash/values";
|
|
||||||
import log from "loglevel";
|
|
||||||
|
|
||||||
function isLearningContentType(object: any): object is LearningContent {
|
export function circleFlatChildren(circle: CircleType) {
|
||||||
return (
|
return [
|
||||||
object?.content_type === "learnpath.LearningContentAssignment" ||
|
...circleFlatLearningContents(circle),
|
||||||
object?.content_type === "learnpath.LearningContentAttendanceCourse" ||
|
...circleFlatLearningUnits(circle).flatMap((lu) => {
|
||||||
object?.content_type === "learnpath.LearningContentDocumentList" ||
|
return lu.performance_criteria;
|
||||||
object?.content_type === "learnpath.LearningContentFeedback" ||
|
}),
|
||||||
object?.content_type === "learnpath.LearningContentLearningModule" ||
|
];
|
||||||
object?.content_type === "learnpath.LearningContentMediaLibrary" ||
|
|
||||||
object?.content_type === "learnpath.LearningContentPlaceholder" ||
|
|
||||||
object?.content_type === "learnpath.LearningContentRichText" ||
|
|
||||||
object?.content_type === "learnpath.LearningContentEdoniqTest" ||
|
|
||||||
object?.content_type === "learnpath.LearningContentVideo"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseLearningSequences(
|
export function circleFlatLearningContents(circle: CircleType) {
|
||||||
circle: Circle,
|
return circleFlatLearningUnits(circle).flatMap((lu) => {
|
||||||
children: CircleChild[]
|
return lu.learning_contents;
|
||||||
): LearningSequence[] {
|
|
||||||
let learningSequence: LearningSequence | undefined;
|
|
||||||
let learningUnit: LearningUnit | undefined;
|
|
||||||
let learningContent: LearningContent | undefined;
|
|
||||||
let previousLearningContent: LearningContent | undefined;
|
|
||||||
const result: LearningSequence[] = [];
|
|
||||||
|
|
||||||
children.forEach((child) => {
|
|
||||||
if (child.content_type === "learnpath.LearningSequence") {
|
|
||||||
if (learningSequence) {
|
|
||||||
if (learningUnit) {
|
|
||||||
learningUnit.last = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
learningSequence = Object.assign(child, { learningUnits: [] });
|
|
||||||
result.push(learningSequence);
|
|
||||||
} else if (child.content_type === "learnpath.LearningUnit") {
|
|
||||||
if (!learningSequence) {
|
|
||||||
throw new Error("LearningUnit found before LearningSequence");
|
|
||||||
}
|
|
||||||
|
|
||||||
learningUnit = Object.assign(child, {
|
|
||||||
learningContents: [],
|
|
||||||
parentLearningSequence: learningSequence,
|
|
||||||
parentCircle: circle,
|
|
||||||
children: child.children.map((c) => {
|
|
||||||
c.parentLearningUnit = learningUnit;
|
|
||||||
c.parentLearningSequence = learningSequence;
|
|
||||||
return c;
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
learningSequence.learningUnits.push(learningUnit);
|
|
||||||
} else if (isLearningContentType(child)) {
|
|
||||||
if (!learningUnit) {
|
|
||||||
throw new Error(`LearningContent found before LearningUnit ${child.slug}`);
|
|
||||||
}
|
|
||||||
previousLearningContent = learningContent;
|
|
||||||
|
|
||||||
learningContent = Object.assign(child, {
|
|
||||||
parentCircle: circle,
|
|
||||||
parentLearningSequence: learningSequence,
|
|
||||||
parentLearningUnit: learningUnit,
|
|
||||||
previousLearningContent: previousLearningContent,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (previousLearningContent) {
|
|
||||||
previousLearningContent.nextLearningContent = learningContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
learningUnit.learningContents.push(child);
|
|
||||||
} else {
|
|
||||||
log.error("Unknown CircleChild found...", child);
|
|
||||||
throw new Error("Unknown CircleChild found...");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (learningUnit) {
|
export function circleFlatLearningUnits(circle: CircleType) {
|
||||||
learningUnit.last = true;
|
return circle.learning_sequences.flatMap((ls) => {
|
||||||
} else {
|
return ls.learning_units;
|
||||||
throw new Error(
|
|
||||||
"Finished with LearningContent but there is no LearningSequence and LearningUnit"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sum minutes
|
|
||||||
result.forEach((learningSequence) => {
|
|
||||||
learningSequence.minutes = 0;
|
|
||||||
learningSequence.learningUnits.forEach((learningUnit) => {
|
|
||||||
learningUnit.minutes = 0;
|
|
||||||
learningUnit.learningContents.forEach((learningContent) => {
|
|
||||||
learningUnit.minutes += learningContent.minutes;
|
|
||||||
});
|
|
||||||
learningSequence.minutes += learningUnit.minutes;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Circle implements WagtailCircle {
|
export function learningSequenceFlatChildren(ls: LearningSequence) {
|
||||||
readonly content_type = "learnpath.Circle";
|
return [
|
||||||
readonly learningSequences: LearningSequence[];
|
...ls.learning_units.flatMap((lu) => {
|
||||||
|
return lu.learning_contents;
|
||||||
nextCircle?: Circle;
|
}),
|
||||||
previousCircle?: Circle;
|
...ls.learning_units.flatMap((lu) => {
|
||||||
|
return lu.performance_criteria;
|
||||||
constructor(
|
}),
|
||||||
public readonly id: string,
|
];
|
||||||
public readonly slug: string,
|
}
|
||||||
public readonly title: string,
|
|
||||||
public readonly translation_key: string,
|
export function someFinishedInLearningSequence(ls: LearningSequence) {
|
||||||
public readonly frontend_url: string,
|
return learningSequenceFlatChildren(ls).some((lc) => {
|
||||||
public readonly description: string,
|
return lc.completion_status === "SUCCESS";
|
||||||
public readonly children: CircleChild[],
|
});
|
||||||
public readonly goal_description: string,
|
}
|
||||||
public readonly goals: CircleGoal[],
|
|
||||||
public readonly job_situation_description: string,
|
export function allFinishedInLearningSequence(ls: LearningSequence) {
|
||||||
public readonly job_situations: CircleJobSituation[],
|
return learningSequenceFlatChildren(ls).every((lc) => {
|
||||||
public readonly parentLearningPath?: LearningPath
|
return lc.completion_status === "SUCCESS";
|
||||||
) {
|
});
|
||||||
this.learningSequences = parseLearningSequences(this, this.children);
|
}
|
||||||
}
|
|
||||||
|
export function calcSelfEvaluationStatus(
|
||||||
public static fromJson(
|
learningUnit: LearningUnit
|
||||||
wagtailCircle: WagtailCircle,
|
): CourseCompletionStatus {
|
||||||
learningPath?: LearningPath
|
if (learningUnit.performance_criteria.length > 0) {
|
||||||
): Circle {
|
if (
|
||||||
// TODO add error checking when the data does not conform to the schema
|
learningUnit.performance_criteria.every((q) => q.completion_status === "SUCCESS")
|
||||||
return new Circle(
|
) {
|
||||||
wagtailCircle.id,
|
return "SUCCESS";
|
||||||
wagtailCircle.slug,
|
}
|
||||||
wagtailCircle.title,
|
if (
|
||||||
wagtailCircle.translation_key,
|
learningUnit.performance_criteria.every(
|
||||||
wagtailCircle.frontend_url,
|
(q) => q.completion_status === "FAIL" || q.completion_status === "SUCCESS"
|
||||||
wagtailCircle.description,
|
)
|
||||||
wagtailCircle.children,
|
) {
|
||||||
wagtailCircle.goal_description,
|
return "FAIL";
|
||||||
wagtailCircle.goals,
|
}
|
||||||
wagtailCircle.job_situation_description,
|
}
|
||||||
wagtailCircle.job_situations,
|
return "UNKNOWN";
|
||||||
learningPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get flatChildren(): (
|
|
||||||
| LearningContentInterface
|
|
||||||
| LearningUnitPerformanceCriteria
|
|
||||||
)[] {
|
|
||||||
const result: (LearningContentInterface | LearningUnitPerformanceCriteria)[] = [];
|
|
||||||
this.learningSequences.forEach((learningSequence) => {
|
|
||||||
learningSequence.learningUnits.forEach((learningUnit) => {
|
|
||||||
learningUnit.children.forEach((performanceCriteria) => {
|
|
||||||
result.push(performanceCriteria);
|
|
||||||
});
|
|
||||||
learningUnit.learningContents.forEach((learningContent) => {
|
|
||||||
result.push(learningContent);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get flatLearningContents(): LearningContent[] {
|
|
||||||
const result: LearningContent[] = [];
|
|
||||||
this.learningSequences.forEach((learningSequence) => {
|
|
||||||
learningSequence.learningUnits.forEach((learningUnit) => {
|
|
||||||
learningUnit.learningContents.forEach((learningContent) => {
|
|
||||||
result.push(learningContent);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get flatLearningUnits(): LearningUnit[] {
|
|
||||||
const result: LearningUnit[] = [];
|
|
||||||
this.learningSequences.forEach((learningSequence) => {
|
|
||||||
learningSequence.learningUnits.forEach((learningUnit) => {
|
|
||||||
result.push(learningUnit);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public someFinishedInLearningSequence(translationKey: string): boolean {
|
|
||||||
if (translationKey) {
|
|
||||||
return (
|
|
||||||
this.flatChildren.filter((lc) => {
|
|
||||||
return (
|
|
||||||
lc.completion_status === "SUCCESS" &&
|
|
||||||
lc.parentLearningSequence?.translation_key === translationKey
|
|
||||||
);
|
|
||||||
}).length > 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public allFinishedInLearningSequence(translationKey: string): boolean {
|
|
||||||
if (translationKey) {
|
|
||||||
const [performanceCriteria, learningContents] = partition(
|
|
||||||
this.flatChildren.filter(
|
|
||||||
(lc) => lc.parentLearningSequence?.translation_key === translationKey
|
|
||||||
),
|
|
||||||
function (child) {
|
|
||||||
return child.content_type === "competence.PerformanceCriteria";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const groupedPerformanceCriteria = values(
|
|
||||||
groupBy(performanceCriteria, (pc) => pc.parentLearningUnit?.id)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
learningContents.every((lc) => lc.completion_status === "SUCCESS") &&
|
|
||||||
(groupedPerformanceCriteria.length === 0 ||
|
|
||||||
groupedPerformanceCriteria.every((group) =>
|
|
||||||
group.every(
|
|
||||||
(pc) =>
|
|
||||||
pc.completion_status === "SUCCESS" || pc.completion_status === "FAIL"
|
|
||||||
)
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isComplete(): boolean {
|
|
||||||
return this.learningSequences.every((ls) =>
|
|
||||||
this.allFinishedInLearningSequence(ls.translation_key)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public parseCompletionData(completionData: CourseCompletion[]) {
|
|
||||||
this.flatChildren.forEach((page) => {
|
|
||||||
const pageIndex = completionData.findIndex((e) => {
|
|
||||||
return e.page_id === page.id;
|
|
||||||
});
|
|
||||||
if (pageIndex >= 0) {
|
|
||||||
page.completion_status = completionData[pageIndex].completion_status;
|
|
||||||
} else {
|
|
||||||
page.completion_status = "UNKNOWN";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.parentLearningPath) {
|
|
||||||
this.parentLearningPath.calcNextLearningContent(completionData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import orderBy from "lodash/orderBy";
|
import orderBy from "lodash/orderBy";
|
||||||
|
|
||||||
import { Circle } from "@/services/circle";
|
import { OldCircle } from "@/services/oldCircle";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
import { useLearningPathStore } from "@/stores/learningPath";
|
import { useLearningPathStore } from "@/stores/learningPath";
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -12,6 +12,8 @@ import type {
|
||||||
WagtailLearningPath,
|
WagtailLearningPath,
|
||||||
} from "@/types";
|
} from "@/types";
|
||||||
|
|
||||||
|
// FIXME: remove
|
||||||
|
|
||||||
export interface ContinueData {
|
export interface ContinueData {
|
||||||
url: string;
|
url: string;
|
||||||
has_no_progress: boolean;
|
has_no_progress: boolean;
|
||||||
|
|
@ -35,7 +37,7 @@ function getLastCompleted(courseSlug: string, completionData: CourseCompletion[]
|
||||||
export class LearningPath implements WagtailLearningPath {
|
export class LearningPath implements WagtailLearningPath {
|
||||||
readonly content_type = "learnpath.LearningPath";
|
readonly content_type = "learnpath.LearningPath";
|
||||||
public topics: Topic[];
|
public topics: Topic[];
|
||||||
public circles: Circle[];
|
public circles: OldCircle[];
|
||||||
public nextLearningContent?: LearningContentInterface;
|
public nextLearningContent?: LearningContentInterface;
|
||||||
|
|
||||||
public static fromJson(
|
public static fromJson(
|
||||||
|
|
@ -81,7 +83,7 @@ export class LearningPath implements WagtailLearningPath {
|
||||||
topic = Object.assign(page, { circles: [] });
|
topic = Object.assign(page, { circles: [] });
|
||||||
}
|
}
|
||||||
if (page.content_type === "learnpath.Circle") {
|
if (page.content_type === "learnpath.Circle") {
|
||||||
const circle = Circle.fromJson(page, this);
|
const circle = OldCircle.fromJson(page, this);
|
||||||
if (completionData && completionData.length > 0) {
|
if (completionData && completionData.length > 0) {
|
||||||
circle.parseCompletionData(completionData);
|
circle.parseCompletionData(completionData);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
import type { LearningPath } from "@/services/learningPath";
|
||||||
|
import type {
|
||||||
|
CircleChild,
|
||||||
|
CircleGoal,
|
||||||
|
CircleJobSituation,
|
||||||
|
CourseCompletion,
|
||||||
|
LearningContent,
|
||||||
|
LearningContentInterface,
|
||||||
|
LearningSequence,
|
||||||
|
LearningUnit,
|
||||||
|
LearningUnitPerformanceCriteria,
|
||||||
|
WagtailCircle,
|
||||||
|
} from "@/types";
|
||||||
|
import groupBy from "lodash/groupBy";
|
||||||
|
import partition from "lodash/partition";
|
||||||
|
import values from "lodash/values";
|
||||||
|
import log from "loglevel";
|
||||||
|
|
||||||
|
// FIXME: remove
|
||||||
|
|
||||||
|
function isLearningContentType(object: any): object is LearningContent {
|
||||||
|
return (
|
||||||
|
object?.content_type === "learnpath.LearningContentAssignment" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentAttendanceCourse" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentDocumentList" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentFeedback" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentLearningModule" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentMediaLibrary" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentPlaceholder" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentRichText" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentEdoniqTest" ||
|
||||||
|
object?.content_type === "learnpath.LearningContentVideo"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseLearningSequences(
|
||||||
|
circle: OldCircle,
|
||||||
|
children: CircleChild[]
|
||||||
|
): LearningSequence[] {
|
||||||
|
let learningSequence: LearningSequence | undefined;
|
||||||
|
let learningUnit: LearningUnit | undefined;
|
||||||
|
let learningContent: LearningContent | undefined;
|
||||||
|
let previousLearningContent: LearningContent | undefined;
|
||||||
|
const result: LearningSequence[] = [];
|
||||||
|
|
||||||
|
children.forEach((child) => {
|
||||||
|
if (child.content_type === "learnpath.LearningSequence") {
|
||||||
|
if (learningSequence) {
|
||||||
|
if (learningUnit) {
|
||||||
|
learningUnit.last = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
learningSequence = Object.assign(child, { learningUnits: [] });
|
||||||
|
result.push(learningSequence);
|
||||||
|
} else if (child.content_type === "learnpath.LearningUnit") {
|
||||||
|
if (!learningSequence) {
|
||||||
|
throw new Error("LearningUnit found before LearningSequence");
|
||||||
|
}
|
||||||
|
|
||||||
|
learningUnit = Object.assign(child, {
|
||||||
|
learningContents: [],
|
||||||
|
parentLearningSequence: learningSequence,
|
||||||
|
parentCircle: circle,
|
||||||
|
children: child.children.map((c) => {
|
||||||
|
c.parentLearningUnit = learningUnit;
|
||||||
|
c.parentLearningSequence = learningSequence;
|
||||||
|
return c;
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
learningSequence.learningUnits.push(learningUnit);
|
||||||
|
} else if (isLearningContentType(child)) {
|
||||||
|
if (!learningUnit) {
|
||||||
|
throw new Error(`LearningContent found before LearningUnit ${child.slug}`);
|
||||||
|
}
|
||||||
|
previousLearningContent = learningContent;
|
||||||
|
|
||||||
|
learningContent = Object.assign(child, {
|
||||||
|
parentCircle: circle,
|
||||||
|
parentLearningSequence: learningSequence,
|
||||||
|
parentLearningUnit: learningUnit,
|
||||||
|
previousLearningContent: previousLearningContent,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (previousLearningContent) {
|
||||||
|
previousLearningContent.nextLearningContent = learningContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
learningUnit.learningContents.push(child);
|
||||||
|
} else {
|
||||||
|
log.error("Unknown CircleChild found...", child);
|
||||||
|
throw new Error("Unknown CircleChild found...");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (learningUnit) {
|
||||||
|
learningUnit.last = true;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
"Finished with LearningContent but there is no LearningSequence and LearningUnit"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sum minutes
|
||||||
|
result.forEach((learningSequence) => {
|
||||||
|
learningSequence.minutes = 0;
|
||||||
|
learningSequence.learningUnits.forEach((learningUnit) => {
|
||||||
|
learningUnit.minutes = 0;
|
||||||
|
learningUnit.learningContents.forEach((learningContent) => {
|
||||||
|
learningUnit.minutes += learningContent.minutes;
|
||||||
|
});
|
||||||
|
learningSequence.minutes += learningUnit.minutes;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OldCircle implements WagtailCircle {
|
||||||
|
readonly content_type = "learnpath.Circle";
|
||||||
|
readonly learningSequences: LearningSequence[];
|
||||||
|
|
||||||
|
nextCircle?: OldCircle;
|
||||||
|
previousCircle?: OldCircle;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public readonly id: string,
|
||||||
|
public readonly slug: string,
|
||||||
|
public readonly title: string,
|
||||||
|
public readonly translation_key: string,
|
||||||
|
public readonly frontend_url: string,
|
||||||
|
public readonly description: string,
|
||||||
|
public readonly children: CircleChild[],
|
||||||
|
public readonly goal_description: string,
|
||||||
|
public readonly goals: CircleGoal[],
|
||||||
|
public readonly job_situation_description: string,
|
||||||
|
public readonly job_situations: CircleJobSituation[],
|
||||||
|
public readonly parentLearningPath?: LearningPath
|
||||||
|
) {
|
||||||
|
this.learningSequences = parseLearningSequences(this, this.children);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromJson(
|
||||||
|
wagtailCircle: WagtailCircle,
|
||||||
|
learningPath?: LearningPath
|
||||||
|
): OldCircle {
|
||||||
|
// TODO add error checking when the data does not conform to the schema
|
||||||
|
return new OldCircle(
|
||||||
|
wagtailCircle.id,
|
||||||
|
wagtailCircle.slug,
|
||||||
|
wagtailCircle.title,
|
||||||
|
wagtailCircle.translation_key,
|
||||||
|
wagtailCircle.frontend_url,
|
||||||
|
wagtailCircle.description,
|
||||||
|
wagtailCircle.children,
|
||||||
|
wagtailCircle.goal_description,
|
||||||
|
wagtailCircle.goals,
|
||||||
|
wagtailCircle.job_situation_description,
|
||||||
|
wagtailCircle.job_situations,
|
||||||
|
learningPath
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get flatChildren(): (
|
||||||
|
| LearningContentInterface
|
||||||
|
| LearningUnitPerformanceCriteria
|
||||||
|
)[] {
|
||||||
|
const result: (LearningContentInterface | LearningUnitPerformanceCriteria)[] = [];
|
||||||
|
this.learningSequences.forEach((learningSequence) => {
|
||||||
|
learningSequence.learningUnits.forEach((learningUnit) => {
|
||||||
|
learningUnit.children.forEach((performanceCriteria) => {
|
||||||
|
result.push(performanceCriteria);
|
||||||
|
});
|
||||||
|
learningUnit.learningContents.forEach((learningContent) => {
|
||||||
|
result.push(learningContent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get flatLearningContents(): LearningContent[] {
|
||||||
|
const result: LearningContent[] = [];
|
||||||
|
this.learningSequences.forEach((learningSequence) => {
|
||||||
|
learningSequence.learningUnits.forEach((learningUnit) => {
|
||||||
|
learningUnit.learningContents.forEach((learningContent) => {
|
||||||
|
result.push(learningContent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get flatLearningUnits(): LearningUnit[] {
|
||||||
|
const result: LearningUnit[] = [];
|
||||||
|
this.learningSequences.forEach((learningSequence) => {
|
||||||
|
learningSequence.learningUnits.forEach((learningUnit) => {
|
||||||
|
result.push(learningUnit);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public someFinishedInLearningSequence(translationKey: string): boolean {
|
||||||
|
if (translationKey) {
|
||||||
|
return (
|
||||||
|
this.flatChildren.filter((lc) => {
|
||||||
|
return (
|
||||||
|
lc.completion_status === "SUCCESS" &&
|
||||||
|
lc.parentLearningSequence?.translation_key === translationKey
|
||||||
|
);
|
||||||
|
}).length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public allFinishedInLearningSequence(translationKey: string): boolean {
|
||||||
|
if (translationKey) {
|
||||||
|
const [performanceCriteria, learningContents] = partition(
|
||||||
|
this.flatChildren.filter(
|
||||||
|
(lc) => lc.parentLearningSequence?.translation_key === translationKey
|
||||||
|
),
|
||||||
|
function (child) {
|
||||||
|
return child.content_type === "competence.PerformanceCriteria";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const groupedPerformanceCriteria = values(
|
||||||
|
groupBy(performanceCriteria, (pc) => pc.parentLearningUnit?.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
learningContents.every((lc) => lc.completion_status === "SUCCESS") &&
|
||||||
|
(groupedPerformanceCriteria.length === 0 ||
|
||||||
|
groupedPerformanceCriteria.every((group) =>
|
||||||
|
group.every(
|
||||||
|
(pc) =>
|
||||||
|
pc.completion_status === "SUCCESS" || pc.completion_status === "FAIL"
|
||||||
|
)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseCompletionData(completionData: CourseCompletion[]) {
|
||||||
|
this.flatChildren.forEach((page) => {
|
||||||
|
const pageIndex = completionData.findIndex((e) => {
|
||||||
|
return e.page_id === page.id;
|
||||||
|
});
|
||||||
|
if (pageIndex >= 0) {
|
||||||
|
page.completion_status = completionData[pageIndex].completion_status;
|
||||||
|
} else {
|
||||||
|
page.completion_status = "UNKNOWN";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.parentLearningPath) {
|
||||||
|
this.parentLearningPath.calcNextLearningContent(completionData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type { Circle } from "@/services/circle";
|
import type { OldCircle } from "@/services/oldCircle";
|
||||||
import { useCompletionStore } from "@/stores/completion";
|
import { useCompletionStore } from "@/stores/completion";
|
||||||
import { useLearningPathStore } from "@/stores/learningPath";
|
import { useLearningPathStore } from "@/stores/learningPath";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
|
|
@ -14,7 +14,7 @@ import { defineStore } from "pinia";
|
||||||
import type { RouteLocationNormalized } from "vue-router";
|
import type { RouteLocationNormalized } from "vue-router";
|
||||||
|
|
||||||
export type CircleStoreState = {
|
export type CircleStoreState = {
|
||||||
circle: Circle | undefined;
|
circle: OldCircle | undefined;
|
||||||
page: "INDEX" | "OVERVIEW";
|
page: "INDEX" | "OVERVIEW";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ export const useCircleStore = defineStore({
|
||||||
courseSlug: string,
|
courseSlug: string,
|
||||||
circleSlug: string,
|
circleSlug: string,
|
||||||
userId: string | undefined = undefined
|
userId: string | undefined = undefined
|
||||||
): Promise<Circle> {
|
): Promise<OldCircle> {
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
userId = userStore.id;
|
userId = userStore.id;
|
||||||
|
|
@ -158,21 +158,6 @@ export const useCircleStore = defineStore({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
calcSelfEvaluationStatus(learningUnit: LearningUnit): CourseCompletionStatus {
|
|
||||||
if (learningUnit.children.length > 0) {
|
|
||||||
if (learningUnit.children.every((q) => q.completion_status === "SUCCESS")) {
|
|
||||||
return "SUCCESS";
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
learningUnit.children.every(
|
|
||||||
(q) => q.completion_status === "FAIL" || q.completion_status === "SUCCESS"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return "FAIL";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "UNKNOWN";
|
|
||||||
},
|
|
||||||
continueFromLearningContent(
|
continueFromLearningContent(
|
||||||
currentLearningContent: LearningContentInterface,
|
currentLearningContent: LearningContentInterface,
|
||||||
returnRoute?: RouteLocationNormalized
|
returnRoute?: RouteLocationNormalized
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
import { bustItGetCache, itGetCached, itPost } from "@/fetchHelpers";
|
import { bustItGetCache, itGetCached, itPost } from "@/fetchHelpers";
|
||||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||||
import { useUserStore } from "@/stores/user";
|
import { useUserStore } from "@/stores/user";
|
||||||
import type { BaseCourseWagtailPage, CourseCompletion } from "@/types";
|
import type {
|
||||||
|
CourseCompletion,
|
||||||
|
LearningContentWithCompletion,
|
||||||
|
LearningUnitPerformanceCriteria,
|
||||||
|
} from "@/types";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
export const useCompletionStore = defineStore({
|
export const useCompletionStore = defineStore({
|
||||||
|
|
@ -29,7 +33,7 @@ export const useCompletionStore = defineStore({
|
||||||
return userCompletionData || [];
|
return userCompletionData || [];
|
||||||
},
|
},
|
||||||
async markPage(
|
async markPage(
|
||||||
page: BaseCourseWagtailPage,
|
page: LearningContentWithCompletion | LearningUnitPerformanceCriteria,
|
||||||
userId: string | undefined = undefined,
|
userId: string | undefined = undefined,
|
||||||
courseSessionId: string | undefined = undefined
|
courseSessionId: string | undefined = undefined
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import log from "loglevel";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { computed, reactive } from "vue";
|
import { computed, reactive } from "vue";
|
||||||
|
|
||||||
|
// FIXME: remove
|
||||||
export type LearningPathStoreState = {
|
export type LearningPathStoreState = {
|
||||||
learningPaths: Map<string, LearningPath>;
|
learningPaths: Map<string, LearningPath>;
|
||||||
page: "INDEX" | "OVERVIEW";
|
page: "INDEX" | "OVERVIEW";
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import type {
|
||||||
LearningPathObjectType,
|
LearningPathObjectType,
|
||||||
LearningSequenceObjectType,
|
LearningSequenceObjectType,
|
||||||
LearningUnitObjectType,
|
LearningUnitObjectType,
|
||||||
|
PerformanceCriteriaObjectType,
|
||||||
TopicObjectType,
|
TopicObjectType,
|
||||||
} from "@/gql/graphql";
|
} from "@/gql/graphql";
|
||||||
import type { Component } from "vue";
|
import type { Component } from "vue";
|
||||||
|
|
@ -26,6 +27,11 @@ export type LoginMethod = "local" | "sso";
|
||||||
|
|
||||||
export type CourseCompletionStatus = "UNKNOWN" | "FAIL" | "SUCCESS";
|
export type CourseCompletionStatus = "UNKNOWN" | "FAIL" | "SUCCESS";
|
||||||
|
|
||||||
|
export type Completable = {
|
||||||
|
completion_status?: CourseCompletionStatus;
|
||||||
|
completion_status_updated_at?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export interface BaseCourseWagtailPage {
|
export interface BaseCourseWagtailPage {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly title: string;
|
readonly title: string;
|
||||||
|
|
@ -33,8 +39,6 @@ export interface BaseCourseWagtailPage {
|
||||||
readonly content_type: string;
|
readonly content_type: string;
|
||||||
readonly translation_key: string;
|
readonly translation_key: string;
|
||||||
readonly frontend_url: string;
|
readonly frontend_url: string;
|
||||||
completion_status?: CourseCompletionStatus;
|
|
||||||
completion_status_updated_at?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CircleLight {
|
export interface CircleLight {
|
||||||
|
|
@ -97,14 +101,17 @@ export type LearningContent =
|
||||||
| LearningContentRichText
|
| LearningContentRichText
|
||||||
| LearningContentVideo;
|
| LearningContentVideo;
|
||||||
|
|
||||||
|
export type LearningContentWithCompletion = LearningContent & Completable;
|
||||||
|
|
||||||
export type LearningContentContentType = LearningContent["content_type"];
|
export type LearningContentContentType = LearningContent["content_type"];
|
||||||
|
|
||||||
export type LearningUnit = Omit<
|
export type LearningUnit = Omit<
|
||||||
LearningUnitObjectType,
|
LearningUnitObjectType,
|
||||||
"content_type" | "learning_contents"
|
"content_type" | "learning_contents" | "performance_criteria"
|
||||||
> & {
|
> & {
|
||||||
content_type: "learnpath.LearningUnit";
|
content_type: "learnpath.LearningUnit";
|
||||||
learning_contents: LearningContent[];
|
learning_contents: LearningContentWithCompletion[];
|
||||||
|
performance_criteria: LearningUnitPerformanceCriteria[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LearningSequence = Omit<
|
export type LearningSequence = Omit<
|
||||||
|
|
@ -136,12 +143,13 @@ export type LearningPathType = Omit<
|
||||||
topics: TopicType[];
|
topics: TopicType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface LearningUnitPerformanceCriteria extends BaseCourseWagtailPage {
|
export type LearningUnitPerformanceCriteria = Omit<
|
||||||
readonly content_type: "competence.PerformanceCriteria";
|
PerformanceCriteriaObjectType,
|
||||||
readonly competence_id: string;
|
"content_type"
|
||||||
parentLearningSequence?: LearningSequence;
|
> &
|
||||||
parentLearningUnit?: LearningUnit;
|
Completable & {
|
||||||
}
|
readonly content_type: "competence.PerformanceCriteria";
|
||||||
|
};
|
||||||
|
|
||||||
export interface CourseCompletion {
|
export interface CourseCompletion {
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ from vbv_lernwelt.assignment.graphql.types import AssignmentObjectType
|
||||||
from vbv_lernwelt.competence.models import (
|
from vbv_lernwelt.competence.models import (
|
||||||
CompetenceCertificate,
|
CompetenceCertificate,
|
||||||
CompetenceCertificateList,
|
CompetenceCertificateList,
|
||||||
|
PerformanceCriteria,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.graphql.interfaces import CoursePageInterface
|
from vbv_lernwelt.course.graphql.interfaces import CoursePageInterface
|
||||||
|
|
||||||
|
|
@ -34,3 +35,10 @@ class CompetenceCertificateListObjectType(DjangoObjectType):
|
||||||
|
|
||||||
def resolve_competence_certificates(self, info):
|
def resolve_competence_certificates(self, info):
|
||||||
return CompetenceCertificate.objects.child_of(self)
|
return CompetenceCertificate.objects.child_of(self)
|
||||||
|
|
||||||
|
|
||||||
|
class PerformanceCriteriaObjectType(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = PerformanceCriteria
|
||||||
|
interfaces = (CoursePageInterface,)
|
||||||
|
fields = ["competence_id"]
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,9 @@ class PerformanceCriteria(CourseBasePage):
|
||||||
FieldPanel("learning_unit"),
|
FieldPanel("learning_unit"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_frontend_url(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
def save(self, clean=True, user=None, log_action=False, **kwargs):
|
def save(self, clean=True, user=None, log_action=False, **kwargs):
|
||||||
profile_parent = (
|
profile_parent = (
|
||||||
self.get_ancestors().exact_type(ActionCompetenceListPage).last()
|
self.get_ancestors().exact_type(ActionCompetenceListPage).last()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from rest_framework.test import APITestCase
|
||||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
from vbv_lernwelt.course.models import CourseSession
|
||||||
from vbv_lernwelt.feedback.factories import FeedbackResponseFactory
|
from vbv_lernwelt.feedback.factories import FeedbackResponseFactory
|
||||||
from vbv_lernwelt.feedback.models import FeedbackResponse
|
from vbv_lernwelt.feedback.models import FeedbackResponse
|
||||||
from vbv_lernwelt.learnpath.models import Circle
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,9 @@ logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
class LearningContentInterface(CoursePageInterface):
|
class LearningContentInterface(CoursePageInterface):
|
||||||
minutes = graphene.Int()
|
minutes = graphene.Int()
|
||||||
description = graphene.String()
|
description = graphene.String(required=True)
|
||||||
content = graphene.String()
|
content_url = graphene.String(required=True)
|
||||||
|
can_user_self_toggle_course_completion = graphene.Boolean(required=True)
|
||||||
circle = graphene.Field(
|
circle = graphene.Field(
|
||||||
"vbv_lernwelt.learnpath.graphql.types.CircleObjectType", required=True
|
"vbv_lernwelt.learnpath.graphql.types.CircleObjectType", required=True
|
||||||
)
|
)
|
||||||
|
|
@ -186,6 +187,12 @@ class LearningUnitObjectType(DjangoObjectType):
|
||||||
learning_contents = graphene.List(
|
learning_contents = graphene.List(
|
||||||
graphene.NonNull(LearningContentInterface), required=True
|
graphene.NonNull(LearningContentInterface), required=True
|
||||||
)
|
)
|
||||||
|
performance_criteria = graphene.List(
|
||||||
|
graphene.NonNull(
|
||||||
|
"vbv_lernwelt.competence.graphql.types.PerformanceCriteriaObjectType"
|
||||||
|
),
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
evaluate_url = graphene.String(required=True)
|
evaluate_url = graphene.String(required=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -197,6 +204,10 @@ class LearningUnitObjectType(DjangoObjectType):
|
||||||
def resolve_evaluate_url(root: LearningUnit, info, **kwargs):
|
def resolve_evaluate_url(root: LearningUnit, info, **kwargs):
|
||||||
return root.get_evaluate_url()
|
return root.get_evaluate_url()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_performance_criteria(root: LearningUnit, info, **kwargs):
|
||||||
|
return root.performancecriteria_set.all()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def resolve_learning_contents(root: LearningUnit, info, **kwargs):
|
def resolve_learning_contents(root: LearningUnit, info, **kwargs):
|
||||||
siblings = None
|
siblings = None
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue