Query performance criteria also by course

This commit is contained in:
Daniel Egger 2023-10-13 15:08:58 +02:00
parent a6cf4ad128
commit a637775b81
21 changed files with 634 additions and 512 deletions

View File

@ -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,

View File

@ -20,7 +20,7 @@ const documents = {
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n competence_certificate {\n ...CoursePageFields\n }\n }\n assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n assignment_user_id: $assignmentUserId\n learning_content_page_id: $learningContentId\n ) {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_user {\n id\n }\n assignment_user {\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_passed\n edoniq_extended_time_flag\n completion_data\n 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

View File

@ -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
scalar GenericScalar
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]
}
"""
Allows use of a JSON String for input / output from the GraphQL schema.
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!
"""
Ü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 AttendanceUserObjectType {
user_id: UUID!
status: AttendanceUserStatus!
first_name: String
last_name: String
email: String
}
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 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 {

View File

@ -1,3 +1,4 @@
export const ActionCompetenceObjectType = "ActionCompetenceObjectType";
export const AssignmentAssignmentAssignmentTypeChoices = "AssignmentAssignmentAssignmentTypeChoices";
export const AssignmentAssignmentCompletionCompletionStatusChoices = "AssignmentAssignmentCompletionCompletionStatusChoices";
export const AssignmentCompletionMutation = "AssignmentCompletionMutation";

View File

@ -193,42 +193,60 @@ export const COURSE_SESSION_DETAIL_QUERY = graphql(`
}
`);
export const LEARNING_PATH_QUERY = graphql(`
query learningPathQuery($slug: String!) {
learning_path(slug: $slug) {
...CoursePageFields
topics {
is_visible
export const COURSE_QUERY = graphql(`
query courseQuery($slug: String!) {
course(slug: $slug) {
id
slug
category_name
action_competences {
competence_id
...CoursePageFields
circles {
description
goals
performance_criteria {
competence_id
learning_unit {
id
slug
evaluate_url
}
...CoursePageFields
learning_sequences {
icon
}
}
learning_path {
...CoursePageFields
topics {
is_visible
...CoursePageFields
circles {
description
goals
...CoursePageFields
learning_units {
evaluate_url
learning_sequences {
icon
...CoursePageFields
performance_criteria {
learning_units {
evaluate_url
...CoursePageFields
}
learning_contents {
can_user_self_toggle_course_completion
...CoursePageFields
... on LearningContentAssignmentObjectType {
assignment_type
content_assignment {
id
}
competence_certificate {
...CoursePageFields
}
performance_criteria {
...CoursePageFields
}
... on LearningContentEdoniqTestObjectType {
checkbox_text
competence_certificate {
...CoursePageFields
learning_contents {
can_user_self_toggle_course_completion
...CoursePageFields
... on LearningContentAssignmentObjectType {
assignment_type
content_assignment {
id
}
competence_certificate {
...CoursePageFields
}
}
... on LearningContentEdoniqTestObjectType {
checkbox_text
competence_certificate {
...CoursePageFields
}
}
}
}

View File

@ -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);
}

View File

@ -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>

View File

@ -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>

View File

@ -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"

View File

@ -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"
>

View File

@ -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>

View File

@ -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>

View File

@ -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,
};
}

View File

@ -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 type PerformanceCriteria = Omit<PerformanceCriteriaObjectType, "content_type"> &
Completable & {
readonly content_type: "competence.PerformanceCriteria";
circle: CircleLight;
};
}
export interface CompetencePage extends BaseCourseWagtailPage {
readonly content_type: "competence.CompetencePage";
readonly competence_id: string;
readonly children: PerformanceCriteria[];
}
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;

View File

@ -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()

View File

@ -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"),

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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