Refactor completions query to get completions for a list of users

This commit is contained in:
Daniel Egger 2024-07-26 17:01:00 +02:00
parent 9674abfa94
commit da348b7756
17 changed files with 113 additions and 298 deletions

View File

@ -1,12 +1,7 @@
import { useCSRFFetch } from "@/fetchHelpers";
import type { CourseStatisticsType } from "@/gql/graphql";
import { graphqlClient } from "@/graphql/client";
import {
COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY,
COMPETENCE_NAVI_CERTIFICATE_QUERY,
COURSE_QUERY,
COURSE_SESSION_DETAIL_QUERY,
} from "@/graphql/queries";
import { COURSE_QUERY, COURSE_SESSION_DETAIL_QUERY } from "@/graphql/queries";
import {
circleFlatChildren,
circleFlatLearningContents,
@ -31,6 +26,7 @@ import { useDashboardStore } from "@/stores/dashboard";
import { useUserStore } from "@/stores/user";
import type {
ActionCompetence,
AgentParticipantRelation,
CircleType,
Course,
CourseCompletion,
@ -39,7 +35,6 @@ import type {
CourseSessionDetail,
DashboardPersonsPageMode,
LearningContentWithCompletion,
AgentParticipantRelation,
LearningPathType,
LearningUnitPerformanceCriteria,
PerformanceCriteria,
@ -51,7 +46,7 @@ import orderBy from "lodash/orderBy";
import log from "loglevel";
import type { ComputedRef, Ref } from "vue";
import { computed, onMounted, ref, watchEffect } from "vue";
import { useRouter, type RouteLocationRaw } from "vue-router";
import { type RouteLocationRaw, useRouter } from "vue-router";
export function useCurrentCourseSession() {
/**
@ -674,32 +669,6 @@ export function useCourseStatisticsv2(courseSlug: string) {
};
}
export function useCertificateQuery(userId: string | undefined, courseSlug: string) {
const certificatesQuery = (() => {
const courseSession = useCurrentCourseSession();
if (userId) {
return useQuery({
query: COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY,
variables: {
courseSlug: courseSlug,
courseSessionId: courseSession.value.id,
userId: userId,
},
});
} else {
return useQuery({
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
variables: {
courseSlug: courseSlug,
courseSessionId: courseSession.value.id,
},
});
}
})();
return { certificatesQuery };
}
export function useEvaluationWithFeedback() {
const currentCourseSession = useCurrentCourseSession();
const hasFeedback = computed(

View File

@ -18,8 +18,7 @@ const documents = {
"\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc,
"\n query attendanceCheckQuery($courseSessionId: ID!) {\n course_session_attendance_course(id: $courseSessionId) {\n id\n attendance_user_list {\n user_id\n status\n }\n }\n }\n": types.AttendanceCheckQueryDocument,
"\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 solution_sample {\n id\n url\n }\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 first_name\n last_name\n }\n assignment_user {\n avatar_url\n first_name\n last_name\n id\n }\n evaluation_points\n evaluation_max_points\n evaluation_points_deducted\n evaluation_points_deducted_reason\n evaluation_points_final\n\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 competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\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 competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\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.CompetenceCertificateForUserQueryDocument,
"\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userIds: [UUID!]!\n ) {\n competence_certificate_list(course_slug: $courseSlug, user_ids: $userIds) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completions(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\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.CompetenceCertificateForUserQueryDocument,
"\n query courseSessionDetail($courseSessionId: ID!) {\n course_session(id: $courseSessionId) {\n id\n title\n course {\n id\n title\n slug\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n }\n }\n users {\n id\n user_id\n first_name\n last_name\n email\n avatar_url\n role\n circles {\n id\n title\n slug\n }\n }\n attendance_courses {\n id\n location\n trainer\n due_date {\n id\n start\n end\n }\n learning_content_id\n learning_content {\n id\n title\n circle {\n id\n title\n slug\n }\n }\n }\n assignments {\n id\n submission_deadline {\n id\n start\n }\n evaluation_deadline {\n id\n start\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n edoniq_tests {\n id\n deadline {\n id\n start\n end\n }\n learning_content {\n id\n title\n content_assignment {\n id\n title\n assignment_type\n }\n }\n }\n }\n }\n": types.CourseSessionDetailDocument,
"\n query courseQuery($slug: String!) {\n course(slug: $slug) {\n id\n title\n slug\n category_name\n configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n action_competences {\n competence_id\n ...CoursePageFields\n performance_criteria {\n competence_id\n learning_unit {\n id\n slug\n evaluate_url\n }\n ...CoursePageFields\n }\n }\n learning_path {\n ...CoursePageFields\n topics {\n is_visible\n ...CoursePageFields\n circles {\n description\n goals\n ...CoursePageFields\n learning_sequences {\n icon\n ...CoursePageFields\n learning_units {\n evaluate_url\n ...CoursePageFields\n performance_criteria {\n ...CoursePageFields\n }\n learning_contents {\n can_user_self_toggle_course_completion\n content_url\n minutes\n description\n ...CoursePageFields\n ... on LearningContentAssignmentObjectType {\n assignment_type\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentEdoniqTestObjectType {\n checkbox_text\n has_extended_time_test\n content_assignment {\n id\n assignment_type\n }\n competence_certificate {\n ...CoursePageFields\n }\n }\n ... on LearningContentRichTextObjectType {\n text\n }\n }\n }\n }\n }\n }\n }\n }\n }\n": types.CourseQueryDocument,
"\n query dashboardConfig {\n dashboard_config {\n id\n slug\n name\n dashboard_type\n course_configuration {\n id\n enable_circle_documents\n enable_learning_mentor\n enable_competence_certificates\n is_uk\n }\n }\n }\n": types.DashboardConfigDocument,
@ -67,11 +66,7 @@ export function graphql(source: "\n query assignmentCompletionQuery(\n $assi
/**
* 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 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 competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\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"): (typeof documents)["\n query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {\n competence_certificate_list(course_slug: $courseSlug) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_deducted\n evaluation_points_final\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"];
/**
* 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 competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\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"): (typeof documents)["\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userId: UUID!\n ) {\n competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completion(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\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"];
export function graphql(source: "\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userIds: [UUID!]!\n ) {\n competence_certificate_list(course_slug: $courseSlug, user_ids: $userIds) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completions(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\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"): (typeof documents)["\n query competenceCertificateForUserQuery(\n $courseSlug: String!\n $courseSessionId: ID!\n $userIds: [UUID!]!\n ) {\n competence_certificate_list(course_slug: $courseSlug, user_ids: $userIds) {\n ...CoursePageFields\n competence_certificates {\n ...CoursePageFields\n assignments {\n ...CoursePageFields\n assignment_type\n max_points\n competence_certificate_weight\n completions(course_session_id: $courseSessionId) {\n id\n completion_status\n submitted_at\n evaluation_points\n evaluation_points_final\n evaluation_points_deducted\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"];
/**
* 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

@ -20,8 +20,7 @@ type Query {
learning_content_video: LearningContentVideoObjectType
learning_content_document_list: LearningContentDocumentListObjectType
competence_certificate(id: ID, slug: String): CompetenceCertificateObjectType
competence_certificate_list(id: ID, slug: String, course_id: ID, course_slug: String): CompetenceCertificateListObjectType
competence_certificate_list_for_user(id: ID, slug: String, course_id: ID, course_slug: String, user_id: UUID): CompetenceCertificateListObjectType
competence_certificate_list(id: ID, slug: String, course_id: ID, course_slug: String, user_ids: [UUID]): CompetenceCertificateListObjectType
assignment(id: ID, slug: String): AssignmentObjectType
assignment_completion(assignment_id: ID!, course_session_id: ID!, learning_content_page_id: ID, assignment_user_id: UUID): AssignmentCompletionObjectType
}
@ -520,7 +519,7 @@ type AssignmentObjectType implements CoursePageInterface {
max_points: Int
competence_certificate_weight: Float
learning_content: LearningContentInterface
completion(course_session_id: ID!, learning_content_page_id: ID, assignment_user_id: UUID): AssignmentCompletionObjectType
completions(course_session_id: ID!, learning_content_page_id: ID, assignment_user_ids: [UUID]): [AssignmentCompletionObjectType]
solution_sample: ContentDocumentObjectType
}

View File

@ -90,47 +90,12 @@ export const ASSIGNMENT_COMPLETION_QUERY = graphql(`
`);
export const COMPETENCE_NAVI_CERTIFICATE_QUERY = graphql(`
query competenceCertificateQuery($courseSlug: String!, $courseSessionId: ID!) {
competence_certificate_list(course_slug: $courseSlug) {
...CoursePageFields
competence_certificates {
...CoursePageFields
assignments {
...CoursePageFields
assignment_type
max_points
competence_certificate_weight
completion(course_session_id: $courseSessionId) {
id
completion_status
submitted_at
evaluation_points
evaluation_points_deducted
evaluation_points_final
evaluation_max_points
evaluation_passed
}
learning_content {
...CoursePageFields
circle {
id
title
slug
}
}
}
}
}
}
`);
export const COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY = graphql(`
query competenceCertificateForUserQuery(
$courseSlug: String!
$courseSessionId: ID!
$userId: UUID!
$userIds: [UUID!]!
) {
competence_certificate_list_for_user(course_slug: $courseSlug, user_id: $userId) {
competence_certificate_list(course_slug: $courseSlug, user_ids: $userIds) {
...CoursePageFields
competence_certificates {
...CoursePageFields
@ -139,7 +104,7 @@ export const COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY = graphql(`
assignment_type
max_points
competence_certificate_weight
completion(course_session_id: $courseSessionId) {
completions(course_session_id: $courseSessionId) {
id
completion_status
submitted_at

View File

@ -48,7 +48,7 @@ const getIconName = () => {
</div>
<div class="grow lg:px-8">
<div
v-if="assignment.completion?.completion_status === 'EVALUATION_SUBMITTED'"
v-if="assignment.completions[0]?.completion_status === 'EVALUATION_SUBMITTED'"
class="flex items-center"
>
<div
@ -61,7 +61,7 @@ const getIconName = () => {
<div
v-else-if="
['EVALUATION_IN_PROGRESS', 'SUBMITTED'].includes(
assignment.completion?.completion_status || ''
assignment.completions[0]?.completion_status || ''
)
"
class="flex items-center"
@ -76,31 +76,31 @@ const getIconName = () => {
</div>
<div>
<div
v-if="assignment.completion?.completion_status === 'EVALUATION_SUBMITTED'"
v-if="assignment.completions[0]?.completion_status === 'EVALUATION_SUBMITTED'"
class="flex flex-col lg:items-center"
>
<div class="flex flex-col lg:items-center">
<div class="heading-2">
{{ assignment.completion?.evaluation_points_final }}
{{ assignment.completions[0]?.evaluation_points_final }}
</div>
<div>
{{ $t("assignment.von x Punkten", { x: assignment.max_points }) }}
({{
(
((assignment.completion?.evaluation_points_final ?? 0) /
(assignment.completion?.evaluation_max_points ?? 1)) *
((assignment.completions[0]?.evaluation_points_final ?? 0) /
(assignment.completions[0]?.evaluation_max_points ?? 1)) *
100
).toFixed(0)
}}%)
</div>
<div
v-if="(assignment.completion?.evaluation_points_deducted ?? 0) > 0"
v-if="(assignment.completions[0]?.evaluation_points_deducted ?? 0) > 0"
class="text-gray-900"
>
{{ $t("a.mit Abzug") }}
</div>
<div
v-if="assignment.completion && !assignment.completion.evaluation_passed"
v-if="assignment.completions[0] && !assignment.completions[0].evaluation_passed"
class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5"
>
{{ $t("a.Nicht Bestanden") }}

View File

@ -32,7 +32,7 @@ const userGradeRounded2Places = computed(() => {
const numAssignmentsEvaluated = computed(() => {
return props.competenceCertificate.assignments.filter((a) => {
return a.completion?.completion_status === "EVALUATION_SUBMITTED";
return a.completions[0]?.completion_status === "EVALUATION_SUBMITTED";
}).length;
});

View File

@ -2,10 +2,12 @@
import log from "loglevel";
import { computed } from "vue";
import type { CompetenceCertificate } from "@/types";
import { useCertificateQuery } from "@/composables";
import { useCurrentCourseSession } from "@/composables";
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
import { getCertificates } from "@/services/competence";
import { getPreviousRoute } from "@/router/history";
import { useQuery } from "@urql/vue";
import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
import { useUserStore } from "@/stores/user";
const props = defineProps<{
courseSlug: string;
@ -15,24 +17,29 @@ const props = defineProps<{
log.debug("CompetenceCertificateDetailPage setup", props);
const certificatesQuery = useCertificateQuery(
props.userId,
props.courseSlug
).certificatesQuery;
const user = useUserStore();
const courseSession = useCurrentCourseSession();
const certificatesQuery = useQuery({
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
variables: {
courseSlug: props.courseSlug,
courseSessionId: courseSession.value.id,
userIds: [user.id ?? user.id],
},
});
const competenceCertificates = computed(() => {
return (
(certificatesQuery.data.value?.competence_certificate_list
?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
);
});
const certificate = computed(() => {
const certificates = getCertificates(
certificatesQuery.data.value,
props.userId ?? null
return competenceCertificates.value.find((cc) =>
cc.slug.endsWith(props.certificateSlug)
);
if (!certificates) {
return null;
}
return (
(certificates.competence_certificates as unknown as CompetenceCertificate[]) ?? []
).find((cc) => cc.slug.endsWith(props.certificateSlug));
});
</script>

View File

@ -2,14 +2,16 @@
import log from "loglevel";
import { computed, onMounted } from "vue";
import type { CompetenceCertificate } from "@/types";
import { useCertificateQuery } from "@/composables";
import { useCurrentCourseSession } from "@/composables";
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
import {
assignmentsUserPoints,
calcCompetencesTotalGrade,
} from "@/pages/competence/utils";
import { useRoute } from "vue-router";
import { getCertificates } from "@/services/competence";
import { useQuery } from "@urql/vue";
import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
import { useUserStore } from "@/stores/user";
const props = defineProps<{
courseSlug: string;
@ -20,23 +22,22 @@ log.debug("CompetenceCertificateListPage setup", props);
const route = useRoute();
const certificatesQuery = useCertificateQuery(
props.userId,
props.courseSlug
).certificatesQuery;
const user = useUserStore();
const courseSession = useCurrentCourseSession();
const certificatesQuery = useQuery({
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
variables: {
courseSlug: props.courseSlug,
courseSessionId: courseSession.value.id,
userIds: [user.id ?? user.id],
},
});
const competenceCertificates = computed(() => {
const certificates = getCertificates(
certificatesQuery.data.value,
props.userId ?? null
);
if (!certificates) {
return null;
}
return (
(certificates?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
(certificatesQuery.data.value?.competence_certificate_list
?.competence_certificates as unknown as CompetenceCertificate[]) ?? []
);
});
@ -54,7 +55,7 @@ const userPointsEvaluatedAssignments = computed(() => {
const numAssignmentsEvaluated = computed(() => {
return (assignments.value ?? []).filter((a) => {
return a.completion?.completion_status === "EVALUATION_SUBMITTED";
return a.completions[0]?.completion_status === "EVALUATION_SUBMITTED";
}).length;
});

View File

@ -1,6 +1,8 @@
<script setup lang="ts">
import log from "loglevel";
import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
import {
COMPETENCE_NAVI_CERTIFICATE_QUERY,
} from "@/graphql/queries";
import { useQuery } from "@urql/vue";
import { computed } from "vue";
import type { CompetenceCertificate } from "@/types";
@ -24,11 +26,14 @@ log.debug("CompetenceIndexPage setup", props);
const courseSession = useCurrentCourseSession();
const user = useUserStore();
const certificatesQuery = useQuery({
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
variables: {
courseSlug: props.courseSlug,
courseSessionId: courseSession.value.id,
userIds: [user.id ?? user.id],
},
});
@ -105,7 +110,8 @@ const router = useRouter();
{{
$t("assignment.x von y Kompetenznachweis-Elementen abgeschlossen", {
x: certificate.assignments.filter(
(a) => a.completion?.completion_status === "EVALUATION_SUBMITTED"
(a) =>
a.completions[0]?.completion_status === "EVALUATION_SUBMITTED"
).length,
y: certificate.assignments.length,
})

View File

@ -8,7 +8,7 @@ export function assignmentsMaxEvaluationPoints(
): number {
return _.sum(
assignments
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
.filter((a) => a.completions[0]?.completion_status === "EVALUATION_SUBMITTED")
.map((a) => a.max_points)
);
}
@ -16,8 +16,8 @@ export function assignmentsMaxEvaluationPoints(
export function assignmentsUserPoints(assignments: CompetenceCertificateAssignment[]) {
return +_.sum(
assignments
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
.map((a) => a.completion?.evaluation_points_final ?? 0)
.filter((a) => a.completions[0]?.completion_status === "EVALUATION_SUBMITTED")
.map((a) => a.completions[0]?.evaluation_points_final ?? 0)
).toFixed(1);
}
@ -26,12 +26,12 @@ export function calcCompetenceCertificateGrade(
roundedToHalfGrade = true
) {
const evaluatedAssignments = assignments.filter(
(a) => a.completion?.completion_status === "EVALUATION_SUBMITTED"
(a) => a.completions[0]?.completion_status === "EVALUATION_SUBMITTED"
);
const adjustedResults = evaluatedAssignments.map((a) => {
return (
((a.completion?.evaluation_points_final ?? 0) / a.max_points) *
((a.completions[0]?.evaluation_points_final ?? 0) / a.max_points) *
a.competence_certificate_weight
);
});
@ -76,7 +76,7 @@ export function competenceCertificateProgressStatusCount(
assignments: CompetenceCertificateAssignment[]
) {
const numAssignmentsEvaluated = assignments.filter(
(a) => a.completion?.completion_status === "EVALUATION_SUBMITTED"
(a) => a.completions[0]?.completion_status === "EVALUATION_SUBMITTED"
).length;
return {
SUCCESS: numAssignmentsEvaluated,

View File

@ -10,7 +10,6 @@ import { useDashboardStore } from "@/stores/dashboard";
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
import _ from "lodash";
import { percentToRoundedGrade } from "@/services/assignmentService";
import { a } from "vitest/dist/suite-BWgaIsVn";
const dashboardStore = useDashboardStore();

View File

@ -1,8 +1,3 @@
import type {
CompetenceCertificateForUserQueryQuery,
CompetenceCertificateListObjectType,
CompetenceCertificateQueryQuery,
} from "@/gql/graphql";
import type { PerformanceCriteria } from "@/types";
import groupBy from "lodash/groupBy";
@ -22,42 +17,3 @@ export function calcPerformanceCriteriaStatusCount(criteria: PerformanceCriteria
FAIL: 0,
};
}
// Type guards
export function isCompetenceCertificateForUserQueryQuery(
data: any
): data is CompetenceCertificateForUserQueryQuery {
return (
(data as CompetenceCertificateForUserQueryQuery)
.competence_certificate_list_for_user !== undefined
);
}
export function isCompetenceCertificateQueryQuery(
data: any
): data is CompetenceCertificateQueryQuery {
return (
(data as CompetenceCertificateQueryQuery).competence_certificate_list !== undefined
);
}
export function getCertificates(
data: any,
userId: string | null
): CompetenceCertificateListObjectType | null {
if (!data) {
return null;
}
let certificates = null;
if (userId && isCompetenceCertificateForUserQueryQuery(data)) {
certificates = data.competence_certificate_list_for_user;
} else if (isCompetenceCertificateQueryQuery(data)) {
certificates = data.competence_certificate_list;
} else {
// Handle case where data does not match expected types
console.error("Data structure is not recognized!");
return null;
}
return (certificates as unknown as CompetenceCertificateListObjectType) ?? null;
}

View File

@ -398,7 +398,7 @@ export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
circle: CircleLight;
})
| null;
completion: {
completions: {
id: string;
completion_status: AssignmentCompletionStatus;
evaluation_submitted_at: string | null;
@ -408,7 +408,7 @@ export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
evaluation_points_reason: string;
evaluation_max_points: number | null;
evaluation_passed: boolean | null;
} | null;
}[];
}
export interface CompetenceCertificate extends BaseCourseWagtailPage {

View File

@ -69,11 +69,11 @@ class AssignmentObjectType(DjangoObjectType):
max_points = graphene.Int()
competence_certificate_weight = graphene.Float()
learning_content = graphene.Field(LearningContentInterface)
completion = graphene.Field(
completions = graphene.List(
AssignmentCompletionObjectType,
course_session_id=graphene.ID(required=True),
learning_content_page_id=graphene.ID(required=False),
assignment_user_id=graphene.UUID(required=False),
assignment_user_ids=graphene.List(graphene.UUID, required=False),
)
solution_sample = graphene.Field(ContentDocumentObjectType)
@ -103,28 +103,33 @@ class AssignmentObjectType(DjangoObjectType):
def resolve_learning_content(self, info):
return self.find_attached_learning_content()
def resolve_completion(
def resolve_completions(
self,
info,
course_session_id,
learning_content_page_id=None,
assignment_user_id=None,
assignment_user_ids=None,
):
if learning_content_page_id is None:
lp = self.find_attached_learning_content()
if lp:
learning_content_page_id = lp.id
if not assignment_user_id:
assignment_user_id = getattr(info.context, "assignment_user_id", None)
if not assignment_user_ids:
assignment_user_ids = getattr(info.context, "assignment_user_ids", [])
return resolve_assignment_completion(
completions = []
for user_id in assignment_user_ids:
completion = resolve_assignment_completion(
info=info,
course_session_id=course_session_id,
learning_content_page_id=learning_content_page_id,
assignment_user_id=assignment_user_id,
assignment_user_id=user_id,
assignment_id=self.id,
)
if completion:
completions.append(completion)
return completions
def resolve_assignment_completion(

View File

@ -24,47 +24,23 @@ class CompetenceCertificateQuery(object):
slug=graphene.String(),
course_id=graphene.ID(),
course_slug=graphene.String(),
)
competence_certificate_list_for_user = graphene.Field(
CompetenceCertificateListObjectType,
id=graphene.ID(),
slug=graphene.String(),
course_id=graphene.ID(),
course_slug=graphene.String(),
user_id=graphene.UUID(),
user_ids=graphene.List(graphene.UUID),
)
def resolve_competence_certificate(root, info, id=None, slug=None):
return resolve_course_page(CompetenceCertificate, root, info, id=id, slug=slug)
def resolve_competence_certificate_list(
root, info, id=None, slug=None, course_id=None, course_slug=None
root, info, id=None, slug=None, course_id=None, course_slug=None, user_ids=None
):
return resolve_course_page(
CompetenceCertificateList,
root,
info,
id=id,
slug=slug,
course_id=course_id,
course_slug=course_slug,
)
def resolve_competence_certificate_list_for_user(
root, info, id=None, slug=None, course_id=None, course_slug=None, user_id=None
):
try:
for user_id in user_ids:
course_session_user = CourseSessionUser.objects.filter(
user__id=user_id
).first()
except CourseSessionUser.DoesNotExist:
return None
if not can_view_profile(info.context.user, course_session_user):
return None
setattr(info.context, "assignment_user_id", user_id)
setattr(info.context, "assignment_user_ids", user_ids)
return resolve_course_page(
CompetenceCertificateList,

View File

@ -76,7 +76,7 @@ class TestCertificateList(GraphQLTestCase):
$courseSessionId: ID!,
$userId: UUID!
) {{
competence_certificate_list_for_user(
competence_certificate_list(
course_slug: $courseSlug,
user_id: $userId
) {{
@ -136,7 +136,7 @@ fragment CoursePageFields on CoursePageInterface {{
# THEN
self.assertResponseNoErrors(response)
certificates = response.json()["data"]["competence_certificate_list_for_user"][
certificates = response.json()["data"]["competence_certificate_list"][
"competence_certificates"
]
self.assertEqual(len(certificates), 1)
@ -161,7 +161,7 @@ fragment CoursePageFields on CoursePageInterface {{
$courseSessionId: ID!,
$userId: UUID!
) {{
competence_certificate_list_for_user(
competence_certificate_list(
course_slug: $courseSlug,
user_id: $userId
) {{
@ -221,7 +221,7 @@ fragment CoursePageFields on CoursePageInterface {{
# THEN
self.assertResponseNoErrors(response)
self.assertIsNone(
response.json()["data"]["competence_certificate_list_for_user"]
response.json()["data"]["competence_certificate_list"]
)
def test_member_userprofile_certificate_summary(self):