Query performance criteria also by course
This commit is contained in:
parent
a6cf4ad128
commit
a637775b81
|
|
@ -1,10 +1,11 @@
|
|||
import { graphqlClient } from "@/graphql/client";
|
||||
import { COURSE_SESSION_DETAIL_QUERY, LEARNING_PATH_QUERY } from "@/graphql/queries";
|
||||
import { COURSE_QUERY, COURSE_SESSION_DETAIL_QUERY } from "@/graphql/queries";
|
||||
import { circleFlatChildren, circleFlatLearningContents } from "@/services/circle";
|
||||
import { useCompletionStore } from "@/stores/completion";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import type {
|
||||
ActionCompetence,
|
||||
CourseCompletion,
|
||||
CourseCompletionStatus,
|
||||
CourseSession,
|
||||
|
|
@ -12,6 +13,7 @@ import type {
|
|||
LearningContentWithCompletion,
|
||||
LearningPathType,
|
||||
LearningUnitPerformanceCriteria,
|
||||
PerformanceCriteria,
|
||||
} from "@/types";
|
||||
import { useQuery } from "@urql/vue";
|
||||
import log from "loglevel";
|
||||
|
|
@ -140,14 +142,18 @@ export function flatCircles(learningPath: LearningPathType) {
|
|||
|
||||
export function useLearningPath(courseSlug: string) {
|
||||
const learningPath = ref<LearningPathType | undefined>(undefined);
|
||||
const actionCompetences = ref<ActionCompetence[]>([]);
|
||||
|
||||
// urql.useQuery is not meant to be used programmatically, so we use graphqlClient.query instead
|
||||
const resultPromise = graphqlClient
|
||||
.query(LEARNING_PATH_QUERY, { slug: `${courseSlug}-lp` })
|
||||
.query(COURSE_QUERY, { slug: `${courseSlug}` })
|
||||
.toPromise();
|
||||
|
||||
resultPromise.then((result) => {
|
||||
learningPath.value = result.data?.learning_path as LearningPathType;
|
||||
actionCompetences.value = result.data?.course
|
||||
?.action_competences as ActionCompetence[];
|
||||
learningPath.value = result.data?.course?.learning_path as LearningPathType;
|
||||
|
||||
// attach circle information to learning contents
|
||||
if (learningPath.value) {
|
||||
flatCircles(learningPath.value).forEach((circle) => {
|
||||
|
|
@ -157,6 +163,17 @@ export function useLearningPath(courseSlug: string) {
|
|||
slug: circle.slug,
|
||||
title: circle.title,
|
||||
};
|
||||
|
||||
if (lc.content_type === "competence.PerformanceCriteria") {
|
||||
const pc = findPerformanceCriterion(lc.id);
|
||||
if (pc) {
|
||||
pc.circle = {
|
||||
id: circle.id,
|
||||
slug: circle.slug,
|
||||
title: circle.title,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -175,6 +192,16 @@ export function useLearningPath(courseSlug: string) {
|
|||
});
|
||||
}
|
||||
|
||||
function findPerformanceCriterion(id: string) {
|
||||
return (actionCompetences.value ?? [])
|
||||
.flatMap((ac) => {
|
||||
return ac.performance_criteria;
|
||||
})
|
||||
.find((pc) => {
|
||||
return pc.id === id;
|
||||
}) as PerformanceCriteria | undefined;
|
||||
}
|
||||
|
||||
function findLearningContent(learningContentId: string) {
|
||||
return (circles.value ?? [])
|
||||
.flatMap((c) => {
|
||||
|
|
@ -185,7 +212,21 @@ export function useLearningPath(courseSlug: string) {
|
|||
});
|
||||
}
|
||||
|
||||
return { resultPromise, learningPath, circles, findCircle, findLearningContent };
|
||||
const flatPerformanceCriteria = computed(() => {
|
||||
return (actionCompetences.value ?? []).flatMap((ac) => {
|
||||
return ac.performance_criteria;
|
||||
}) as PerformanceCriteria[];
|
||||
});
|
||||
|
||||
return {
|
||||
resultPromise,
|
||||
learningPath,
|
||||
actionCompetences,
|
||||
circles,
|
||||
findCircle,
|
||||
findLearningContent,
|
||||
flatPerformanceCriteria,
|
||||
};
|
||||
}
|
||||
|
||||
export function useLearningPathWithCompletion(
|
||||
|
|
@ -203,9 +244,10 @@ export function useLearningPathWithCompletion(
|
|||
courseSessionId = useCurrentCourseSession().value.id;
|
||||
}
|
||||
|
||||
const learningPathResult = useLearningPath(courseSlug);
|
||||
const courseResult = useLearningPath(courseSlug);
|
||||
const completionStore = useCompletionStore();
|
||||
const nextLearningContent = ref<LearningContentWithCompletion | null>(null);
|
||||
const loaded = ref(false);
|
||||
|
||||
function updateCompletionData() {
|
||||
if (userId && courseSessionId) {
|
||||
|
|
@ -215,8 +257,8 @@ export function useLearningPathWithCompletion(
|
|||
}
|
||||
|
||||
function _parseCompletionData(completionData: CourseCompletion[]) {
|
||||
if (learningPathResult.circles.value) {
|
||||
learningPathResult.circles.value.forEach((circle) => {
|
||||
if (courseResult.circles.value) {
|
||||
courseResult.circles.value.forEach((circle) => {
|
||||
circleFlatChildren(circle).forEach((lc) => {
|
||||
const pageIndex = completionData.findIndex((e) => {
|
||||
return e.page_id === lc.id;
|
||||
|
|
@ -230,10 +272,25 @@ export function useLearningPathWithCompletion(
|
|||
});
|
||||
}
|
||||
|
||||
if (courseResult.actionCompetences.value) {
|
||||
courseResult.actionCompetences.value.forEach((ac) => {
|
||||
ac.performance_criteria.forEach((pc) => {
|
||||
const pageIndex = completionData.findIndex((e) => {
|
||||
return e.page_id === pc.id;
|
||||
});
|
||||
if (pageIndex >= 0) {
|
||||
pc.completion_status = completionData[pageIndex].completion_status;
|
||||
} else {
|
||||
pc.completion_status = "UNKNOWN";
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME calculate nextLearningContent
|
||||
if (learningPathResult.circles.value?.length) {
|
||||
if (courseResult.circles.value?.length) {
|
||||
nextLearningContent.value = circleFlatLearningContents(
|
||||
learningPathResult.circles.value[0]
|
||||
courseResult.circles.value[0]
|
||||
)[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -254,10 +311,11 @@ export function useLearningPathWithCompletion(
|
|||
}
|
||||
|
||||
async function _start() {
|
||||
Promise.all([learningPathResult.resultPromise, updateCompletionData()]).then(
|
||||
Promise.all([courseResult.resultPromise, updateCompletionData()]).then(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
([_queryResults, completionData]) => {
|
||||
_parseCompletionData(completionData);
|
||||
loaded.value = true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -265,7 +323,8 @@ export function useLearningPathWithCompletion(
|
|||
_start();
|
||||
|
||||
return {
|
||||
...learningPathResult,
|
||||
...courseResult,
|
||||
loaded,
|
||||
updateCompletionData,
|
||||
markCompletion,
|
||||
nextLearningContent,
|
||||
|
|
|
|||
|
|
@ -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 competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_max_points\n evaluation_passed\n }\n learning_content {\n ...CoursePageFields\n circle {\n id\n title\n slug\n }\n }\n }\n }\n }\n }\n": types.CompetenceCertificateQueryDocument,
|
||||
"\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument,
|
||||
"\n query 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 query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n slug\n category_name\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n ...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 }\n": types.CourseQueryDocument,
|
||||
"\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.
|
||||
*/
|
||||
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"];
|
||||
export function graphql(source: "\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n slug\n category_name\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n ...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 }\n"): (typeof documents)["\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n slug\n category_name\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n ...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 }\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
|
|
@ -1,5 +1,8 @@
|
|||
type Query {
|
||||
learning_path(id: ID, slug: String, course_id: ID, course_slug: String): LearningPathObjectType
|
||||
course_session_attendance_course(id: ID!, assignment_user_id: ID): CourseSessionAttendanceCourseObjectType
|
||||
course(id: ID, slug: String): CourseObjectType
|
||||
course_session(id: ID): CourseSessionObjectType
|
||||
learning_content_media_library: LearningContentMediaLibraryObjectType
|
||||
learning_content_assignment: LearningContentAssignmentObjectType
|
||||
learning_content_attendance_course: LearningContentAttendanceCourseObjectType
|
||||
|
|
@ -10,9 +13,6 @@ type Query {
|
|||
learning_content_test: LearningContentEdoniqTestObjectType
|
||||
learning_content_video: LearningContentVideoObjectType
|
||||
learning_content_document_list: LearningContentDocumentListObjectType
|
||||
course_session_attendance_course(id: ID!, assignment_user_id: ID): CourseSessionAttendanceCourseObjectType
|
||||
course(id: ID): CourseObjectType
|
||||
course_session(id: ID): CourseSessionObjectType
|
||||
competence_certificate(id: ID, slug: String): CompetenceCertificateObjectType
|
||||
competence_certificate_list(id: ID, slug: String, course_id: ID, course_slug: String): CompetenceCertificateListObjectType
|
||||
assignment(id: ID, slug: String): AssignmentObjectType
|
||||
|
|
@ -47,6 +47,71 @@ type CourseObjectType {
|
|||
title: String!
|
||||
category_name: String!
|
||||
slug: String!
|
||||
learning_path: LearningPathObjectType!
|
||||
action_competences: [ActionCompetenceObjectType!]!
|
||||
}
|
||||
|
||||
type ActionCompetenceObjectType implements CoursePageInterface {
|
||||
competence_id: String!
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
performance_criteria: [PerformanceCriteriaObjectType!]!
|
||||
}
|
||||
|
||||
type PerformanceCriteriaObjectType implements CoursePageInterface {
|
||||
competence_id: String!
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
learning_unit: LearningUnitObjectType
|
||||
}
|
||||
|
||||
type LearningUnitObjectType implements CoursePageInterface {
|
||||
title_hidden: Boolean!
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
learning_contents: [LearningContentInterface!]!
|
||||
performance_criteria: [PerformanceCriteriaObjectType!]!
|
||||
evaluate_url: String!
|
||||
}
|
||||
|
||||
interface LearningContentInterface {
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
minutes: Int
|
||||
description: String!
|
||||
content_url: String!
|
||||
can_user_self_toggle_course_completion: Boolean!
|
||||
circle: CircleLightObjectType
|
||||
}
|
||||
|
||||
type CircleLightObjectType {
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
}
|
||||
|
||||
type TopicObjectType implements CoursePageInterface {
|
||||
|
|
@ -89,22 +154,18 @@ type LearningSequenceObjectType implements CoursePageInterface {
|
|||
learning_units: [LearningUnitObjectType!]!
|
||||
}
|
||||
|
||||
type LearningUnitObjectType implements CoursePageInterface {
|
||||
title_hidden: Boolean!
|
||||
type CourseSessionAttendanceCourseObjectType {
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
learning_contents: [LearningContentInterface!]!
|
||||
performance_criteria: [PerformanceCriteriaObjectType!]!
|
||||
evaluate_url: String!
|
||||
learning_content: LearningContentAttendanceCourseObjectType!
|
||||
due_date: DueDateObjectType
|
||||
location: String!
|
||||
trainer: String!
|
||||
course_session_id: ID
|
||||
learning_content_id: ID
|
||||
attendance_user_list: [AttendanceUserObjectType]
|
||||
}
|
||||
|
||||
interface LearningContentInterface {
|
||||
type LearningContentAttendanceCourseObjectType implements CoursePageInterface & LearningContentInterface {
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
|
|
@ -120,38 +181,74 @@ interface LearningContentInterface {
|
|||
circle: CircleLightObjectType
|
||||
}
|
||||
|
||||
type CircleLightObjectType {
|
||||
type DueDateObjectType {
|
||||
id: ID!
|
||||
|
||||
"""Startdatum ist Pflicht"""
|
||||
start: DateTime
|
||||
|
||||
"""Enddatum ist optional"""
|
||||
end: DateTime
|
||||
|
||||
"""Nur aktivieren, wenn man die Felder manuell überschreiben will"""
|
||||
manual_override_fields: Boolean!
|
||||
|
||||
"""Title wird standarmässig vom LearningContent übernommen"""
|
||||
title: String!
|
||||
slug: String!
|
||||
|
||||
"""Translation Key aus dem Frontend"""
|
||||
assignment_type_translation_key: String!
|
||||
|
||||
"""Translation Key aus dem Frontend"""
|
||||
date_type_translation_key: String!
|
||||
|
||||
"""
|
||||
Überschreibt den Untertitel bei `assignment_type_translation_key` und `date_type_translation_key`
|
||||
"""
|
||||
subtitle: String!
|
||||
|
||||
"""
|
||||
URL wird vom LearningContent übernommen (sichtbar für Member/Teilnehmer)
|
||||
"""
|
||||
url: String!
|
||||
course_session: CourseSessionObjectType!
|
||||
}
|
||||
|
||||
type PerformanceCriteriaObjectType implements CoursePageInterface {
|
||||
competence_id: String!
|
||||
"""
|
||||
The `DateTime` scalar type represents a DateTime
|
||||
value as specified by
|
||||
[iso8601](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
"""
|
||||
scalar DateTime
|
||||
|
||||
type CourseSessionObjectType {
|
||||
id: ID!
|
||||
created_at: DateTime!
|
||||
updated_at: DateTime!
|
||||
course: CourseObjectType!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
start_date: Date
|
||||
end_date: Date
|
||||
attendance_courses: [CourseSessionAttendanceCourseObjectType!]!
|
||||
assignments: [CourseSessionAssignmentObjectType!]!
|
||||
edoniq_tests: [CourseSessionEdoniqTestObjectType!]!
|
||||
users: [CourseSessionUserObjectsType!]!
|
||||
}
|
||||
|
||||
type LearningContentMediaLibraryObjectType implements CoursePageInterface & LearningContentInterface {
|
||||
"""
|
||||
The `Date` scalar type represents a Date
|
||||
value as specified by
|
||||
[iso8601](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
"""
|
||||
scalar Date
|
||||
|
||||
type CourseSessionAssignmentObjectType {
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
minutes: Int
|
||||
description: String!
|
||||
content_url: String!
|
||||
can_user_self_toggle_course_completion: Boolean!
|
||||
circle: CircleLightObjectType
|
||||
learning_content: LearningContentAssignmentObjectType!
|
||||
submission_deadline: DueDateObjectType
|
||||
evaluation_deadline: DueDateObjectType
|
||||
course_session_id: ID!
|
||||
learning_content_id: ID!
|
||||
}
|
||||
|
||||
type LearningContentAssignmentObjectType implements CoursePageInterface & LearningContentInterface {
|
||||
|
|
@ -268,13 +365,6 @@ in fields, resolvers and input.
|
|||
"""
|
||||
scalar UUID
|
||||
|
||||
"""
|
||||
The `DateTime` scalar type represents a DateTime
|
||||
value as specified by
|
||||
[iso8601](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
"""
|
||||
scalar DateTime
|
||||
|
||||
type UserObjectType {
|
||||
"""
|
||||
Erforderlich. 150 Zeichen oder weniger. Nur Buchstaben, Ziffern und @/./+/-/_.
|
||||
|
|
@ -300,108 +390,52 @@ enum CoreUserLanguageChoices {
|
|||
IT
|
||||
}
|
||||
|
||||
type CourseSessionObjectType {
|
||||
id: ID!
|
||||
created_at: DateTime!
|
||||
updated_at: DateTime!
|
||||
course: CourseObjectType!
|
||||
title: String!
|
||||
start_date: Date
|
||||
end_date: Date
|
||||
attendance_courses: [CourseSessionAttendanceCourseObjectType!]!
|
||||
assignments: [CourseSessionAssignmentObjectType!]!
|
||||
edoniq_tests: [CourseSessionEdoniqTestObjectType!]!
|
||||
users: [CourseSessionUserObjectsType!]!
|
||||
"""An enumeration."""
|
||||
enum AssignmentAssignmentCompletionCompletionStatusChoices {
|
||||
"""IN_PROGRESS"""
|
||||
IN_PROGRESS
|
||||
|
||||
"""SUBMITTED"""
|
||||
SUBMITTED
|
||||
|
||||
"""EVALUATION_IN_PROGRESS"""
|
||||
EVALUATION_IN_PROGRESS
|
||||
|
||||
"""EVALUATION_SUBMITTED"""
|
||||
EVALUATION_SUBMITTED
|
||||
}
|
||||
|
||||
"""
|
||||
The `Date` scalar type represents a Date
|
||||
value as specified by
|
||||
[iso8601](https://en.wikipedia.org/wiki/ISO_8601).
|
||||
The `GenericScalar` scalar type represents a generic
|
||||
GraphQL scalar value that could be:
|
||||
String, Boolean, Int, Float, List or Object.
|
||||
"""
|
||||
scalar Date
|
||||
|
||||
type CourseSessionAttendanceCourseObjectType {
|
||||
id: ID!
|
||||
learning_content: LearningContentAttendanceCourseObjectType!
|
||||
due_date: DueDateObjectType
|
||||
location: String!
|
||||
trainer: String!
|
||||
course_session_id: ID
|
||||
learning_content_id: ID
|
||||
attendance_user_list: [AttendanceUserObjectType]
|
||||
}
|
||||
|
||||
type LearningContentAttendanceCourseObjectType implements CoursePageInterface & LearningContentInterface {
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
minutes: Int
|
||||
description: String!
|
||||
content_url: String!
|
||||
can_user_self_toggle_course_completion: Boolean!
|
||||
circle: CircleLightObjectType
|
||||
}
|
||||
|
||||
type DueDateObjectType {
|
||||
id: ID!
|
||||
|
||||
"""Startdatum ist Pflicht"""
|
||||
start: DateTime
|
||||
|
||||
"""Enddatum ist optional"""
|
||||
end: DateTime
|
||||
|
||||
"""Nur aktivieren, wenn man die Felder manuell überschreiben will"""
|
||||
manual_override_fields: Boolean!
|
||||
|
||||
"""Title wird standarmässig vom LearningContent übernommen"""
|
||||
title: String!
|
||||
|
||||
"""Translation Key aus dem Frontend"""
|
||||
assignment_type_translation_key: String!
|
||||
|
||||
"""Translation Key aus dem Frontend"""
|
||||
date_type_translation_key: String!
|
||||
scalar GenericScalar
|
||||
|
||||
"""
|
||||
Überschreibt den Untertitel bei `assignment_type_translation_key` und `date_type_translation_key`
|
||||
"""
|
||||
subtitle: String!
|
||||
Allows use of a JSON String for input / output from the GraphQL schema.
|
||||
|
||||
Use of this type is *not recommended* as you lose the benefits of having a defined, static
|
||||
schema (one of the key benefits of GraphQL).
|
||||
"""
|
||||
URL wird vom LearningContent übernommen (sichtbar für Member/Teilnehmer)
|
||||
"""
|
||||
url: String!
|
||||
course_session: CourseSessionObjectType!
|
||||
}
|
||||
|
||||
type AttendanceUserObjectType {
|
||||
user_id: UUID!
|
||||
status: AttendanceUserStatus!
|
||||
first_name: String
|
||||
last_name: String
|
||||
email: String
|
||||
}
|
||||
scalar JSONString
|
||||
|
||||
"""An enumeration."""
|
||||
enum AttendanceUserStatus {
|
||||
PRESENT
|
||||
ABSENT
|
||||
}
|
||||
enum LearnpathLearningContentAssignmentAssignmentTypeChoices {
|
||||
"""CASEWORK"""
|
||||
CASEWORK
|
||||
|
||||
type CourseSessionAssignmentObjectType {
|
||||
id: ID!
|
||||
learning_content: LearningContentAssignmentObjectType!
|
||||
submission_deadline: DueDateObjectType
|
||||
evaluation_deadline: DueDateObjectType
|
||||
course_session_id: ID!
|
||||
learning_content_id: ID!
|
||||
"""PREP_ASSIGNMENT"""
|
||||
PREP_ASSIGNMENT
|
||||
|
||||
"""REFLECTION"""
|
||||
REFLECTION
|
||||
|
||||
"""CONDITION_ACCEPTANCE"""
|
||||
CONDITION_ACCEPTANCE
|
||||
|
||||
"""EDONIQ_TEST"""
|
||||
EDONIQ_TEST
|
||||
}
|
||||
|
||||
type CourseSessionEdoniqTestObjectType {
|
||||
|
|
@ -460,52 +494,34 @@ type CourseSessionUserExpertCircleType {
|
|||
slug: String!
|
||||
}
|
||||
|
||||
"""An enumeration."""
|
||||
enum AssignmentAssignmentCompletionCompletionStatusChoices {
|
||||
"""IN_PROGRESS"""
|
||||
IN_PROGRESS
|
||||
|
||||
"""SUBMITTED"""
|
||||
SUBMITTED
|
||||
|
||||
"""EVALUATION_IN_PROGRESS"""
|
||||
EVALUATION_IN_PROGRESS
|
||||
|
||||
"""EVALUATION_SUBMITTED"""
|
||||
EVALUATION_SUBMITTED
|
||||
type AttendanceUserObjectType {
|
||||
user_id: UUID!
|
||||
status: AttendanceUserStatus!
|
||||
first_name: String
|
||||
last_name: String
|
||||
email: String
|
||||
}
|
||||
|
||||
"""
|
||||
The `GenericScalar` scalar type represents a generic
|
||||
GraphQL scalar value that could be:
|
||||
String, Boolean, Int, Float, List or Object.
|
||||
"""
|
||||
scalar GenericScalar
|
||||
|
||||
"""
|
||||
Allows use of a JSON String for input / output from the GraphQL schema.
|
||||
|
||||
Use of this type is *not recommended* as you lose the benefits of having a defined, static
|
||||
schema (one of the key benefits of GraphQL).
|
||||
"""
|
||||
scalar JSONString
|
||||
|
||||
"""An enumeration."""
|
||||
enum LearnpathLearningContentAssignmentAssignmentTypeChoices {
|
||||
"""CASEWORK"""
|
||||
CASEWORK
|
||||
enum AttendanceUserStatus {
|
||||
PRESENT
|
||||
ABSENT
|
||||
}
|
||||
|
||||
"""PREP_ASSIGNMENT"""
|
||||
PREP_ASSIGNMENT
|
||||
|
||||
"""REFLECTION"""
|
||||
REFLECTION
|
||||
|
||||
"""CONDITION_ACCEPTANCE"""
|
||||
CONDITION_ACCEPTANCE
|
||||
|
||||
"""EDONIQ_TEST"""
|
||||
EDONIQ_TEST
|
||||
type LearningContentMediaLibraryObjectType implements CoursePageInterface & LearningContentInterface {
|
||||
id: ID!
|
||||
title: String!
|
||||
slug: String!
|
||||
content_type: String!
|
||||
live: Boolean!
|
||||
translation_key: String!
|
||||
frontend_url: String!
|
||||
course: CourseObjectType
|
||||
minutes: Int
|
||||
description: String!
|
||||
content_url: String!
|
||||
can_user_self_toggle_course_completion: Boolean!
|
||||
circle: CircleLightObjectType
|
||||
}
|
||||
|
||||
type LearningContentFeedbackObjectType implements CoursePageInterface & LearningContentInterface {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export const ActionCompetenceObjectType = "ActionCompetenceObjectType";
|
||||
export const AssignmentAssignmentAssignmentTypeChoices = "AssignmentAssignmentAssignmentTypeChoices";
|
||||
export const AssignmentAssignmentCompletionCompletionStatusChoices = "AssignmentAssignmentCompletionCompletionStatusChoices";
|
||||
export const AssignmentCompletionMutation = "AssignmentCompletionMutation";
|
||||
|
|
|
|||
|
|
@ -193,9 +193,26 @@ export const COURSE_SESSION_DETAIL_QUERY = graphql(`
|
|||
}
|
||||
`);
|
||||
|
||||
export const LEARNING_PATH_QUERY = graphql(`
|
||||
query learningPathQuery($slug: String!) {
|
||||
learning_path(slug: $slug) {
|
||||
export const COURSE_QUERY = graphql(`
|
||||
query courseQuery($slug: String!) {
|
||||
course(slug: $slug) {
|
||||
id
|
||||
slug
|
||||
category_name
|
||||
action_competences {
|
||||
competence_id
|
||||
...CoursePageFields
|
||||
performance_criteria {
|
||||
competence_id
|
||||
learning_unit {
|
||||
id
|
||||
slug
|
||||
evaluate_url
|
||||
}
|
||||
...CoursePageFields
|
||||
}
|
||||
}
|
||||
learning_path {
|
||||
...CoursePageFields
|
||||
topics {
|
||||
is_visible
|
||||
|
|
@ -238,4 +255,5 @@ export const LEARNING_PATH_QUERY = graphql(`
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { useCourseSessionDetailQuery } from "@/composables";
|
||||
import { useCockpitStore } from "@/stores/cockpit";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import * as log from "loglevel";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
|
|
@ -12,7 +11,6 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const cockpitStore = useCockpitStore();
|
||||
const competenceStore = useCompetenceStore();
|
||||
const courseSessionDetailResult = useCourseSessionDetailQuery();
|
||||
|
||||
onMounted(async () => {
|
||||
|
|
@ -20,17 +18,13 @@ onMounted(async () => {
|
|||
|
||||
try {
|
||||
await courseSessionDetailResult.waitForData();
|
||||
const members = courseSessionDetailResult.filterMembers();
|
||||
members.forEach((csu) => {
|
||||
competenceStore.loadCompetenceProfilePage(
|
||||
props.courseSlug + "-competencenavi-competences",
|
||||
csu.user_id
|
||||
);
|
||||
});
|
||||
await cockpitStore.loadCircles(
|
||||
props.courseSlug,
|
||||
courseSessionDetailResult.findCurrentUser()
|
||||
);
|
||||
// const members = courseSessionDetailResult.filterMembers().map((m) => {
|
||||
// return {}
|
||||
// });
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import ItPersonRow from "@/components/ui/ItPersonRow.vue";
|
|||
import { useCourseSessionDetailQuery, useCurrentCourseSession } from "@/composables";
|
||||
import SubmissionsOverview from "@/pages/cockpit/cockpitPage/SubmissionsOverview.vue";
|
||||
import { useCockpitStore } from "@/stores/cockpit";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import log from "loglevel";
|
||||
import CockpitDates from "@/pages/cockpit/cockpitPage/CockpitDates.vue";
|
||||
import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
|
||||
import UserStatusCount from "@/pages/cockpit/cockpitPage/UserStatusCount.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
|
|
@ -17,18 +17,8 @@ const props = defineProps<{
|
|||
log.debug("CockpitIndexPage created", props.courseSlug);
|
||||
|
||||
const cockpitStore = useCockpitStore();
|
||||
const competenceStore = useCompetenceStore();
|
||||
const courseSession = useCurrentCourseSession();
|
||||
const courseSessionDetailResult = useCourseSessionDetailQuery();
|
||||
|
||||
function userCountStatusForCircle(userId: string) {
|
||||
if (!cockpitStore.currentCircle) return { FAIL: 0, SUCCESS: 0, UNKNOWN: 0 };
|
||||
const criteria = competenceStore.flatPerformanceCriteria(
|
||||
userId,
|
||||
cockpitStore.currentCircle.id
|
||||
);
|
||||
return competenceStore.calcStatusCount(criteria);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -150,32 +140,10 @@ function userCountStatusForCircle(userId: string) {
|
|||
<p class="lg:min-w-[150px]">
|
||||
{{ cockpitStore.currentCircle.title }}
|
||||
</p>
|
||||
<div class="ml-4 flex flex-row items-center">
|
||||
<div class="mr-6 flex flex-row items-center">
|
||||
<it-icon-smiley-thinking
|
||||
class="mr-2 inline-block h-8 w-8"
|
||||
></it-icon-smiley-thinking>
|
||||
<p class="text-bold inline-block w-6">
|
||||
{{ userCountStatusForCircle(csu.user_id).FAIL }}
|
||||
</p>
|
||||
</div>
|
||||
<li class="mr-6 flex flex-row items-center">
|
||||
<it-icon-smiley-happy
|
||||
class="mr-2 inline-block h-8 w-8"
|
||||
></it-icon-smiley-happy>
|
||||
<p class="text-bold inline-block w-6">
|
||||
{{ userCountStatusForCircle(csu.user_id).SUCCESS }}
|
||||
</p>
|
||||
</li>
|
||||
<li class="flex flex-row items-center">
|
||||
<it-icon-smiley-neutral
|
||||
class="mr-2 inline-block h-8 w-8"
|
||||
></it-icon-smiley-neutral>
|
||||
<p class="text-bold inline-block w-6">
|
||||
{{ userCountStatusForCircle(csu.user_id).UNKNOWN }}
|
||||
</p>
|
||||
</li>
|
||||
</div>
|
||||
<UserStatusCount
|
||||
:course-slug="props.courseSlug"
|
||||
:user-id="csu.user_id"
|
||||
></UserStatusCount>
|
||||
</div>
|
||||
</template>
|
||||
<template #link>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
<script setup lang="ts">
|
||||
import { useLearningPathWithCompletion } from "@/composables";
|
||||
import { computed } from "vue";
|
||||
import { calcPerformanceCriteriaStatusCount } from "@/services/competence";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
userId: string;
|
||||
circleId?: string;
|
||||
}>();
|
||||
|
||||
const courseData = useLearningPathWithCompletion(props.courseSlug, props.userId);
|
||||
|
||||
const circleStatusCount = computed(() => {
|
||||
if (props.circleId) {
|
||||
return calcPerformanceCriteriaStatusCount(
|
||||
(courseData.flatPerformanceCriteria.value ?? []).filter(
|
||||
(pc) => pc.circle.id === props.circleId
|
||||
)
|
||||
);
|
||||
}
|
||||
return calcPerformanceCriteriaStatusCount(courseData.flatPerformanceCriteria.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="courseData.loaded" class="ml-4 flex flex-row items-center">
|
||||
<div class="mr-6 flex flex-row items-center">
|
||||
<it-icon-smiley-thinking
|
||||
class="mr-2 inline-block h-8 w-8"
|
||||
></it-icon-smiley-thinking>
|
||||
<p class="text-bold inline-block w-6">
|
||||
{{ circleStatusCount.FAIL }}
|
||||
</p>
|
||||
</div>
|
||||
<li class="mr-6 flex flex-row items-center">
|
||||
<it-icon-smiley-happy class="mr-2 inline-block h-8 w-8"></it-icon-smiley-happy>
|
||||
<p class="text-bold inline-block w-6">
|
||||
{{ circleStatusCount.SUCCESS }}
|
||||
</p>
|
||||
</li>
|
||||
<li class="flex flex-row items-center">
|
||||
<it-icon-smiley-neutral
|
||||
class="mr-2 inline-block h-8 w-8"
|
||||
></it-icon-smiley-neutral>
|
||||
<p class="text-bold inline-block w-6">
|
||||
{{ circleStatusCount.UNKNOWN }}
|
||||
</p>
|
||||
</li>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,15 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import ItToggleArrow from "@/components/ui/ItToggleArrow.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import type { CompetencePage } from "@/types";
|
||||
import log from "loglevel";
|
||||
import { ref, watch } from "vue";
|
||||
import SinglePerformanceCriteriaRow from "@/pages/competence/SinglePerformanceCriteriaRow.vue";
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
import type { ActionCompetence } from "@/types";
|
||||
|
||||
interface Props {
|
||||
competence: CompetencePage;
|
||||
competence: ActionCompetence;
|
||||
courseSlug: string;
|
||||
showAssessAgain?: boolean;
|
||||
isInline?: boolean;
|
||||
|
|
@ -60,12 +57,12 @@ const togglePerformanceCriteria = () => {
|
|||
</li>
|
||||
|
||||
<li
|
||||
v-for="performanceCriteria in competenceStore.criteriaByCompetence(competence)"
|
||||
v-for="performanceCriteria in competence.performance_criteria"
|
||||
:key="performanceCriteria.id"
|
||||
class="my-4 border-b pb-4 last:border-0"
|
||||
>
|
||||
<SinglePerformanceCriteriaRow
|
||||
:criteria="performanceCriteria"
|
||||
:criterion="performanceCriteria"
|
||||
:show-state="false"
|
||||
:course-slug="props.courseSlug"
|
||||
:show-assess-again="props.showAssessAgain"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import CompetenceDetail from "@/pages/competence/ActionCompetenceDetail.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import * as log from "loglevel";
|
||||
import { ref } from "vue";
|
||||
import { useLearningPathWithCompletion } from "@/composables";
|
||||
|
||||
log.debug("CompetenceListPage created");
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ const props = defineProps<{
|
|||
courseSlug: string;
|
||||
}>();
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
const courseData = useLearningPathWithCompletion(props.courseSlug);
|
||||
|
||||
const isOpenAll = ref(false);
|
||||
|
||||
|
|
@ -40,9 +40,9 @@ function toggleOpen() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<ul v-if="competenceStore.competenceProfilePage()">
|
||||
<ul v-if="courseData.actionCompetences.value?.length">
|
||||
<li
|
||||
v-for="competence in competenceStore.competences()"
|
||||
v-for="competence in courseData.actionCompetences.value"
|
||||
:key="competence.id"
|
||||
class="mb-8 bg-white px-8 pt-6"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
|
|||
import { useQuery } from "@urql/vue";
|
||||
import { computed } from "vue";
|
||||
import type { CompetenceCertificate } from "@/types";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import { useCurrentCourseSession, useLearningPathWithCompletion } from "@/composables";
|
||||
import {
|
||||
assignmentsMaxEvaluationPoints,
|
||||
assignmentsUserPoints,
|
||||
competenceCertificateProgressStatusCount,
|
||||
} from "@/pages/competence/utils";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import { useCompetenceStore } from "@/stores/competence";
|
||||
import { calcPerformanceCriteriaStatusCount } from "@/services/competence";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
|
|
@ -20,8 +20,7 @@ const props = defineProps<{
|
|||
log.debug("CompetenceIndexPage setup", props);
|
||||
|
||||
const courseSession = useCurrentCourseSession();
|
||||
|
||||
const competenceStore = useCompetenceStore();
|
||||
const courseData = useLearningPathWithCompletion(props.courseSlug);
|
||||
|
||||
const certificatesQuery = useQuery({
|
||||
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
|
||||
|
|
@ -51,7 +50,7 @@ const userPointsEvaluatedAssignments = computed(() => {
|
|||
});
|
||||
|
||||
const performanceCriteriaStatusCount = computed(() => {
|
||||
return competenceStore.calcStatusCount(competenceStore.flatPerformanceCriteria());
|
||||
return calcPerformanceCriteriaStatusCount(courseData.flatPerformanceCriteria.value);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import type { PerformanceCriteria } from "@/types";
|
||||
|
||||
interface Props {
|
||||
criteria: PerformanceCriteria;
|
||||
criterion: PerformanceCriteria;
|
||||
courseSlug: string;
|
||||
showState?: boolean;
|
||||
showAssessAgain?: boolean;
|
||||
|
|
@ -19,24 +19,24 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
<div class="flex flex-row items-center">
|
||||
<div v-if="showState" class="mr-4 h-8 w-8">
|
||||
<it-icon-smiley-happy
|
||||
v-if="criteria.completion_status === 'SUCCESS'"
|
||||
v-if="criterion.completion_status === 'SUCCESS'"
|
||||
></it-icon-smiley-happy>
|
||||
<it-icon-smiley-thinking
|
||||
v-else-if="criteria.completion_status === 'FAIL'"
|
||||
v-else-if="criterion.completion_status === 'FAIL'"
|
||||
></it-icon-smiley-thinking>
|
||||
<it-icon-smiley-neutral v-else></it-icon-smiley-neutral>
|
||||
</div>
|
||||
<div class="mb-4 pr-4 lg:mb-0 lg:mr-8">
|
||||
{{ criteria.title }}
|
||||
{{ criterion.title }}
|
||||
</div>
|
||||
</div>
|
||||
<span class="lg:whitespace-nowrap">
|
||||
<router-link
|
||||
v-if="props.showAssessAgain"
|
||||
v-if="props.showAssessAgain && criterion.learning_unit?.evaluate_url"
|
||||
class="link"
|
||||
:to="criteria.learning_unit.evaluate_url"
|
||||
:to="criterion.learning_unit.evaluate_url"
|
||||
>
|
||||
{{ $t("competences.assessAgain", { x: criteria.circle.title }) }}
|
||||
{{ $t("competences.assessAgain", { x: criterion.circle.title }) }}
|
||||
</router-link>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import type { PerformanceCriteria } from "@/types";
|
||||
import groupBy from "lodash/groupBy";
|
||||
|
||||
export function calcPerformanceCriteriaStatusCount(criteria: PerformanceCriteria[]) {
|
||||
if (criteria) {
|
||||
const grouped = groupBy(criteria, "completion_status");
|
||||
return {
|
||||
UNKNOWN: grouped?.UNKNOWN?.length || 0,
|
||||
SUCCESS: grouped?.SUCCESS?.length || 0,
|
||||
FAIL: grouped?.FAIL?.length || 0,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
UNKNOWN: 0,
|
||||
SUCCESS: 0,
|
||||
FAIL: 0,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
import type {
|
||||
ActionCompetenceObjectType,
|
||||
AssignmentAssignmentAssignmentTypeChoices,
|
||||
AssignmentCompletionObjectType,
|
||||
AssignmentCompletionStatus as AssignmentCompletionStatusGenerated,
|
||||
AssignmentObjectType,
|
||||
CircleObjectType,
|
||||
CourseCourseSessionUserRoleChoices,
|
||||
CourseSessionObjectType,
|
||||
|
|
@ -327,57 +330,31 @@ export interface AssignmentEvaluationTask {
|
|||
};
|
||||
}
|
||||
|
||||
export type AssignmentType =
|
||||
| "CASEWORK"
|
||||
| "PREP_ASSIGNMENT"
|
||||
| "REFLECTION"
|
||||
| "CONDITION_ACCEPTANCE"
|
||||
| "EDONIQ_TEST";
|
||||
export type AssignmentType = AssignmentAssignmentAssignmentTypeChoices;
|
||||
|
||||
export interface Assignment extends BaseCourseWagtailPage {
|
||||
export type Assignment = Omit<
|
||||
AssignmentObjectType,
|
||||
"content_type" | "performance_objectives" | "tasks" | "evaluation_tasks"
|
||||
> & {
|
||||
readonly content_type: "assignment.Assignment";
|
||||
readonly assignment_type: AssignmentType;
|
||||
readonly needs_expert_evaluation: boolean;
|
||||
readonly intro_text: string;
|
||||
readonly effort_required: string;
|
||||
readonly performance_objectives: AssignmentPerformanceObjective[];
|
||||
readonly evaluation_description: string;
|
||||
readonly evaluation_document_url: string;
|
||||
readonly tasks: AssignmentTask[];
|
||||
readonly evaluation_tasks: AssignmentEvaluationTask[];
|
||||
readonly max_points: number;
|
||||
readonly competence_certificate?: {
|
||||
id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
content_type: string;
|
||||
translation_key: string;
|
||||
frontend_url: string;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export interface PerformanceCriteria extends BaseCourseWagtailPage {
|
||||
readonly content_type: "competence.PerformanceCriteria";
|
||||
readonly competence_id: string;
|
||||
readonly circle: CircleLight;
|
||||
readonly course_category: CourseCategory;
|
||||
readonly learning_unit: BaseCourseWagtailPage & {
|
||||
evaluate_url: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CompetencePage extends BaseCourseWagtailPage {
|
||||
readonly content_type: "competence.CompetencePage";
|
||||
readonly competence_id: string;
|
||||
readonly children: PerformanceCriteria[];
|
||||
}
|
||||
export type PerformanceCriteria = Omit<PerformanceCriteriaObjectType, "content_type"> &
|
||||
Completable & {
|
||||
readonly content_type: "competence.PerformanceCriteria";
|
||||
circle: CircleLight;
|
||||
};
|
||||
|
||||
export interface CompetenceProfilePage extends BaseCourseWagtailPage {
|
||||
readonly content_type: "competence.CompetenceProfilePage";
|
||||
readonly course: Course;
|
||||
readonly circles: CircleLight[];
|
||||
readonly children: CompetencePage[];
|
||||
}
|
||||
export type ActionCompetence = Omit<
|
||||
ActionCompetenceObjectType,
|
||||
"content_type" | "performance_criteria"
|
||||
> & {
|
||||
readonly content_type: "competence.ActionCompetence";
|
||||
performance_criteria: PerformanceCriteria[];
|
||||
};
|
||||
|
||||
export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
|
||||
assignment_type: "CASEWORK" | "EDONIQ_TEST";
|
||||
|
|
@ -437,47 +414,6 @@ export interface CircleDocument {
|
|||
};
|
||||
}
|
||||
|
||||
// export interface CourseSessionAttendanceCourse {
|
||||
// id: string;
|
||||
// due_date: SimpleDueDate;
|
||||
// location: string;
|
||||
// trainer: string;
|
||||
// circle_title: string;
|
||||
// learning_content: {
|
||||
// id: string;
|
||||
// title: string;
|
||||
// circle: CircleLight;
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// export interface CourseSessionAssignment {
|
||||
// id: string;
|
||||
// submission_deadline: SimpleDueDate;
|
||||
// evaluation_deadline: SimpleDueDate;
|
||||
// learning_content: {
|
||||
// id: string;
|
||||
// content_assignment: {
|
||||
// id: string;
|
||||
// title: string;
|
||||
// assignment_type: AssignmentType;
|
||||
// };
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// export interface CourseSessionEdoniqTest {
|
||||
// id: number;
|
||||
// course_session_id: string;
|
||||
// deadline: SimpleDueDate;
|
||||
// learning_content: {
|
||||
// id: string;
|
||||
// content_assignment: {
|
||||
// id: string;
|
||||
// title: string;
|
||||
// assignment_type: AssignmentType;
|
||||
// };
|
||||
// };
|
||||
// }
|
||||
|
||||
export interface CourseSession {
|
||||
id: string;
|
||||
created_at: string;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ from vbv_lernwelt.competence.models import (
|
|||
CompetenceCertificate,
|
||||
CompetenceCertificateList,
|
||||
PerformanceCriteria,
|
||||
ActionCompetence,
|
||||
)
|
||||
from vbv_lernwelt.course.graphql.interfaces import CoursePageInterface
|
||||
from vbv_lernwelt.learnpath.graphql.types import LearningUnitObjectType
|
||||
|
||||
|
||||
class CompetenceCertificateObjectType(DjangoObjectType):
|
||||
|
|
@ -38,7 +40,26 @@ class CompetenceCertificateListObjectType(DjangoObjectType):
|
|||
|
||||
|
||||
class PerformanceCriteriaObjectType(DjangoObjectType):
|
||||
learning_unit = graphene.Field(LearningUnitObjectType)
|
||||
|
||||
class Meta:
|
||||
model = PerformanceCriteria
|
||||
interfaces = (CoursePageInterface,)
|
||||
fields = ["competence_id"]
|
||||
|
||||
def resolve_learning_unit(self: PerformanceCriteria, info):
|
||||
return self.learning_unit
|
||||
|
||||
|
||||
class ActionCompetenceObjectType(DjangoObjectType):
|
||||
performance_criteria = graphene.List(
|
||||
graphene.NonNull(PerformanceCriteriaObjectType), required=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ActionCompetence
|
||||
interfaces = (CoursePageInterface,)
|
||||
fields = ["competence_id", "children"]
|
||||
|
||||
def resolve_performance_criteria(self, info):
|
||||
return self.get_children().specific()
|
||||
|
|
|
|||
|
|
@ -105,6 +105,9 @@ class ActionCompetence(CourseBasePage):
|
|||
use_json_field=True,
|
||||
)
|
||||
|
||||
def get_frontend_url(self):
|
||||
return f""
|
||||
|
||||
content_panels = [
|
||||
FieldPanel("title"),
|
||||
FieldPanel("competence_id"),
|
||||
|
|
|
|||
|
|
@ -1,22 +1,92 @@
|
|||
import graphene
|
||||
from django.db.models import Q
|
||||
from graphql import GraphQLError
|
||||
|
||||
from vbv_lernwelt.course.graphql.types import CourseObjectType, CourseSessionObjectType
|
||||
from vbv_lernwelt.course.models import Course, CourseSession
|
||||
from vbv_lernwelt.course.permissions import has_course_access
|
||||
from vbv_lernwelt.learnpath.graphql.types import (
|
||||
LearningContentMediaLibraryObjectType,
|
||||
LearningContentAssignmentObjectType,
|
||||
LearningContentAttendanceCourseObjectType,
|
||||
LearningContentFeedbackObjectType,
|
||||
LearningContentLearningModuleObjectType,
|
||||
LearningContentPlaceholderObjectType,
|
||||
LearningContentRichTextObjectType,
|
||||
LearningContentEdoniqTestObjectType,
|
||||
LearningContentVideoObjectType,
|
||||
LearningContentDocumentListObjectType,
|
||||
)
|
||||
|
||||
|
||||
class CourseQuery(graphene.ObjectType):
|
||||
course = graphene.Field(CourseObjectType, id=graphene.ID())
|
||||
course = graphene.Field(
|
||||
CourseObjectType,
|
||||
id=graphene.ID(),
|
||||
slug=graphene.String(),
|
||||
)
|
||||
course_session = graphene.Field(CourseSessionObjectType, id=graphene.ID())
|
||||
|
||||
def resolve_course(root, info, id):
|
||||
course = Course.objects.get(pk=id)
|
||||
def resolve_course(self, info, id=None, slug=None):
|
||||
if id is None and slug is None:
|
||||
raise GraphQLError("Either 'id', 'slug' must be provided.")
|
||||
course = Course.objects.get(Q(id=id) | Q(slug=slug))
|
||||
if has_course_access(info.context.user, course):
|
||||
return course
|
||||
raise PermissionError("You do not have access to this course")
|
||||
|
||||
def resolve_course_session(root, info, id):
|
||||
def resolve_course_session(self, info, id):
|
||||
course_session = CourseSession.objects.get(pk=id)
|
||||
if has_course_access(info.context.user, course_session.course):
|
||||
return course_session
|
||||
raise PermissionError("You do not have access to this course session")
|
||||
|
||||
# dummy import, so that graphene recognizes the types
|
||||
learning_content_media_library = graphene.Field(
|
||||
LearningContentMediaLibraryObjectType
|
||||
)
|
||||
learning_content_assignment = graphene.Field(LearningContentAssignmentObjectType)
|
||||
learning_content_attendance_course = graphene.Field(
|
||||
LearningContentAttendanceCourseObjectType
|
||||
)
|
||||
learning_content_feedback = graphene.Field(LearningContentFeedbackObjectType)
|
||||
learning_content_learning_module = graphene.Field(
|
||||
LearningContentLearningModuleObjectType
|
||||
)
|
||||
learning_content_placeholder = graphene.Field(LearningContentPlaceholderObjectType)
|
||||
learning_content_rich_text = graphene.Field(LearningContentRichTextObjectType)
|
||||
learning_content_test = graphene.Field(LearningContentEdoniqTestObjectType)
|
||||
learning_content_video = graphene.Field(LearningContentVideoObjectType)
|
||||
learning_content_document_list = graphene.Field(
|
||||
LearningContentDocumentListObjectType
|
||||
)
|
||||
|
||||
def resolve_learning_content_media_library(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_assignment(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_attendance_course(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_feedback(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_learning_module(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_placeholder(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_rich_text(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_test(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_video(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_document_list(self, info):
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from graphene_django import DjangoObjectType
|
|||
from graphql import GraphQLError
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from vbv_lernwelt.competence.graphql.types import ActionCompetenceObjectType
|
||||
from vbv_lernwelt.course.models import (
|
||||
CircleDocument,
|
||||
Course,
|
||||
|
|
@ -26,6 +27,7 @@ from vbv_lernwelt.course_session.models import (
|
|||
CourseSessionAttendanceCourse,
|
||||
CourseSessionEdoniqTest,
|
||||
)
|
||||
from vbv_lernwelt.learnpath.graphql.types import LearningPathObjectType
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
|
@ -83,10 +85,23 @@ def resolve_course_page(
|
|||
|
||||
|
||||
class CourseObjectType(DjangoObjectType):
|
||||
learning_path = graphene.Field(LearningPathObjectType, required=True)
|
||||
action_competences = graphene.List(
|
||||
graphene.NonNull(ActionCompetenceObjectType), required=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Course
|
||||
fields = ("id", "title", "category_name", "slug")
|
||||
|
||||
@staticmethod
|
||||
def resolve_learning_path(root: Course, info):
|
||||
return root.get_learning_path()
|
||||
|
||||
@staticmethod
|
||||
def resolve_action_competences(root: Course, info):
|
||||
return root.get_action_competences()
|
||||
|
||||
|
||||
class CourseSessionUserExpertCircleType(ObjectType):
|
||||
id = graphene.ID(required=True)
|
||||
|
|
|
|||
|
|
@ -32,21 +32,10 @@ class Course(models.Model):
|
|||
|
||||
return self.coursepage.get_children().exact_type(LearningPath).first().specific
|
||||
|
||||
def get_learning_path_url(self):
|
||||
return self.get_learning_path().get_frontend_url()
|
||||
def get_action_competences(self):
|
||||
from vbv_lernwelt.competence.models import ActionCompetence
|
||||
|
||||
def get_cockpit_url(self):
|
||||
return self.get_learning_path().get_cockpit_url()
|
||||
|
||||
def get_competence_url(self):
|
||||
from vbv_lernwelt.competence.models import ActionCompetenceListPage
|
||||
|
||||
competence_page = (
|
||||
self.coursepage.get_descendants()
|
||||
.exact_type(ActionCompetenceListPage)
|
||||
.first()
|
||||
)
|
||||
return competence_page.specific.get_frontend_url()
|
||||
return self.coursepage.get_descendants().exact_type(ActionCompetence).specific()
|
||||
|
||||
def get_media_library_url(self):
|
||||
from vbv_lernwelt.media_library.models import MediaLibraryPage
|
||||
|
|
|
|||
|
|
@ -2,16 +2,6 @@ import graphene
|
|||
|
||||
from vbv_lernwelt.course.graphql.types import resolve_course_page
|
||||
from vbv_lernwelt.learnpath.graphql.types import (
|
||||
LearningContentAssignmentObjectType,
|
||||
LearningContentAttendanceCourseObjectType,
|
||||
LearningContentDocumentListObjectType,
|
||||
LearningContentEdoniqTestObjectType,
|
||||
LearningContentFeedbackObjectType,
|
||||
LearningContentLearningModuleObjectType,
|
||||
LearningContentMediaLibraryObjectType,
|
||||
LearningContentPlaceholderObjectType,
|
||||
LearningContentRichTextObjectType,
|
||||
LearningContentVideoObjectType,
|
||||
LearningPathObjectType,
|
||||
)
|
||||
from vbv_lernwelt.learnpath.models import LearningPath
|
||||
|
|
@ -38,53 +28,3 @@ class LearningPathQuery:
|
|||
course_id=course_id,
|
||||
course_slug=course_slug,
|
||||
)
|
||||
|
||||
# dummy import, so that graphene recognizes the types
|
||||
learning_content_media_library = graphene.Field(
|
||||
LearningContentMediaLibraryObjectType
|
||||
)
|
||||
learning_content_assignment = graphene.Field(LearningContentAssignmentObjectType)
|
||||
learning_content_attendance_course = graphene.Field(
|
||||
LearningContentAttendanceCourseObjectType
|
||||
)
|
||||
learning_content_feedback = graphene.Field(LearningContentFeedbackObjectType)
|
||||
learning_content_learning_module = graphene.Field(
|
||||
LearningContentLearningModuleObjectType
|
||||
)
|
||||
learning_content_placeholder = graphene.Field(LearningContentPlaceholderObjectType)
|
||||
learning_content_rich_text = graphene.Field(LearningContentRichTextObjectType)
|
||||
learning_content_test = graphene.Field(LearningContentEdoniqTestObjectType)
|
||||
learning_content_video = graphene.Field(LearningContentVideoObjectType)
|
||||
learning_content_document_list = graphene.Field(
|
||||
LearningContentDocumentListObjectType
|
||||
)
|
||||
|
||||
def resolve_learning_content_media_library(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_assignment(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_attendance_course(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_feedback(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_learning_module(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_placeholder(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_rich_text(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_test(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_video(self, info):
|
||||
return None
|
||||
|
||||
def resolve_learning_content_document_list(self, info):
|
||||
return None
|
||||
|
|
|
|||
Loading…
Reference in New Issue