Merged develop into feature/VBV-684-export-trainer-rl
This commit is contained in:
commit
0cad9666c5
|
|
@ -106,7 +106,12 @@ function evaluationForTask(task: AssignmentEvaluationTask) {
|
||||||
return expertData;
|
return expertData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxPoints = computed(() => maxAssignmentPoints(props.assignment));
|
const maxPoints = computed(() => {
|
||||||
|
if (props.assignmentCompletion.completion_status === "EVALUATION_SUBMITTED") {
|
||||||
|
return props.assignmentCompletion.evaluation_max_points;
|
||||||
|
}
|
||||||
|
return maxAssignmentPoints(props.assignment);
|
||||||
|
});
|
||||||
const userPoints = computed(() =>
|
const userPoints = computed(() =>
|
||||||
userAssignmentPoints(props.assignment, props.assignmentCompletion)
|
userAssignmentPoints(props.assignment, props.assignmentCompletion)
|
||||||
);
|
);
|
||||||
|
|
@ -128,23 +133,50 @@ const userPoints = computed(() =>
|
||||||
</h3>
|
</h3>
|
||||||
<h3 v-else class="mb-6" data-cy="sub-title">{{ $t(text.evaluationSubmission) }}</h3>
|
<h3 v-else class="mb-6" data-cy="sub-title">{{ $t(text.evaluationSubmission) }}</h3>
|
||||||
<section class="mb-6 border p-6" data-cy="result-section">
|
<section class="mb-6 border p-6" data-cy="result-section">
|
||||||
<section
|
<section v-if="props.assignment.assignment_type === 'CASEWORK'">
|
||||||
v-if="props.assignment.assignment_type === 'CASEWORK'"
|
<div class="flex items-center">
|
||||||
class="flex items-center"
|
|
||||||
>
|
|
||||||
<div class="heading-1 py-4" data-cy="user-points">
|
<div class="heading-1 py-4" data-cy="user-points">
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
props.assignmentCompletion.completion_status == 'EVALUATION_SUBMITTED'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ props.assignmentCompletion.evaluation_points_final }}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
{{ userPoints }}
|
{{ userPoints }}
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="pl-2" data-cy="total-points">
|
<div class="pl-2" data-cy="total-points">
|
||||||
{{ $t("assignment.von x Punkten", { x: maxPoints }) }}
|
{{ $t("assignment.von x Punkten", { x: maxPoints }) }}
|
||||||
({{
|
({{
|
||||||
(
|
(
|
||||||
((props.assignmentCompletion?.evaluation_points ?? 0) /
|
((props.assignmentCompletion?.evaluation_points_final ?? 0) /
|
||||||
(props.assignmentCompletion?.evaluation_max_points ?? 1)) *
|
(props.assignmentCompletion?.evaluation_max_points ?? 1)) *
|
||||||
100
|
100
|
||||||
).toFixed(0)
|
).toFixed(0)
|
||||||
}}%)
|
}}%)
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="assignmentCompletion.evaluation_points_deducted > 0"
|
||||||
|
data-cy="points-deducted"
|
||||||
|
class="mb-4 text-gray-900"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ $t("a.Punkte aus Bewertung") }}:
|
||||||
|
{{ assignmentCompletion.evaluation_points }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ $t("a.Abgezogene Punkte") }}:
|
||||||
|
{{ assignmentCompletion.evaluation_points_deducted }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ $t("a.Grund") }}:
|
||||||
|
{{ assignmentCompletion.evaluation_points_deducted_reason }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,7 @@ const getIconName = (lc: LearningContent) => {
|
||||||
v-for="submittable in submittables"
|
v-for="submittable in submittables"
|
||||||
:key="submittable.id"
|
:key="submittable.id"
|
||||||
class="flex flex-col justify-between gap-2 py-4 lg:flex-row lg:gap-4"
|
class="flex flex-col justify-between gap-2 py-4 lg:flex-row lg:gap-4"
|
||||||
|
:data-cy="`submittable-${submittable.content.slug}`"
|
||||||
>
|
>
|
||||||
<div class="flex flex-row items-center gap-2 lg:w-1/3">
|
<div class="flex flex-row items-center gap-2 lg:w-1/3">
|
||||||
<div class="min-h-9 min-w-9">
|
<div class="min-h-9 min-w-9">
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ const documents = {
|
||||||
"\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n $evaluationUserId: ID\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n evaluation_user_id: $evaluationUserId\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_points\n completion_data\n task_completion_data\n }\n }\n }\n": types.UpsertAssignmentCompletionDocument,
|
"\n mutation UpsertAssignmentCompletion(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n $completionStatus: AssignmentCompletionStatus!\n $completionDataString: String!\n $evaluationPoints: Float\n $initializeCompletion: Boolean\n $evaluationUserId: ID\n ) {\n upsert_assignment_completion(\n assignment_id: $assignmentId\n course_session_id: $courseSessionId\n learning_content_page_id: $learningContentId\n assignment_user_id: $assignmentUserId\n completion_status: $completionStatus\n completion_data_string: $completionDataString\n evaluation_points: $evaluationPoints\n initialize_completion: $initializeCompletion\n evaluation_user_id: $evaluationUserId\n ) {\n assignment_completion {\n id\n completion_status\n submitted_at\n evaluation_submitted_at\n evaluation_points\n completion_data\n task_completion_data\n }\n }\n }\n": types.UpsertAssignmentCompletionDocument,
|
||||||
"\n fragment CoursePageFields on CoursePageInterface {\n title\n id\n slug\n content_type\n frontend_url\n }\n": types.CoursePageFieldsFragmentDoc,
|
"\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 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_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n": types.AssignmentCompletionQueryDocument,
|
"\n query assignmentCompletionQuery(\n $assignmentId: ID!\n $courseSessionId: ID!\n $learningContentId: ID\n $assignmentUserId: UUID\n ) {\n assignment(id: $assignmentId) {\n assignment_type\n needs_expert_evaluation\n max_points\n content_type\n effort_required\n evaluation_description\n evaluation_document_url\n evaluation_tasks\n id\n intro_text\n performance_objectives\n slug\n tasks\n title\n translation_key\n 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 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 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 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.CompetenceCertificateForUserQueryDocument,
|
"\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 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 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 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,
|
"\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,
|
||||||
|
|
@ -63,15 +63,15 @@ export function graphql(source: "\n query attendanceCheckQuery($courseSessionId
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query 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_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n"): (typeof 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 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_passed\n edoniq_extended_time_flag\n completion_data\n task_completion_data\n }\n }\n"];
|
export function graphql(source: "\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"): (typeof 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 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"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n query 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"): (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 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"];
|
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.
|
* 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 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"): (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 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"];
|
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"];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -512,6 +512,7 @@ type AssignmentObjectType implements CoursePageInterface {
|
||||||
evaluation_tasks: JSONStreamField
|
evaluation_tasks: JSONStreamField
|
||||||
performance_objectives: JSONStreamField
|
performance_objectives: JSONStreamField
|
||||||
max_points: Int
|
max_points: Int
|
||||||
|
competence_certificate_weight: Float
|
||||||
learning_content: LearningContentInterface
|
learning_content: LearningContentInterface
|
||||||
completion(course_session_id: ID!, learning_content_page_id: ID, assignment_user_id: UUID): AssignmentCompletionObjectType
|
completion(course_session_id: ID!, learning_content_page_id: ID, assignment_user_id: UUID): AssignmentCompletionObjectType
|
||||||
solution_sample: ContentDocumentObjectType
|
solution_sample: ContentDocumentObjectType
|
||||||
|
|
@ -559,6 +560,9 @@ type AssignmentCompletionObjectType {
|
||||||
submitted_at: DateTime
|
submitted_at: DateTime
|
||||||
evaluation_submitted_at: DateTime
|
evaluation_submitted_at: DateTime
|
||||||
evaluation_user: UserObjectType
|
evaluation_user: UserObjectType
|
||||||
|
evaluation_points_deducted: Float!
|
||||||
|
evaluation_points_deducted_reason: String!
|
||||||
|
evaluation_points_deducted_user: UserObjectType
|
||||||
evaluation_passed: Boolean
|
evaluation_passed: Boolean
|
||||||
edoniq_extended_time_flag: Boolean!
|
edoniq_extended_time_flag: Boolean!
|
||||||
assignment_user: UserObjectType!
|
assignment_user: UserObjectType!
|
||||||
|
|
@ -570,6 +574,7 @@ type AssignmentCompletionObjectType {
|
||||||
task_completion_data: GenericScalar
|
task_completion_data: GenericScalar
|
||||||
learning_content_page_id: ID
|
learning_content_page_id: ID
|
||||||
evaluation_points: Float
|
evaluation_points: Float
|
||||||
|
evaluation_points_final: Float
|
||||||
evaluation_max_points: Float
|
evaluation_max_points: Float
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,10 @@ export const ASSIGNMENT_COMPLETION_QUERY = graphql(`
|
||||||
}
|
}
|
||||||
evaluation_points
|
evaluation_points
|
||||||
evaluation_max_points
|
evaluation_max_points
|
||||||
|
evaluation_points_deducted
|
||||||
|
evaluation_points_deducted_reason
|
||||||
|
evaluation_points_final
|
||||||
|
|
||||||
evaluation_passed
|
evaluation_passed
|
||||||
edoniq_extended_time_flag
|
edoniq_extended_time_flag
|
||||||
completion_data
|
completion_data
|
||||||
|
|
@ -95,11 +99,14 @@ export const COMPETENCE_NAVI_CERTIFICATE_QUERY = graphql(`
|
||||||
...CoursePageFields
|
...CoursePageFields
|
||||||
assignment_type
|
assignment_type
|
||||||
max_points
|
max_points
|
||||||
|
competence_certificate_weight
|
||||||
completion(course_session_id: $courseSessionId) {
|
completion(course_session_id: $courseSessionId) {
|
||||||
id
|
id
|
||||||
completion_status
|
completion_status
|
||||||
submitted_at
|
submitted_at
|
||||||
evaluation_points
|
evaluation_points
|
||||||
|
evaluation_points_deducted
|
||||||
|
evaluation_points_final
|
||||||
evaluation_max_points
|
evaluation_max_points
|
||||||
evaluation_passed
|
evaluation_passed
|
||||||
}
|
}
|
||||||
|
|
@ -131,11 +138,14 @@ export const COMPETENCE_NAVI_CERTIFICATE_FOR_USER_QUERY = graphql(`
|
||||||
...CoursePageFields
|
...CoursePageFields
|
||||||
assignment_type
|
assignment_type
|
||||||
max_points
|
max_points
|
||||||
|
competence_certificate_weight
|
||||||
completion(course_session_id: $courseSessionId) {
|
completion(course_session_id: $courseSessionId) {
|
||||||
id
|
id
|
||||||
completion_status
|
completion_status
|
||||||
submitted_at
|
submitted_at
|
||||||
evaluation_points
|
evaluation_points
|
||||||
|
evaluation_points_final
|
||||||
|
evaluation_points_deducted
|
||||||
evaluation_max_points
|
evaluation_max_points
|
||||||
evaluation_passed
|
evaluation_passed
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,18 +81,24 @@ const getIconName = () => {
|
||||||
>
|
>
|
||||||
<div class="flex flex-col lg:items-center">
|
<div class="flex flex-col lg:items-center">
|
||||||
<div class="heading-2">
|
<div class="heading-2">
|
||||||
{{ assignment.completion?.evaluation_points }}
|
{{ assignment.completion?.evaluation_points_final }}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{{ $t("assignment.von x Punkten", { x: assignment.max_points }) }}
|
{{ $t("assignment.von x Punkten", { x: assignment.max_points }) }}
|
||||||
({{
|
({{
|
||||||
(
|
(
|
||||||
((assignment.completion?.evaluation_points ?? 0) /
|
((assignment.completion?.evaluation_points_final ?? 0) /
|
||||||
(assignment.completion?.evaluation_max_points ?? 1)) *
|
(assignment.completion?.evaluation_max_points ?? 1)) *
|
||||||
100
|
100
|
||||||
).toFixed(0)
|
).toFixed(0)
|
||||||
}}%)
|
}}%)
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="(assignment.completion?.evaluation_points_deducted ?? 0) > 0"
|
||||||
|
class="text-gray-900"
|
||||||
|
>
|
||||||
|
{{ $t("a.mit Abzug") }}
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="assignment.completion && !assignment.completion.evaluation_passed"
|
v-if="assignment.completion && !assignment.completion.evaluation_passed"
|
||||||
class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5"
|
class="my-2 rounded-md bg-error-red-200 px-2.5 py-0.5"
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import CompetenceAssignmentRow from "@/pages/competence/CompetenceAssignmentRow.
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||||
import {
|
import {
|
||||||
assignmentsMaxEvaluationPoints,
|
|
||||||
assignmentsUserPoints,
|
assignmentsUserPoints,
|
||||||
|
calcCompetenceCertificateGrade,
|
||||||
competenceCertificateProgressStatusCount,
|
competenceCertificateProgressStatusCount,
|
||||||
} from "@/pages/competence/utils";
|
} from "@/pages/competence/utils";
|
||||||
|
|
||||||
|
|
@ -18,14 +18,18 @@ const props = defineProps<{
|
||||||
frontendUrl?: string;
|
frontendUrl?: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const totalPointsEvaluatedAssignments = computed(() => {
|
|
||||||
return assignmentsMaxEvaluationPoints(props.competenceCertificate.assignments);
|
|
||||||
});
|
|
||||||
|
|
||||||
const userPointsEvaluatedAssignments = computed(() => {
|
const userPointsEvaluatedAssignments = computed(() => {
|
||||||
return assignmentsUserPoints(props.competenceCertificate.assignments);
|
return assignmentsUserPoints(props.competenceCertificate.assignments);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const userGrade = computed(() => {
|
||||||
|
return calcCompetenceCertificateGrade(props.competenceCertificate.assignments, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
const userGradeRounded2Places = computed(() => {
|
||||||
|
return calcCompetenceCertificateGrade(props.competenceCertificate.assignments, false);
|
||||||
|
});
|
||||||
|
|
||||||
const numAssignmentsEvaluated = computed(() => {
|
const numAssignmentsEvaluated = computed(() => {
|
||||||
return props.competenceCertificate.assignments.filter((a) => {
|
return props.competenceCertificate.assignments.filter((a) => {
|
||||||
return a.completion?.completion_status === "EVALUATION_SUBMITTED";
|
return a.completion?.completion_status === "EVALUATION_SUBMITTED";
|
||||||
|
|
@ -70,15 +74,29 @@ const frontendUrl = computed(() => {
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section v-if="userPointsEvaluatedAssignments > 0" class="flex items-center">
|
<section v-if="userPointsEvaluatedAssignments > 0">
|
||||||
|
<div class="flex items-center">
|
||||||
<div
|
<div
|
||||||
class="py-4"
|
class="py-4"
|
||||||
:class="{ 'heading-1': props.detailView, 'heading-2': !props.detailView }"
|
:class="{ 'heading-1': props.detailView, 'heading-2': !props.detailView }"
|
||||||
|
:data-cy="`certificate-${competenceCertificate.slug}-grade`"
|
||||||
>
|
>
|
||||||
{{ userPointsEvaluatedAssignments }}
|
{{ $t("a.Note") }}: {{ userGrade }}
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-1">
|
</div>
|
||||||
{{ $t("assignment.von x Punkten", { x: totalPointsEvaluatedAssignments }) }}
|
<div
|
||||||
|
class="text-gray-900"
|
||||||
|
:data-cy="`certificate-${competenceCertificate.slug}-grade-percent`"
|
||||||
|
>
|
||||||
|
{{ $t("a.Ungerundete Note") }}: {{ userGradeRounded2Places }}.
|
||||||
|
<a
|
||||||
|
:href="$t('a.wegleitungUkUrl')"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="underline"
|
||||||
|
>
|
||||||
|
{{ $t("a.Wegleitung üK") }}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section v-else class="py-2">
|
<section v-else class="py-2">
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import type { CompetenceCertificate } from "@/types";
|
||||||
import { useCertificateQuery } from "@/composables";
|
import { useCertificateQuery } from "@/composables";
|
||||||
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
|
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
|
||||||
import {
|
import {
|
||||||
assignmentsMaxEvaluationPoints,
|
|
||||||
assignmentsUserPoints,
|
assignmentsUserPoints,
|
||||||
|
calcCompetencesTotalGrade,
|
||||||
} from "@/pages/competence/utils";
|
} from "@/pages/competence/utils";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import { getCertificates } from "@/services/competence";
|
import { getCertificates } from "@/services/competence";
|
||||||
|
|
@ -44,8 +44,8 @@ const assignments = computed(() => {
|
||||||
return competenceCertificates?.value?.flatMap((cc) => cc.assignments);
|
return competenceCertificates?.value?.flatMap((cc) => cc.assignments);
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalPointsEvaluatedAssignments = computed(() => {
|
const totalGrade = computed(() => {
|
||||||
return assignmentsMaxEvaluationPoints(assignments.value ?? []);
|
return calcCompetencesTotalGrade(competenceCertificates.value ?? []);
|
||||||
});
|
});
|
||||||
|
|
||||||
const userPointsEvaluatedAssignments = computed(() => {
|
const userPointsEvaluatedAssignments = computed(() => {
|
||||||
|
|
@ -86,16 +86,15 @@ onMounted(async () => {
|
||||||
>
|
>
|
||||||
{{ $t("a.Zwischenstand") }}
|
{{ $t("a.Zwischenstand") }}
|
||||||
</div>
|
</div>
|
||||||
<h3 class="mt-2 lg:order-first lg:mt-0">{{ $t("a.Gesamtpunktzahl") }}</h3>
|
<h3 class="mt-2 lg:order-first lg:mt-0">{{ $t("a.Erfahrungsnote üK") }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section v-if="userPointsEvaluatedAssignments > 0" class="flex items-center">
|
<section
|
||||||
<div class="heading-1 py-4">
|
v-if="userPointsEvaluatedAssignments > 0"
|
||||||
{{ userPointsEvaluatedAssignments }}
|
class="flex items-center"
|
||||||
</div>
|
data-cy="certificate-total-grade"
|
||||||
<div class="pl-2">
|
>
|
||||||
{{ $t("assignment.von x Punkten", { x: totalPointsEvaluatedAssignments }) }}
|
<div class="heading-1 py-4">{{ $t("a.Note") }}: {{ totalGrade }}</div>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
<section v-else class="my-4">
|
<section v-else class="my-4">
|
||||||
{{ $t("a.competenceCertificateNoUserPoints") }}
|
{{ $t("a.competenceCertificateNoUserPoints") }}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ import { computed } from "vue";
|
||||||
import type { CompetenceCertificate } from "@/types";
|
import type { CompetenceCertificate } from "@/types";
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCurrentCourseSession } from "@/composables";
|
||||||
import {
|
import {
|
||||||
assignmentsMaxEvaluationPoints,
|
|
||||||
assignmentsUserPoints,
|
assignmentsUserPoints,
|
||||||
|
calcCompetenceCertificateGrade,
|
||||||
|
calcCompetencesTotalGrade,
|
||||||
competenceCertificateProgressStatusCount,
|
competenceCertificateProgressStatusCount,
|
||||||
} from "@/pages/competence/utils";
|
} from "@/pages/competence/utils";
|
||||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||||
|
|
@ -42,10 +43,6 @@ const allAssignments = computed(() => {
|
||||||
return competenceCertificates.value.flatMap((cc) => cc.assignments);
|
return competenceCertificates.value.flatMap((cc) => cc.assignments);
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalPointsEvaluatedAssignments = computed(() => {
|
|
||||||
return assignmentsMaxEvaluationPoints(allAssignments.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
const userPointsEvaluatedAssignments = computed(() => {
|
const userPointsEvaluatedAssignments = computed(() => {
|
||||||
return assignmentsUserPoints(allAssignments.value);
|
return assignmentsUserPoints(allAssignments.value);
|
||||||
});
|
});
|
||||||
|
|
@ -69,11 +66,14 @@ const router = useRouter();
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4" data-cy="certificate-total-points-text">
|
<div class="mt-4" data-cy="certificate-total-points-text">
|
||||||
<div v-if="userPointsEvaluatedAssignments > 0">
|
<div v-if="userPointsEvaluatedAssignments > 0">
|
||||||
{{ $t("a.Zwischenstand") }} {{ $t("a.Gesamtpunktzahl") }}:
|
{{ $t("a.Erfahrungsnote üK") }}:
|
||||||
<span class="font-bold">
|
<span class="font-bold">
|
||||||
{{ userPointsEvaluatedAssignments }}
|
{{ calcCompetencesTotalGrade(competenceCertificates ?? []) }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="rounded-full bg-gray-200 px-2.5 py-0.5 text-sm lg:ml-2">
|
||||||
|
{{ $t("a.Zwischenstand") }}
|
||||||
</span>
|
</span>
|
||||||
{{ $t("assignment.von x Punkten", { x: totalPointsEvaluatedAssignments }) }}
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
{{ $t("a.competenceCertificateNoUserPoints") }}
|
{{ $t("a.competenceCertificateNoUserPoints") }}
|
||||||
|
|
@ -92,14 +92,13 @@ const router = useRouter();
|
||||||
{{ certificate.title }}
|
{{ certificate.title }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 lg:mt-0">
|
<div class="mt-4 lg:mt-0">
|
||||||
<span class="text-bold">
|
<span
|
||||||
{{ assignmentsUserPoints(certificate.assignments) }}
|
v-if="calcCompetenceCertificateGrade(certificate.assignments)"
|
||||||
|
class="text-bold"
|
||||||
|
>
|
||||||
|
{{ $t("a.Note") }}:
|
||||||
|
{{ calcCompetenceCertificateGrade(certificate.assignments) }}
|
||||||
</span>
|
</span>
|
||||||
{{
|
|
||||||
$t("assignment.von x Punkten", {
|
|
||||||
x: assignmentsMaxEvaluationPoints(certificate.assignments),
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import type { StatusCount } from "@/components/ui/ItProgress.vue";
|
import type { StatusCount } from "@/components/ui/ItProgress.vue";
|
||||||
import type { CompetenceCertificateAssignment } from "@/types";
|
import { percentToRoundedGrade } from "@/services/assignmentService";
|
||||||
|
import type { CompetenceCertificate, CompetenceCertificateAssignment } from "@/types";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
||||||
export function assignmentsMaxEvaluationPoints(
|
export function assignmentsMaxEvaluationPoints(
|
||||||
|
|
@ -16,10 +17,61 @@ export function assignmentsUserPoints(assignments: CompetenceCertificateAssignme
|
||||||
return +_.sum(
|
return +_.sum(
|
||||||
assignments
|
assignments
|
||||||
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
.filter((a) => a.completion?.completion_status === "EVALUATION_SUBMITTED")
|
||||||
.map((a) => a.completion?.evaluation_points ?? 0)
|
.map((a) => a.completion?.evaluation_points_final ?? 0)
|
||||||
).toFixed(1);
|
).toFixed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function calcCompetenceCertificateGrade(
|
||||||
|
assignments: CompetenceCertificateAssignment[],
|
||||||
|
roundedToHalfGrade = true
|
||||||
|
) {
|
||||||
|
const evaluatedAssignments = assignments.filter(
|
||||||
|
(a) => a.completion?.completion_status === "EVALUATION_SUBMITTED"
|
||||||
|
);
|
||||||
|
|
||||||
|
const adjustedResults = evaluatedAssignments.map((a) => {
|
||||||
|
return (
|
||||||
|
((a.completion?.evaluation_points_final ?? 0) / a.max_points) *
|
||||||
|
a.competence_certificate_weight
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const adjustedAssignmentCount = _.sum(
|
||||||
|
evaluatedAssignments.map((a) => a.competence_certificate_weight)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (adjustedAssignmentCount === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return percentToRoundedGrade(
|
||||||
|
_.sum(adjustedResults) / adjustedAssignmentCount,
|
||||||
|
roundedToHalfGrade
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calcCompetencesTotalGrade(
|
||||||
|
competenceCertificates: CompetenceCertificate[]
|
||||||
|
) {
|
||||||
|
// für das Total der Kompetenznote werden jeweils die gerundenten Noten der
|
||||||
|
// einzelnen Kompetenznachweise verwendet und dann noch einmal gerundet.
|
||||||
|
const competenceCertificateGrades = competenceCertificates
|
||||||
|
.map((cc) => {
|
||||||
|
return calcCompetenceCertificateGrade(cc.assignments);
|
||||||
|
})
|
||||||
|
.filter((g) => {
|
||||||
|
// filter out "empty" grades
|
||||||
|
return !!g;
|
||||||
|
});
|
||||||
|
|
||||||
|
const percentGraded =
|
||||||
|
// @ts-ignore `g` cannot be undefined here
|
||||||
|
_.sum(competenceCertificateGrades.map((g) => g - 1)) /
|
||||||
|
(competenceCertificateGrades.length * 5);
|
||||||
|
|
||||||
|
return percentToRoundedGrade(percentGraded);
|
||||||
|
}
|
||||||
|
|
||||||
export function competenceCertificateProgressStatusCount(
|
export function competenceCertificateProgressStatusCount(
|
||||||
assignments: CompetenceCertificateAssignment[]
|
assignments: CompetenceCertificateAssignment[]
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,10 @@ const total = (metrics: AssignmentCompletionMetricsType) => {
|
||||||
:items="courseStatistics.assignments.records"
|
:items="courseStatistics.assignments.records"
|
||||||
>
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<div class="flex justify-between">
|
<div
|
||||||
|
class="flex justify-between"
|
||||||
|
:data-cy=" (item as AssignmentStatisticsRecordType).assignment_title"
|
||||||
|
>
|
||||||
<div>
|
<div>
|
||||||
<h4 class="font-bold">
|
<h4 class="font-bold">
|
||||||
{{ (item as AssignmentStatisticsRecordType).assignment_title }}
|
{{ (item as AssignmentStatisticsRecordType).assignment_title }}
|
||||||
|
|
|
||||||
|
|
@ -36,11 +36,20 @@ const { data, error } = useCSRFFetch(
|
||||||
"a.Die Einladung konnte nicht akzeptiert werden. Bitte melde dich beim Support."
|
"a.Die Einladung konnte nicht akzeptiert werden. Bitte melde dich beim Support."
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
<div>
|
<ul>
|
||||||
|
<li>
|
||||||
|
{{ $t("a.Versicherungsvermittler/-in") }}
|
||||||
<a class="underline" href="mailto:vermittler@vbv-afa.ch">
|
<a class="underline" href="mailto:vermittler@vbv-afa.ch">
|
||||||
vermittler@vbv-afa.ch
|
vermittler@vbv-afa.ch
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ $t("a.Überbetriebliche Kurse") }}
|
||||||
|
<a class="underline" href="mailto:uek-support@vbv-afa.ch">
|
||||||
|
uek-support@vbv-afa.ch
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<div v-if="error.message" class="my-4">
|
<div v-if="error.message" class="my-4">
|
||||||
{{ $t("a.Fehlermeldung") }}: {{ error.message }}
|
{{ $t("a.Fehlermeldung") }}: {{ error.message }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ async function startTest() {
|
||||||
<div class="my-4">
|
<div class="my-4">
|
||||||
{{ $t("a.Resultat") }}:
|
{{ $t("a.Resultat") }}:
|
||||||
<span class="font-bold">
|
<span class="font-bold">
|
||||||
{{ assignmentCompletion.evaluation_points }}
|
{{ assignmentCompletion.evaluation_points_final }}
|
||||||
</span>
|
</span>
|
||||||
{{
|
{{
|
||||||
$t("assignment.von x Punkten", {
|
$t("assignment.von x Punkten", {
|
||||||
|
|
@ -178,7 +178,7 @@ async function startTest() {
|
||||||
}}
|
}}
|
||||||
({{
|
({{
|
||||||
(
|
(
|
||||||
((assignmentCompletion.evaluation_points ?? 0) /
|
((assignmentCompletion.evaluation_points_final ?? 0) /
|
||||||
(assignmentCompletion.evaluation_max_points ?? 1)) *
|
(assignmentCompletion.evaluation_max_points ?? 1)) *
|
||||||
100
|
100
|
||||||
).toFixed(0)
|
).toFixed(0)
|
||||||
|
|
@ -191,6 +191,24 @@ async function startTest() {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="assignmentCompletion.evaluation_points_deducted > 0"
|
||||||
|
class="my-4 text-gray-900"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{{ $t("a.Punkte aus Bewertung") }}:
|
||||||
|
{{ assignmentCompletion.evaluation_points }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ $t("a.Abgezogene Punkte") }}:
|
||||||
|
{{ assignmentCompletion.evaluation_points_deducted }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{ $t("a.Grund") }}:
|
||||||
|
{{ assignmentCompletion.evaluation_points_deducted_reason }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<button class="btn-primary inline-flex items-center" @click="startTest()">
|
<button class="btn-primary inline-flex items-center" @click="startTest()">
|
||||||
{{ $t("edoniqTest.viewResults") }}
|
{{ $t("edoniqTest.viewResults") }}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ const { t } = useTranslation();
|
||||||
<div>
|
<div>
|
||||||
<div class="mx-auto mb-8 mt-0 p-0 lg:mt-8 lg:p-4">
|
<div class="mx-auto mb-8 mt-0 p-0 lg:mt-8 lg:p-4">
|
||||||
<h2 class="mb-4 text-2xl font-semibold">
|
<h2 class="mb-4 text-2xl font-semibold">
|
||||||
So startest du mit diesem Lehrgang:
|
{{ $t("a.So startest du mit diesem Lehrgang") }}:
|
||||||
</h2>
|
</h2>
|
||||||
<ol class="circle-numbered-list">
|
<ol class="circle-numbered-list">
|
||||||
<li>
|
<li>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ const { t } = useTranslation();
|
||||||
<div>
|
<div>
|
||||||
<div class="mx-auto mb-8 mt-0 p-0 lg:mt-8 lg:p-4">
|
<div class="mx-auto mb-8 mt-0 p-0 lg:mt-8 lg:p-4">
|
||||||
<h2 class="mb-4 text-2xl font-semibold">
|
<h2 class="mb-4 text-2xl font-semibold">
|
||||||
So startest du mit diesem Lehrgang:
|
{{ $t("a.So startest du mit diesem Lehrgang") }}:
|
||||||
</h2>
|
</h2>
|
||||||
<ol class="circle-numbered-list">
|
<ol class="circle-numbered-list">
|
||||||
<li>
|
<li>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, it } from "vitest";
|
import { describe, it } from "vitest";
|
||||||
import { pointsToGrade } from "../assignmentService";
|
import { percentToRoundedGrade, pointsToGrade } from "../assignmentService";
|
||||||
|
|
||||||
describe("assignmentService", () => {
|
describe("assignmentService", () => {
|
||||||
it("pointsToGrade", () => {
|
it("pointsToGrade", () => {
|
||||||
|
|
@ -29,4 +29,60 @@ describe("assignmentService", () => {
|
||||||
expect(pointsToGrade(1, 24)).toBe(1);
|
expect(pointsToGrade(1, 24)).toBe(1);
|
||||||
expect(pointsToGrade(0, 24)).toBe(1);
|
expect(pointsToGrade(0, 24)).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("percentToRoundedGrade with half grades", () => {
|
||||||
|
expect(percentToRoundedGrade(24 / 24)).toBe(6);
|
||||||
|
expect(percentToRoundedGrade(23 / 24)).toBe(6);
|
||||||
|
expect(percentToRoundedGrade(22 / 24)).toBe(5.5);
|
||||||
|
expect(percentToRoundedGrade(21 / 24)).toBe(5.5);
|
||||||
|
expect(percentToRoundedGrade(20 / 24)).toBe(5);
|
||||||
|
expect(percentToRoundedGrade(19 / 24)).toBe(5);
|
||||||
|
expect(percentToRoundedGrade(18 / 24)).toBe(5);
|
||||||
|
expect(percentToRoundedGrade(17 / 24)).toBe(4.5);
|
||||||
|
expect(percentToRoundedGrade(16 / 24)).toBe(4.5);
|
||||||
|
expect(percentToRoundedGrade(15 / 24)).toBe(4);
|
||||||
|
expect(percentToRoundedGrade(14 / 24)).toBe(4);
|
||||||
|
expect(percentToRoundedGrade(13 / 24)).toBe(3.5);
|
||||||
|
expect(percentToRoundedGrade(12 / 24)).toBe(3.5);
|
||||||
|
expect(percentToRoundedGrade(11 / 24)).toBe(3.5);
|
||||||
|
expect(percentToRoundedGrade(10 / 24)).toBe(3);
|
||||||
|
expect(percentToRoundedGrade(9 / 24)).toBe(3);
|
||||||
|
expect(percentToRoundedGrade(8 / 24)).toBe(2.5);
|
||||||
|
expect(percentToRoundedGrade(7 / 24)).toBe(2.5);
|
||||||
|
expect(percentToRoundedGrade(6 / 24)).toBe(2.5);
|
||||||
|
expect(percentToRoundedGrade(5 / 24)).toBe(2);
|
||||||
|
expect(percentToRoundedGrade(4 / 24)).toBe(2);
|
||||||
|
expect(percentToRoundedGrade(3 / 24)).toBe(1.5);
|
||||||
|
expect(percentToRoundedGrade(2 / 24)).toBe(1.5);
|
||||||
|
expect(percentToRoundedGrade(1 / 24)).toBe(1);
|
||||||
|
expect(percentToRoundedGrade(0 / 24)).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("percentToRoundedGrade with 2 decimal places", () => {
|
||||||
|
expect(percentToRoundedGrade(24 / 24, false)).toBeCloseTo(6);
|
||||||
|
expect(percentToRoundedGrade(23 / 24, false)).toBeCloseTo(5.79);
|
||||||
|
expect(percentToRoundedGrade(22 / 24, false)).toBeCloseTo(5.58);
|
||||||
|
expect(percentToRoundedGrade(21 / 24, false)).toBeCloseTo(5.38);
|
||||||
|
expect(percentToRoundedGrade(20 / 24, false)).toBeCloseTo(5.17);
|
||||||
|
expect(percentToRoundedGrade(19 / 24, false)).toBeCloseTo(4.96);
|
||||||
|
expect(percentToRoundedGrade(18 / 24, false)).toBeCloseTo(4.75);
|
||||||
|
expect(percentToRoundedGrade(17 / 24, false)).toBeCloseTo(4.54);
|
||||||
|
expect(percentToRoundedGrade(16 / 24, false)).toBeCloseTo(4.33);
|
||||||
|
expect(percentToRoundedGrade(15 / 24, false)).toBeCloseTo(4.13);
|
||||||
|
expect(percentToRoundedGrade(14 / 24, false)).toBeCloseTo(3.92);
|
||||||
|
expect(percentToRoundedGrade(13 / 24, false)).toBeCloseTo(3.71);
|
||||||
|
expect(percentToRoundedGrade(12 / 24, false)).toBeCloseTo(3.5);
|
||||||
|
expect(percentToRoundedGrade(11 / 24, false)).toBeCloseTo(3.29);
|
||||||
|
expect(percentToRoundedGrade(10 / 24, false)).toBeCloseTo(3.08);
|
||||||
|
expect(percentToRoundedGrade(9 / 24, false)).toBeCloseTo(2.88);
|
||||||
|
expect(percentToRoundedGrade(8 / 24, false)).toBeCloseTo(2.67);
|
||||||
|
expect(percentToRoundedGrade(7 / 24, false)).toBeCloseTo(2.46);
|
||||||
|
expect(percentToRoundedGrade(6 / 24, false)).toBeCloseTo(2.25);
|
||||||
|
expect(percentToRoundedGrade(5 / 24, false)).toBeCloseTo(2.04);
|
||||||
|
expect(percentToRoundedGrade(4 / 24, false)).toBeCloseTo(1.83);
|
||||||
|
expect(percentToRoundedGrade(3 / 24, false)).toBeCloseTo(1.63);
|
||||||
|
expect(percentToRoundedGrade(2 / 24, false)).toBeCloseTo(1.42);
|
||||||
|
expect(percentToRoundedGrade(1 / 24, false)).toBeCloseTo(1.21);
|
||||||
|
expect(percentToRoundedGrade(0 / 24, false)).toBeCloseTo(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export async function loadAssignmentCompletionStatusData(
|
||||||
if (userAssignmentStatus?.completion_status === "EVALUATION_SUBMITTED") {
|
if (userAssignmentStatus?.completion_status === "EVALUATION_SUBMITTED") {
|
||||||
gradedUsers.push({
|
gradedUsers.push({
|
||||||
user: csu,
|
user: csu,
|
||||||
points: userAssignmentStatus.evaluation_points ?? 0,
|
points: userAssignmentStatus.evaluation_points_final ?? 0,
|
||||||
maxPoints: userAssignmentStatus.evaluation_max_points ?? 0,
|
maxPoints: userAssignmentStatus.evaluation_max_points ?? 0,
|
||||||
passed: userAssignmentStatus.evaluation_passed ?? false,
|
passed: userAssignmentStatus.evaluation_passed ?? false,
|
||||||
});
|
});
|
||||||
|
|
@ -90,3 +90,15 @@ export function pointsToGrade(points: number, maxPoints: number) {
|
||||||
const halfGrade = grade / 2;
|
const halfGrade = grade / 2;
|
||||||
return Math.min(halfGrade, 5) + 1;
|
return Math.min(halfGrade, 5) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function percentToRoundedGrade(percent: number, roundedToHalfGrade = true) {
|
||||||
|
if (roundedToHalfGrade) {
|
||||||
|
// Round to half-grades
|
||||||
|
const grade = Math.round(percent * 10);
|
||||||
|
const halfGrade = grade / 2;
|
||||||
|
return Math.min(halfGrade, 5) + 1;
|
||||||
|
} else {
|
||||||
|
// Round to 2 decimal places
|
||||||
|
return Math.round((percent * 5 + 1) * 100) / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,7 @@ export type ActionCompetence = Omit<
|
||||||
export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
|
export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
|
||||||
assignment_type: "CASEWORK" | "EDONIQ_TEST";
|
assignment_type: "CASEWORK" | "EDONIQ_TEST";
|
||||||
max_points: number;
|
max_points: number;
|
||||||
|
competence_certificate_weight: number;
|
||||||
learning_content:
|
learning_content:
|
||||||
| (BaseCourseWagtailPage & {
|
| (BaseCourseWagtailPage & {
|
||||||
circle: CircleLight;
|
circle: CircleLight;
|
||||||
|
|
@ -402,6 +403,9 @@ export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
|
||||||
completion_status: AssignmentCompletionStatus;
|
completion_status: AssignmentCompletionStatus;
|
||||||
evaluation_submitted_at: string | null;
|
evaluation_submitted_at: string | null;
|
||||||
evaluation_points: number | null;
|
evaluation_points: number | null;
|
||||||
|
evaluation_points_final: number | null;
|
||||||
|
evaluation_points_deducted: number | null;
|
||||||
|
evaluation_points_reason: string;
|
||||||
evaluation_max_points: number | null;
|
evaluation_max_points: number | null;
|
||||||
evaluation_passed: boolean | null;
|
evaluation_passed: boolean | null;
|
||||||
} | null;
|
} | null;
|
||||||
|
|
@ -566,6 +570,8 @@ export interface UserAssignmentCompletionStatus {
|
||||||
assignment_user_id: string;
|
assignment_user_id: string;
|
||||||
completion_status: AssignmentCompletionStatus;
|
completion_status: AssignmentCompletionStatus;
|
||||||
evaluation_points: number | null;
|
evaluation_points: number | null;
|
||||||
|
evaluation_points_final: number | null;
|
||||||
|
evaluation_points_deducted: number | null;
|
||||||
evaluation_max_points: number | null;
|
evaluation_max_points: number | null;
|
||||||
evaluation_passed: boolean;
|
evaluation_passed: boolean;
|
||||||
learning_content_page_id: string;
|
learning_content_page_id: string;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { login } from "../helpers";
|
||||||
|
|
||||||
|
describe("cockpitPointsDeducted.cy.js", () => {
|
||||||
|
it("will show results with points", () => {
|
||||||
|
cy.manageCommand(
|
||||||
|
"cypress_reset --create-assignment-evaluation --assignment-evaluation-scores 6,4,6,3,3 --create-edoniq-test-results 19 24 0"
|
||||||
|
);
|
||||||
|
login("test-trainer1@example.com", "test");
|
||||||
|
cy.visit("/course/test-lehrgang/cockpit");
|
||||||
|
|
||||||
|
// check edoniq test with deducted points
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="submittable-test-lehrgang-lp-circle-fahrzeug-lc-wissens-und-verständnisfragen"]'
|
||||||
|
).should("contain", "1 von 3 Bewertungen freigegeben");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="show-details-btn-test-lehrgang-lp-circle-fahrzeug-lc-wissens-und-verständnisfragen"]'
|
||||||
|
).click();
|
||||||
|
cy.get('[data-cy="Student1"]')
|
||||||
|
.should("contain", "19 von 24 Punkten")
|
||||||
|
.and("contain", "79%")
|
||||||
|
.and("not.contain", "Nicht bestanden");
|
||||||
|
|
||||||
|
// check casework with deducted points
|
||||||
|
cy.visit("/course/test-lehrgang/cockpit");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="submittable-test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice"]'
|
||||||
|
).should("contain", "1 von 3 Bewertungen freigegeben");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="show-details-btn-test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice"]'
|
||||||
|
).click();
|
||||||
|
cy.get('[data-cy="Student1"]')
|
||||||
|
.should("contain", "22 von 24 Punkten")
|
||||||
|
.and("contain", "92%")
|
||||||
|
.and("not.contain", "Nicht bestanden");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("will show results with deducted points", () => {
|
||||||
|
cy.manageCommand(
|
||||||
|
"cypress_reset --create-assignment-evaluation --assignment-evaluation-scores 6,4,6,3,3 --assignment-points-deducted 14 --create-edoniq-test-results 19 24 8"
|
||||||
|
);
|
||||||
|
login("test-trainer1@example.com", "test");
|
||||||
|
cy.visit("/course/test-lehrgang/cockpit");
|
||||||
|
|
||||||
|
// check edoniq test with deducted points
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="submittable-test-lehrgang-lp-circle-fahrzeug-lc-wissens-und-verständnisfragen"]'
|
||||||
|
).should("contain", "1 von 3 Bewertungen freigegeben");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="show-details-btn-test-lehrgang-lp-circle-fahrzeug-lc-wissens-und-verständnisfragen"]'
|
||||||
|
).click();
|
||||||
|
cy.get('[data-cy="Student1"]')
|
||||||
|
.should("contain", "11 von 24 Punkten")
|
||||||
|
.and("contain", "46%")
|
||||||
|
.and("contain", "Nicht bestanden");
|
||||||
|
|
||||||
|
// check casework with deducted points
|
||||||
|
cy.visit("/course/test-lehrgang/cockpit");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="submittable-test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice"]'
|
||||||
|
).should("contain", "1 von 3 Bewertungen freigegeben");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="show-details-btn-test-lehrgang-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice"]'
|
||||||
|
).click();
|
||||||
|
cy.get('[data-cy="Student1"]')
|
||||||
|
.should("contain", "8 von 24 Punkten")
|
||||||
|
.and("contain", "33%")
|
||||||
|
.and("contain", "Nicht bestanden");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -14,9 +14,7 @@ describe("competenceCertificate.cy.js", () => {
|
||||||
|
|
||||||
cy.get(
|
cy.get(
|
||||||
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
||||||
)
|
).and("contain", "0 von 2 Kompetenznachweis-Elementen");
|
||||||
.should("contain", "0 von 0 Punkten")
|
|
||||||
.and("contain", "0 von 2 Kompetenznachweis-Elementen");
|
|
||||||
|
|
||||||
// check on certificates page
|
// check on certificates page
|
||||||
cy.get('[data-cy="certificates-show-all-button"]').click();
|
cy.get('[data-cy="certificates-show-all-button"]').click();
|
||||||
|
|
@ -48,31 +46,37 @@ describe("competenceCertificate.cy.js", () => {
|
||||||
|
|
||||||
it("check with finished passed edoniq test", () => {
|
it("check with finished passed edoniq test", () => {
|
||||||
cy.manageCommand(
|
cy.manageCommand(
|
||||||
"cypress_reset --create-assignment-completion --create-edoniq-test-results 19 24"
|
"cypress_reset --create-assignment-completion --create-edoniq-test-results 19 24 0"
|
||||||
);
|
);
|
||||||
login("test-student1@example.com", "test");
|
login("test-student1@example.com", "test");
|
||||||
cy.visit("/course/test-lehrgang/competence");
|
cy.visit("/course/test-lehrgang/competence");
|
||||||
|
|
||||||
cy.get('[data-cy="certificate-total-points-text"]').contains(
|
cy.get('[data-cy="certificate-total-points-text"]').contains(
|
||||||
"Zwischenstand Gesamtpunktzahl: 19 von 24 Punkten"
|
"Erfahrungsnote üK: 5"
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.get(
|
cy.get(
|
||||||
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
||||||
)
|
)
|
||||||
.should("contain", "19 von 24 Punkten")
|
.should("contain", "Note: 5")
|
||||||
.and("contain", "1 von 2 Kompetenznachweis-Elementen");
|
.and("contain", "1 von 2 Kompetenznachweis-Elementen");
|
||||||
|
|
||||||
// check on certificates page
|
// check on certificates page
|
||||||
cy.get('[data-cy="certificates-show-all-button"]').click();
|
cy.get('[data-cy="certificates-show-all-button"]').click();
|
||||||
cy.get('[data-cy="certificate-total-points-text"]')
|
cy.get('[data-cy="certificate-total-points-text"]')
|
||||||
.should("contain", "19")
|
.should("contain", "Erfahrungsnote üK")
|
||||||
.and("contain", "Zwischenstand");
|
.and("contain", "Zwischenstand");
|
||||||
|
cy.get('[data-cy="certificate-total-grade"]').should("contain", "Note: 5");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
|
||||||
|
).should("contain", "Note: 5");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
|
||||||
|
).should("contain", "Ungerundete Note: 4.96");
|
||||||
cy.get(
|
cy.get(
|
||||||
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
||||||
)
|
)
|
||||||
.should("contain", "19")
|
|
||||||
.and("contain", "Zwischenstand")
|
.and("contain", "Zwischenstand")
|
||||||
.and("contain", "1 von 2 Kompetenznachweis-Elementen");
|
.and("contain", "1 von 2 Kompetenznachweis-Elementen");
|
||||||
|
|
||||||
|
|
@ -104,7 +108,7 @@ describe("competenceCertificate.cy.js", () => {
|
||||||
|
|
||||||
it("check with finished failed edoniq test", () => {
|
it("check with finished failed edoniq test", () => {
|
||||||
cy.manageCommand(
|
cy.manageCommand(
|
||||||
"cypress_reset --create-assignment-completion --create-edoniq-test-results 10 24"
|
"cypress_reset --create-assignment-completion --create-edoniq-test-results 10 24 0"
|
||||||
);
|
);
|
||||||
login("test-student1@example.com", "test");
|
login("test-student1@example.com", "test");
|
||||||
|
|
||||||
|
|
@ -113,6 +117,13 @@ describe("competenceCertificate.cy.js", () => {
|
||||||
"/course/test-lehrgang/competence/certificates/kompetenznachweis-1"
|
"/course/test-lehrgang/competence/certificates/kompetenznachweis-1"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
|
||||||
|
).should("contain", "Note: 3");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
|
||||||
|
).should("contain", "Ungerundete Note: 3.08");
|
||||||
|
|
||||||
cy.get(
|
cy.get(
|
||||||
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
|
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
|
||||||
)
|
)
|
||||||
|
|
@ -133,33 +144,39 @@ describe("competenceCertificate.cy.js", () => {
|
||||||
|
|
||||||
it("check with finished edoniq test and finished casework", () => {
|
it("check with finished edoniq test and finished casework", () => {
|
||||||
cy.manageCommand(
|
cy.manageCommand(
|
||||||
"cypress_reset --create-assignment-evaluation --create-edoniq-test-results 19 24"
|
"cypress_reset --create-assignment-evaluation --create-edoniq-test-results 19 24 0"
|
||||||
);
|
);
|
||||||
login("test-student1@example.com", "test");
|
login("test-student1@example.com", "test");
|
||||||
cy.visit("/course/test-lehrgang/competence");
|
cy.visit("/course/test-lehrgang/competence");
|
||||||
|
|
||||||
cy.get('[data-cy="certificate-total-points-text"]').contains(
|
cy.get('[data-cy="certificate-total-points-text"]').contains(
|
||||||
"Zwischenstand Gesamtpunktzahl: 43 von 48 Punkten"
|
"Erfahrungsnote üK: 5.5"
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.get(
|
cy.get(
|
||||||
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
||||||
)
|
)
|
||||||
.should("contain", "43 von 48 Punkten")
|
.should("contain", "Note: 5.5")
|
||||||
.and("contain", "2 von 2 Kompetenznachweis-Elementen");
|
.and("contain", "2 von 2 Kompetenznachweis-Elementen");
|
||||||
|
|
||||||
// check on certificates page
|
// check on certificates page
|
||||||
cy.get('[data-cy="certificates-show-all-button"]').click();
|
cy.get('[data-cy="certificates-show-all-button"]').click();
|
||||||
cy.get('[data-cy="certificate-total-points-text"]')
|
cy.get('[data-cy="certificate-total-points-text"]')
|
||||||
.should("contain", "43")
|
.should("contain", "Erfahrungsnote üK")
|
||||||
|
.and("contain", "Note: 5.5")
|
||||||
.and("not.contain", "Zwischenstand");
|
.and("not.contain", "Zwischenstand");
|
||||||
|
|
||||||
cy.get(
|
cy.get(
|
||||||
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
||||||
)
|
)
|
||||||
.should("contain", "43")
|
|
||||||
.and("not.contain", "Zwischenstand")
|
.and("not.contain", "Zwischenstand")
|
||||||
.and("contain", "2 von 2 Kompetenznachweis-Elementen");
|
.and("contain", "2 von 2 Kompetenznachweis-Elementen");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
|
||||||
|
).should("contain", "Note: 5.5");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
|
||||||
|
).should("contain", "Ungerundete Note: 5.48");
|
||||||
|
|
||||||
// check certificate detail page
|
// check certificate detail page
|
||||||
cy.get(
|
cy.get(
|
||||||
|
|
@ -180,6 +197,110 @@ describe("competenceCertificate.cy.js", () => {
|
||||||
.and("contain", "Bewertung freigegeben");
|
.and("contain", "Bewertung freigegeben");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("check with finished edoniq test with deducted points", () => {
|
||||||
|
cy.manageCommand(
|
||||||
|
"cypress_reset --create-assignment-completion --create-edoniq-test-results 19 24 8"
|
||||||
|
);
|
||||||
|
login("test-student1@example.com", "test");
|
||||||
|
|
||||||
|
// go to certificate detail page
|
||||||
|
cy.visit(
|
||||||
|
"/course/test-lehrgang/competence/certificates/kompetenznachweis-1"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
|
||||||
|
).should("contain", "Note: 3.5");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
|
||||||
|
).should("contain", "Ungerundete Note: 3.29");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"]'
|
||||||
|
)
|
||||||
|
.should("contain", "11")
|
||||||
|
.and("contain", "Bewertung freigegeben")
|
||||||
|
.and("contain", "46%")
|
||||||
|
.and("contain", "mit Abzug")
|
||||||
|
.and("contain", "Nicht bestanden");
|
||||||
|
|
||||||
|
// it can open learning content page directly
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="assignment-test-lehrgang-assignment-edoniq-wissens-und-verständisfragen-circle-fahrzeug-demo"] [data-cy="open-learning-content"]'
|
||||||
|
).click();
|
||||||
|
cy.get('[data-cy="test-result"]')
|
||||||
|
.should("contain", "11 von 24 Punkten")
|
||||||
|
.and("contain", "46%")
|
||||||
|
.and("contain", "Punkte aus Bewertung: 19")
|
||||||
|
.and("contain", "Abgezogene Punkte: 8")
|
||||||
|
.and("contain", "Grund: Edoniq Punkteabzug Test")
|
||||||
|
.and("contain", "Nicht bestanden");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("check with finished casework and points deducted", () => {
|
||||||
|
cy.manageCommand(
|
||||||
|
"cypress_reset --create-assignment-evaluation --assignment-evaluation-scores 4,6,4,3,2 --assignment-points-deducted 5"
|
||||||
|
);
|
||||||
|
login("test-student1@example.com", "test");
|
||||||
|
cy.visit("/course/test-lehrgang/competence");
|
||||||
|
|
||||||
|
cy.get('[data-cy="certificate-total-points-text"]').contains(
|
||||||
|
"Erfahrungsnote üK: 4"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
||||||
|
)
|
||||||
|
.should("contain", "Note: 4")
|
||||||
|
.and("contain", "1 von 2 Kompetenznachweis-Elementen");
|
||||||
|
|
||||||
|
// check on certificates page
|
||||||
|
cy.get('[data-cy="certificates-show-all-button"]').click();
|
||||||
|
cy.get('[data-cy="certificate-total-points-text"]')
|
||||||
|
.should("contain", "Erfahrungsnote üK")
|
||||||
|
.and("contain", "Note: 4")
|
||||||
|
.and("contain", "Zwischenstand");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1"]'
|
||||||
|
)
|
||||||
|
.and("contain", "Zwischenstand")
|
||||||
|
.and("contain", "1 von 2 Kompetenznachweis-Elementen");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade"]'
|
||||||
|
).should("contain", "Note: 4");
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-grade-percent"]'
|
||||||
|
).should("contain", "Ungerundete Note: 3.92");
|
||||||
|
|
||||||
|
// check certificate detail page
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="certificate-test-lehrgang-competencenavi-certificates-kompetenznachweis-1-detail-link"]'
|
||||||
|
).click();
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"]'
|
||||||
|
)
|
||||||
|
.should("contain", "14")
|
||||||
|
.and("contain", "von 24 Punkten")
|
||||||
|
.and("contain", "58%")
|
||||||
|
.and("contain", "mit Abzug")
|
||||||
|
.and("contain", "Bewertung freigegeben");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="assignment-test-lehrgang-assignment-überprüfen-einer-motorfahrzeugs-versicherungspolice"] [data-cy="open-learning-content"]'
|
||||||
|
).click();
|
||||||
|
cy.get('[data-cy="user-points"]').should("contain", "14");
|
||||||
|
cy.get('[data-cy="total-points"]').should(
|
||||||
|
"contain",
|
||||||
|
"von 24 Punkten (58%)"
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="points-deducted"]')
|
||||||
|
.should("contain", "Punkte aus Bewertung: 19")
|
||||||
|
.and("contain", "Abgezogene Punkte: 5")
|
||||||
|
.and("contain", "Grund: Assignment Punkteabzug Test");
|
||||||
|
});
|
||||||
|
|
||||||
it("should display link to details", () => {
|
it("should display link to details", () => {
|
||||||
cy.manageCommand("cypress_reset");
|
cy.manageCommand("cypress_reset");
|
||||||
login("test-student1@example.com", "test");
|
login("test-student1@example.com", "test");
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ const clickOnDetailsLink = (within) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("dashboardSupervisor.cy.js", () => {
|
describe("dashboardSupervisor.cy.js", () => {
|
||||||
|
describe("with data", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.manageCommand(
|
cy.manageCommand(
|
||||||
"cypress_reset --create-assignment-evaluation --create-feedback-responses --create-course-completion-performance-criteria --create-attendance-days"
|
"cypress_reset --create-assignment-evaluation --create-feedback-responses --create-course-completion-performance-criteria --create-attendance-days"
|
||||||
|
|
@ -25,7 +26,10 @@ describe("dashboardSupervisor.cy.js", () => {
|
||||||
it("contains correct numbers", () => {
|
it("contains correct numbers", () => {
|
||||||
// we have no completed assignments, but some are in progress
|
// we have no completed assignments, but some are in progress
|
||||||
// -> makes sure that the numbers are correct
|
// -> makes sure that the numbers are correct
|
||||||
getDashboardStatistics("assignments.completed").should("have.text", "1");
|
getDashboardStatistics("assignments.completed").should(
|
||||||
|
"have.text",
|
||||||
|
"1"
|
||||||
|
);
|
||||||
getDashboardStatistics("assignments.passed").should("have.text", "34%");
|
getDashboardStatistics("assignments.passed").should("have.text", "34%");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -98,3 +102,33 @@ describe("dashboardSupervisor.cy.js", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("with deducted points", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.manageCommand(
|
||||||
|
"cypress_reset --create-assignment-evaluation --assignment-evaluation-scores 6,6,6,3,3 --assignment-points-deducted 14 --create-edoniq-test-results 19 24 8"
|
||||||
|
);
|
||||||
|
login("test-supervisor1@example.com", "test");
|
||||||
|
cy.visit("/");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains numbers with deduction", () => {
|
||||||
|
// we have no completed assignments, but some are in progress
|
||||||
|
// -> makes sure that the numbers are correct
|
||||||
|
getDashboardStatistics("assignments.completed").should("have.text", "2");
|
||||||
|
getDashboardStatistics("assignments.passed").should("have.text", "0%");
|
||||||
|
|
||||||
|
// check data on the details page
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="dashboard.stats.assignments"] [data-cy="basebox.detailsLink"]'
|
||||||
|
).click();
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="Edoniq Wissens- und Verständisfragen - Circle Fahrzeug (Demo)"]'
|
||||||
|
).should("contain", "0 von 3 bestanden");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="Überprüfen einer Motorfahrzeugs-Versicherungspolice"]'
|
||||||
|
).should("contain", "0 von 3 bestanden");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ For more information on this file, see
|
||||||
https://docs.djangoproject.com/en/dev/howto/deployment/asgi/
|
https://docs.djangoproject.com/en/dev/howto/deployment/asgi/
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
"""
|
"""
|
||||||
Base settings to build other settings files upon.
|
Base settings to build other settings files upon.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
@ -398,9 +399,9 @@ if IT_DJANGO_LOGGING_CONF == "IT_DJANGO_LOGGING_CONF_CONSOLE_COLOR":
|
||||||
"propagate": False,
|
"propagate": False,
|
||||||
},
|
},
|
||||||
"django.server": {
|
"django.server": {
|
||||||
"handlers": ["null"]
|
"handlers": (
|
||||||
if IT_LOCAL_HIDE_DJANGO_SERVER_LOGS
|
["null"] if IT_LOCAL_HIDE_DJANGO_SERVER_LOGS else ["default"]
|
||||||
else ["default"],
|
),
|
||||||
"level": "INFO",
|
"level": "INFO",
|
||||||
"propagate": False,
|
"propagate": False,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ middleware here, or combine a Django application with an application of another
|
||||||
framework.
|
framework.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,43 @@ class AssignmentCompletionAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = "created_at"
|
date_hierarchy = "created_at"
|
||||||
list_display = [
|
list_display = [
|
||||||
"id",
|
"id",
|
||||||
"completion_status",
|
|
||||||
"assignment",
|
"assignment",
|
||||||
|
"get_circle",
|
||||||
"assignment_user",
|
"assignment_user",
|
||||||
"course_session",
|
"course_session",
|
||||||
|
"completion_status",
|
||||||
|
"evaluation_points",
|
||||||
|
"evaluation_points_deducted",
|
||||||
]
|
]
|
||||||
|
list_filter = [
|
||||||
|
"completion_status",
|
||||||
|
"assignment__assignment_type",
|
||||||
|
"course_session__course",
|
||||||
|
"course_session",
|
||||||
|
]
|
||||||
|
search_fields = ["assignment_user__email"]
|
||||||
|
readonly_fields = [
|
||||||
|
"assignment_user",
|
||||||
|
"assignment",
|
||||||
|
"completion_data",
|
||||||
|
"course_session",
|
||||||
|
"learning_content_page",
|
||||||
|
"evaluation_points",
|
||||||
|
"submitted_at",
|
||||||
|
"evaluation_user",
|
||||||
|
"evaluation_submitted_at",
|
||||||
|
"evaluation_points_deducted_user",
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_circle(self, obj):
|
||||||
|
try:
|
||||||
|
return obj.learning_content_page.specific.get_circle().title
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
get_circle.short_description = "Circle"
|
||||||
|
|
||||||
|
def save_model(self, request, obj, form, change):
|
||||||
|
if change and "evaluation_points_deducted" in form.changed_data:
|
||||||
|
obj.evaluation_points_deducted_user = request.user
|
||||||
|
super().save_model(request, obj, form, change)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ class AssignmentCompletionObjectType(DjangoObjectType):
|
||||||
|
|
||||||
# rounded to sensible representation
|
# rounded to sensible representation
|
||||||
evaluation_points = graphene.Float()
|
evaluation_points = graphene.Float()
|
||||||
|
evaluation_points_final = graphene.Float()
|
||||||
evaluation_max_points = graphene.Float()
|
evaluation_max_points = graphene.Float()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -38,6 +39,9 @@ class AssignmentCompletionObjectType(DjangoObjectType):
|
||||||
"evaluation_user",
|
"evaluation_user",
|
||||||
"additional_json_data",
|
"additional_json_data",
|
||||||
"edoniq_extended_time_flag",
|
"edoniq_extended_time_flag",
|
||||||
|
"evaluation_points_deducted",
|
||||||
|
"evaluation_points_deducted_reason",
|
||||||
|
"evaluation_points_deducted_user",
|
||||||
"evaluation_passed",
|
"evaluation_passed",
|
||||||
"task_completion_data",
|
"task_completion_data",
|
||||||
)
|
)
|
||||||
|
|
@ -47,6 +51,11 @@ class AssignmentCompletionObjectType(DjangoObjectType):
|
||||||
return round(self.evaluation_points, 1) # noqa
|
return round(self.evaluation_points, 1) # noqa
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def resolve_evaluation_points_final(self, info):
|
||||||
|
if self.evaluation_points:
|
||||||
|
return round(self.evaluation_points_final, 1) # noqa
|
||||||
|
return None
|
||||||
|
|
||||||
def resolve_evaluation_max_points(self, info):
|
def resolve_evaluation_max_points(self, info):
|
||||||
if self.evaluation_max_points:
|
if self.evaluation_max_points:
|
||||||
return round(self.evaluation_max_points, 1) # noqa
|
return round(self.evaluation_max_points, 1) # noqa
|
||||||
|
|
@ -58,6 +67,7 @@ class AssignmentObjectType(DjangoObjectType):
|
||||||
evaluation_tasks = JSONStreamField()
|
evaluation_tasks = JSONStreamField()
|
||||||
performance_objectives = JSONStreamField()
|
performance_objectives = JSONStreamField()
|
||||||
max_points = graphene.Int()
|
max_points = graphene.Int()
|
||||||
|
competence_certificate_weight = graphene.Float()
|
||||||
learning_content = graphene.Field(LearningContentInterface)
|
learning_content = graphene.Field(LearningContentInterface)
|
||||||
completion = graphene.Field(
|
completion = graphene.Field(
|
||||||
AssignmentCompletionObjectType,
|
AssignmentCompletionObjectType,
|
||||||
|
|
@ -87,6 +97,9 @@ class AssignmentObjectType(DjangoObjectType):
|
||||||
def resolve_max_points(self, info):
|
def resolve_max_points(self, info):
|
||||||
return self.get_max_points()
|
return self.get_max_points()
|
||||||
|
|
||||||
|
def resolve_competence_certificate_weight(self, info):
|
||||||
|
return self.competence_certificate_weight
|
||||||
|
|
||||||
def resolve_learning_content(self, info):
|
def resolve_learning_content(self, info):
|
||||||
return self.find_attached_learning_content()
|
return self.find_attached_learning_content()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.2.20 on 2024-05-03 14:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("assignment", "0012_auto_20240124_1004"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assignment",
|
||||||
|
name="competence_certificate_weight",
|
||||||
|
field=models.FloatField(
|
||||||
|
default=1.0, help_text="Gewichtung für den Kompetenznachweis"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Generated by Django 3.2.20 on 2024-05-21 14:52
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("assignment", "0013_assignment_competence_certificate_weight"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assignmentcompletion",
|
||||||
|
name="evaluation_points_deducted",
|
||||||
|
field=models.FloatField(default=0.0, verbose_name="Punkteabzug"),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assignmentcompletion",
|
||||||
|
name="evaluation_points_deducted_reason",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True, default="", verbose_name="Punkteabzug Begründung"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="assignmentcompletion",
|
||||||
|
name="evaluation_points_deducted_user",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
related_name="+",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assignmentcompletion",
|
||||||
|
name="completion_data",
|
||||||
|
field=models.JSONField(blank=True, default=dict),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -153,6 +153,10 @@ class Assignment(CourseBasePage):
|
||||||
blank=True,
|
blank=True,
|
||||||
on_delete=models.SET_NULL,
|
on_delete=models.SET_NULL,
|
||||||
)
|
)
|
||||||
|
competence_certificate_weight = models.FloatField(
|
||||||
|
default=1.0,
|
||||||
|
help_text="Gewichtung für den Kompetenznachweis",
|
||||||
|
)
|
||||||
|
|
||||||
intro_text = RichTextField(
|
intro_text = RichTextField(
|
||||||
help_text="Erläuterung der Ausgangslage",
|
help_text="Erläuterung der Ausgangslage",
|
||||||
|
|
@ -336,10 +340,34 @@ class AssignmentCompletion(models.Model):
|
||||||
related_name="+",
|
related_name="+",
|
||||||
)
|
)
|
||||||
evaluation_points = models.FloatField(null=True, blank=True)
|
evaluation_points = models.FloatField(null=True, blank=True)
|
||||||
|
evaluation_points_deducted = models.FloatField(
|
||||||
|
default=0.0, verbose_name="Punkteabzug"
|
||||||
|
)
|
||||||
|
evaluation_points_deducted_reason = models.TextField(
|
||||||
|
default="", blank=True, verbose_name="Punkteabzug Begründung"
|
||||||
|
)
|
||||||
|
evaluation_points_deducted_user = models.ForeignKey(
|
||||||
|
User,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
related_name="+",
|
||||||
|
)
|
||||||
|
|
||||||
evaluation_max_points = models.FloatField(null=True, blank=True)
|
evaluation_max_points = models.FloatField(null=True, blank=True)
|
||||||
evaluation_passed = models.BooleanField(null=True, blank=True)
|
evaluation_passed = models.BooleanField(null=True, blank=True)
|
||||||
edoniq_extended_time_flag = models.BooleanField(default=False)
|
edoniq_extended_time_flag = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def evaluation_points_final(self):
|
||||||
|
"""
|
||||||
|
Das ist das relevante Feld für die Punkteberechnung, es berücksichtigt
|
||||||
|
den Punkteabzug aus `evaluation_points_deducted`
|
||||||
|
"""
|
||||||
|
if self.evaluation_points is None:
|
||||||
|
return None
|
||||||
|
return self.evaluation_points - self.evaluation_points_deducted
|
||||||
|
|
||||||
assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
|
assignment_user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
|
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
|
||||||
course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)
|
course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)
|
||||||
|
|
@ -360,7 +388,7 @@ class AssignmentCompletion(models.Model):
|
||||||
default=AssignmentCompletionStatus.IN_PROGRESS.value,
|
default=AssignmentCompletionStatus.IN_PROGRESS.value,
|
||||||
)
|
)
|
||||||
|
|
||||||
completion_data = models.JSONField(default=dict)
|
completion_data = models.JSONField(default=dict, blank=True)
|
||||||
additional_json_data = models.JSONField(default=dict, blank=True)
|
additional_json_data = models.JSONField(default=dict, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
@ -387,6 +415,14 @@ class AssignmentCompletion(models.Model):
|
||||||
data[task.id] = get_task_data(task, self.completion_data)
|
data[task.id] = get_task_data(task, self.completion_data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def save(
|
||||||
|
self,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
if self.evaluation_points_deducted > 0:
|
||||||
|
recalculate_assignment_passed(self)
|
||||||
|
super().save(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def get_file_info(file_id):
|
def get_file_info(file_id):
|
||||||
file_info = UploadFile.objects.filter(id=file_id).first()
|
file_info = UploadFile.objects.filter(id=file_id).first()
|
||||||
|
|
@ -444,3 +480,13 @@ class AssignmentCompletionAuditLog(models.Model):
|
||||||
evaluation_points = models.FloatField(null=True, blank=True)
|
evaluation_points = models.FloatField(null=True, blank=True)
|
||||||
evaluation_max_points = models.FloatField(null=True, blank=True)
|
evaluation_max_points = models.FloatField(null=True, blank=True)
|
||||||
evaluation_passed = models.BooleanField(null=True, blank=True)
|
evaluation_passed = models.BooleanField(null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
def recalculate_assignment_passed(ac: AssignmentCompletion):
|
||||||
|
if ac.evaluation_points_final is not None and ac.evaluation_max_points is not None:
|
||||||
|
# if more or equal than 55% of the points are reached, the assignment is passed
|
||||||
|
ac.evaluation_passed = (
|
||||||
|
ac.evaluation_points_final / ac.evaluation_max_points
|
||||||
|
) >= 0.55
|
||||||
|
|
||||||
|
return ac
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ class AssignmentCompletionSerializer(serializers.ModelSerializer):
|
||||||
"evaluation_user",
|
"evaluation_user",
|
||||||
"additional_json_data",
|
"additional_json_data",
|
||||||
"evaluation_points",
|
"evaluation_points",
|
||||||
|
"evaluation_points_deducted",
|
||||||
|
"evaluation_points_deducted_reason",
|
||||||
"evaluation_max_points",
|
"evaluation_max_points",
|
||||||
"evaluation_passed",
|
"evaluation_passed",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from vbv_lernwelt.assignment.models import (
|
||||||
AssignmentCompletionStatus,
|
AssignmentCompletionStatus,
|
||||||
AssignmentType,
|
AssignmentType,
|
||||||
is_valid_assignment_completion_status,
|
is_valid_assignment_completion_status,
|
||||||
|
recalculate_assignment_passed,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
from vbv_lernwelt.core.utils import find_first
|
from vbv_lernwelt.core.utils import find_first
|
||||||
|
|
@ -178,11 +179,7 @@ def update_assignment_completion(
|
||||||
|
|
||||||
# if no evaluation_passed is provided, we calculate it from the points
|
# if no evaluation_passed is provided, we calculate it from the points
|
||||||
if evaluation_passed is None and ac.evaluation_max_points > 0:
|
if evaluation_passed is None and ac.evaluation_max_points > 0:
|
||||||
if evaluation_points is not None and ac.evaluation_max_points is not None:
|
recalculate_assignment_passed(ac)
|
||||||
# if more or equal than 60% of the points are reached, the assignment is passed
|
|
||||||
ac.evaluation_passed = (
|
|
||||||
evaluation_points / ac.evaluation_max_points
|
|
||||||
) >= 0.55
|
|
||||||
else:
|
else:
|
||||||
ac.evaluation_passed = evaluation_passed
|
ac.evaluation_passed = evaluation_passed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ def request_assignment_completion_status(request, assignment_id, course_session_
|
||||||
"assignment_user_id",
|
"assignment_user_id",
|
||||||
"completion_status",
|
"completion_status",
|
||||||
"evaluation_points",
|
"evaluation_points",
|
||||||
|
"evaluation_points_deducted",
|
||||||
"evaluation_max_points",
|
"evaluation_max_points",
|
||||||
"evaluation_passed",
|
"evaluation_passed",
|
||||||
"learning_content_page_id",
|
"learning_content_page_id",
|
||||||
|
|
@ -29,6 +30,13 @@ def request_assignment_completion_status(request, assignment_id, course_session_
|
||||||
# Convert the learning_content_page_id to a string
|
# Convert the learning_content_page_id to a string
|
||||||
data = list(qs) # Evaluate the queryset
|
data = list(qs) # Evaluate the queryset
|
||||||
for item in data:
|
for item in data:
|
||||||
|
if item["evaluation_points"] is not None:
|
||||||
|
# only `evaluation_points_final` is relevant for the frontend
|
||||||
|
item["evaluation_points_final"] = (
|
||||||
|
item["evaluation_points"] - item["evaluation_points_deducted"]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
item["evaluation_points_final"] = None
|
||||||
item["learning_content_page_id"] = str(item["learning_content_page_id"])
|
item["learning_content_page_id"] = str(item["learning_content_page_id"])
|
||||||
|
|
||||||
return Response(status=200, data=data)
|
return Response(status=200, data=data)
|
||||||
|
|
|
||||||
|
|
@ -66,11 +66,16 @@ from vbv_lernwelt.self_evaluation_feedback.models import (
|
||||||
default=None,
|
default=None,
|
||||||
help="Provide assignment evaluation scores in the format: 6,6,6,3,3",
|
help="Provide assignment evaluation scores in the format: 6,6,6,3,3",
|
||||||
)
|
)
|
||||||
|
@click.option(
|
||||||
|
"--assignment-points-deducted",
|
||||||
|
default=0,
|
||||||
|
help="Provide assignment points deducted",
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"--create-edoniq-test-results",
|
"--create-edoniq-test-results",
|
||||||
type=(int, int),
|
type=(int, int, float),
|
||||||
default=(None, None),
|
default=(None, None, 0.0),
|
||||||
metavar="USER_POINTS MAX_POINTS",
|
metavar="USER_POINTS MAX_POINTS POINTS_DEDUCTED",
|
||||||
help="Create edoniq result data for test-student1@example.com with user points and max points",
|
help="Create edoniq result data for test-student1@example.com with user points and max points",
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
|
|
@ -112,6 +117,7 @@ def command(
|
||||||
create_assignment_completion,
|
create_assignment_completion,
|
||||||
create_assignment_evaluation,
|
create_assignment_evaluation,
|
||||||
assignment_evaluation_scores,
|
assignment_evaluation_scores,
|
||||||
|
assignment_points_deducted,
|
||||||
create_edoniq_test_results,
|
create_edoniq_test_results,
|
||||||
create_feedback_responses,
|
create_feedback_responses,
|
||||||
create_course_completion_performance_criteria,
|
create_course_completion_performance_criteria,
|
||||||
|
|
@ -171,9 +177,10 @@ def command(
|
||||||
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
|
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
|
||||||
evaluation_user=User.objects.get(id=TEST_TRAINER1_USER_ID),
|
evaluation_user=User.objects.get(id=TEST_TRAINER1_USER_ID),
|
||||||
input_scores=assignment_evaluation_scores,
|
input_scores=assignment_evaluation_scores,
|
||||||
|
points_deducted=assignment_points_deducted,
|
||||||
)
|
)
|
||||||
|
|
||||||
user_points, max_points = create_edoniq_test_results
|
user_points, max_points, points_deducted = create_edoniq_test_results
|
||||||
if user_points is not None and max_points is not None:
|
if user_points is not None and max_points is not None:
|
||||||
print(
|
print(
|
||||||
f"Create edoniq test results: User Points: {user_points}, Max Points: {max_points}"
|
f"Create edoniq test results: User Points: {user_points}, Max Points: {max_points}"
|
||||||
|
|
@ -186,6 +193,7 @@ def command(
|
||||||
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
|
assignment_user=User.objects.get(id=TEST_STUDENT1_USER_ID),
|
||||||
user_points=user_points,
|
user_points=user_points,
|
||||||
max_points=max_points,
|
max_points=max_points,
|
||||||
|
evaluation_points_deducted=points_deducted,
|
||||||
)
|
)
|
||||||
|
|
||||||
if create_feedback_responses:
|
if create_feedback_responses:
|
||||||
|
|
|
||||||
|
|
@ -58,3 +58,10 @@ def pretty_print_json(json_string):
|
||||||
# pylint: disable=broad-except
|
# pylint: disable=broad-except
|
||||||
except Exception:
|
except Exception:
|
||||||
return json_string
|
return json_string
|
||||||
|
|
||||||
|
|
||||||
|
def safe_deque_popleft(deq, default=None):
|
||||||
|
try:
|
||||||
|
return deq.popleft()
|
||||||
|
except IndexError:
|
||||||
|
return default
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,9 @@ def cypress_reset_view(request):
|
||||||
assignment_evaluation_scores = request.data.get("assignment_evaluation_scores")
|
assignment_evaluation_scores = request.data.get("assignment_evaluation_scores")
|
||||||
if assignment_evaluation_scores:
|
if assignment_evaluation_scores:
|
||||||
options["assignment_evaluation_scores"] = assignment_evaluation_scores
|
options["assignment_evaluation_scores"] = assignment_evaluation_scores
|
||||||
|
options["assignment_points_deducted"] = float(
|
||||||
|
request.data.get("assignment_points_deducted") or 0
|
||||||
|
)
|
||||||
|
|
||||||
options["create_feedback_responses"] = (
|
options["create_feedback_responses"] = (
|
||||||
request.data.get("create_feedback_responses") == "true"
|
request.data.get("create_feedback_responses") == "true"
|
||||||
|
|
@ -159,10 +162,12 @@ def cypress_reset_view(request):
|
||||||
# edoniq test results
|
# edoniq test results
|
||||||
edoniq_test_user_points = request.data.get("edoniq_test_user_points")
|
edoniq_test_user_points = request.data.get("edoniq_test_user_points")
|
||||||
edoniq_test_max_points = request.data.get("edoniq_test_max_points")
|
edoniq_test_max_points = request.data.get("edoniq_test_max_points")
|
||||||
|
edoniq_points_deducted = request.data.get("edoniq_test_points_deducted") or 0
|
||||||
if bool(edoniq_test_user_points and edoniq_test_max_points):
|
if bool(edoniq_test_user_points and edoniq_test_max_points):
|
||||||
options["create_edoniq_test_results"] = (
|
options["create_edoniq_test_results"] = (
|
||||||
int(edoniq_test_user_points),
|
int(edoniq_test_user_points),
|
||||||
int(edoniq_test_max_points),
|
int(edoniq_test_max_points),
|
||||||
|
float(edoniq_points_deducted),
|
||||||
)
|
)
|
||||||
|
|
||||||
options["create_course_completion_performance_criteria"] = (
|
options["create_course_completion_performance_criteria"] = (
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
from collections import deque
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from dateutil.relativedelta import MO, relativedelta, TH, TU, WE
|
from dateutil.relativedelta import MO, relativedelta, TH, TU, WE
|
||||||
|
|
@ -42,6 +43,7 @@ from vbv_lernwelt.core.constants import (
|
||||||
TEST_TRAINER1_USER_ID,
|
TEST_TRAINER1_USER_ID,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.core.utils import safe_deque_popleft
|
||||||
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
||||||
from vbv_lernwelt.course.factories import CoursePageFactory
|
from vbv_lernwelt.course.factories import CoursePageFactory
|
||||||
from vbv_lernwelt.course.models import (
|
from vbv_lernwelt.course.models import (
|
||||||
|
|
@ -350,20 +352,27 @@ def create_test_assignment_submitted_data(assignment, course_session, user):
|
||||||
|
|
||||||
|
|
||||||
def create_test_assignment_evaluation_data(
|
def create_test_assignment_evaluation_data(
|
||||||
assignment, course_session, assignment_user, evaluation_user, input_scores=None
|
assignment,
|
||||||
|
course_session,
|
||||||
|
assignment_user,
|
||||||
|
evaluation_user,
|
||||||
|
input_scores=None,
|
||||||
|
points_deducted=0,
|
||||||
):
|
):
|
||||||
if assignment and course_session and assignment_user and evaluation_user:
|
if assignment and course_session and assignment_user and evaluation_user:
|
||||||
subtasks = assignment.get_evaluation_tasks()
|
subtasks = assignment.get_evaluation_tasks()
|
||||||
evaluation_points = 0
|
evaluation_points = 0
|
||||||
|
|
||||||
|
input_scores_deque = deque(input_scores) if input_scores else deque()
|
||||||
|
|
||||||
for index, evaluation_task in enumerate(subtasks):
|
for index, evaluation_task in enumerate(subtasks):
|
||||||
task_score = evaluation_task["value"]["max_points"]
|
task_score = evaluation_task["value"]["max_points"]
|
||||||
if input_scores[index] < len(input_scores):
|
input_score = safe_deque_popleft(input_scores_deque)
|
||||||
|
if input_score is not None:
|
||||||
task_score = input_scores[index]
|
task_score = input_scores[index]
|
||||||
|
|
||||||
evaluation_points += task_score
|
evaluation_points += task_score
|
||||||
|
|
||||||
update_assignment_completion(
|
ac, _ = update_assignment_completion(
|
||||||
assignment_user=assignment_user,
|
assignment_user=assignment_user,
|
||||||
assignment=assignment,
|
assignment=assignment,
|
||||||
course_session=course_session,
|
course_session=course_session,
|
||||||
|
|
@ -380,7 +389,7 @@ def create_test_assignment_evaluation_data(
|
||||||
evaluation_user=evaluation_user,
|
evaluation_user=evaluation_user,
|
||||||
)
|
)
|
||||||
|
|
||||||
update_assignment_completion(
|
ac, _ = update_assignment_completion(
|
||||||
assignment_user=assignment_user,
|
assignment_user=assignment_user,
|
||||||
assignment=assignment,
|
assignment=assignment,
|
||||||
course_session=course_session,
|
course_session=course_session,
|
||||||
|
|
@ -391,12 +400,27 @@ def create_test_assignment_evaluation_data(
|
||||||
evaluation_points=evaluation_points,
|
evaluation_points=evaluation_points,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# take the last input score as deduction if there is one left...
|
||||||
|
if points_deducted > 0:
|
||||||
|
ac.evaluation_points_deducted = points_deducted
|
||||||
|
ac.evaluation_points_deducted_reason = "Assignment Punkteabzug Test"
|
||||||
|
ac.save()
|
||||||
|
|
||||||
|
|
||||||
def create_edoniq_test_result_data(
|
def create_edoniq_test_result_data(
|
||||||
assignment, course_session, assignment_user, user_points=19, max_points=24
|
assignment,
|
||||||
|
course_session,
|
||||||
|
assignment_user,
|
||||||
|
user_points=19,
|
||||||
|
max_points=24,
|
||||||
|
evaluation_points_deducted=0,
|
||||||
):
|
):
|
||||||
|
assignment.assignment.evaluation_tasks.raw_data[0]["value"][
|
||||||
|
"max_points"
|
||||||
|
] = max_points
|
||||||
|
assignment.assignment.save()
|
||||||
if assignment and course_session and assignment_user:
|
if assignment and course_session and assignment_user:
|
||||||
update_assignment_completion(
|
ac, _ = update_assignment_completion(
|
||||||
assignment_user=assignment_user,
|
assignment_user=assignment_user,
|
||||||
assignment=assignment,
|
assignment=assignment,
|
||||||
course_session=course_session,
|
course_session=course_session,
|
||||||
|
|
@ -408,6 +432,11 @@ def create_edoniq_test_result_data(
|
||||||
evaluation_max_points=max_points,
|
evaluation_max_points=max_points,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if evaluation_points_deducted > 0:
|
||||||
|
ac.evaluation_points_deducted = evaluation_points_deducted
|
||||||
|
ac.evaluation_points_deducted_reason = "Edoniq Punkteabzug Test"
|
||||||
|
ac.save()
|
||||||
|
|
||||||
|
|
||||||
def create_feedback_response_data(
|
def create_feedback_response_data(
|
||||||
course_session,
|
course_session,
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ class DashboardQuery(graphene.ObjectType):
|
||||||
course_session_ids.add(course_session_id)
|
course_session_ids.add(course_session_id)
|
||||||
|
|
||||||
return CourseStatisticsType(
|
return CourseStatisticsType(
|
||||||
_id=course.id, # noqa
|
_id=f"mentor:{course.id}", # noqa
|
||||||
course_id=course.id, # noqa
|
course_id=course.id, # noqa
|
||||||
course_title=course.title, # noqa
|
course_title=course.title, # noqa
|
||||||
course_slug=course.slug, # noqa
|
course_slug=course.slug, # noqa
|
||||||
|
|
@ -188,14 +188,22 @@ class DashboardQuery(graphene.ObjectType):
|
||||||
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
|
completion_status=AssignmentCompletionStatus.EVALUATION_SUBMITTED.value,
|
||||||
assignment_user=user,
|
assignment_user=user,
|
||||||
course_session__course=course,
|
course_session__course=course,
|
||||||
).values("evaluation_max_points", "evaluation_points")
|
).values(
|
||||||
|
"evaluation_max_points", "evaluation_points", "evaluation_points_deducted"
|
||||||
|
)
|
||||||
|
|
||||||
evaluation_results = list(evaluation_results)
|
evaluation_results = list(evaluation_results)
|
||||||
points_max_count = sum(
|
points_max_count = sum(
|
||||||
[result.get("evaluation_max_points", 0) for result in evaluation_results]
|
[result.get("evaluation_max_points", 0) for result in evaluation_results]
|
||||||
)
|
)
|
||||||
points_achieved_count = sum(
|
points_achieved_count = sum(
|
||||||
[result.get("evaluation_points", 0) for result in evaluation_results]
|
[
|
||||||
|
(
|
||||||
|
result.get("evaluation_points", 0)
|
||||||
|
- result.get("evaluation_points_deducted", 0)
|
||||||
|
)
|
||||||
|
for result in evaluation_results
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
return CourseProgressType(
|
return CourseProgressType(
|
||||||
|
|
|
||||||
|
|
@ -58,12 +58,17 @@ class AssignmentsStatisticsType(graphene.ObjectType):
|
||||||
summary = graphene.Field(AssignmentStatisticsSummaryType, required=True)
|
summary = graphene.Field(AssignmentStatisticsSummaryType, required=True)
|
||||||
|
|
||||||
|
|
||||||
def create_assignment_summary(course_id, metrics) -> AssignmentStatisticsSummaryType:
|
def create_assignment_summary(
|
||||||
|
course_id, metrics, urql_id: str = None
|
||||||
|
) -> AssignmentStatisticsSummaryType:
|
||||||
|
if urql_id is None:
|
||||||
|
urql_id = str(course_id)
|
||||||
|
|
||||||
completed_metrics = [m for m in metrics if m.ranking_completed]
|
completed_metrics = [m for m in metrics if m.ranking_completed]
|
||||||
|
|
||||||
if not completed_metrics:
|
if not completed_metrics:
|
||||||
return AssignmentStatisticsSummaryType(
|
return AssignmentStatisticsSummaryType(
|
||||||
_id=course_id,
|
_id=urql_id,
|
||||||
completed_count=0,
|
completed_count=0,
|
||||||
average_passed=0,
|
average_passed=0,
|
||||||
total_passed=0,
|
total_passed=0,
|
||||||
|
|
@ -80,7 +85,7 @@ def create_assignment_summary(course_id, metrics) -> AssignmentStatisticsSummary
|
||||||
total_failed = sum([m.failed_count for m in completed_metrics])
|
total_failed = sum([m.failed_count for m in completed_metrics])
|
||||||
|
|
||||||
return AssignmentStatisticsSummaryType(
|
return AssignmentStatisticsSummaryType(
|
||||||
_id=course_id, # noqa
|
_id=urql_id, # noqa
|
||||||
completed_count=completed_count, # noqa
|
completed_count=completed_count, # noqa
|
||||||
average_passed=average_passed_completed, # noqa
|
average_passed=average_passed_completed, # noqa
|
||||||
total_passed=total_passed, # noqa
|
total_passed=total_passed, # noqa
|
||||||
|
|
@ -92,6 +97,7 @@ def get_assignment_completion_metrics(
|
||||||
course_session: CourseSession,
|
course_session: CourseSession,
|
||||||
assignment: vbv_lernwelt.assignment.models.Assignment,
|
assignment: vbv_lernwelt.assignment.models.Assignment,
|
||||||
user_selection_ids: List[str] | None,
|
user_selection_ids: List[str] | None,
|
||||||
|
urql_id_postfix: str = "",
|
||||||
) -> AssignmentCompletionMetricsType:
|
) -> AssignmentCompletionMetricsType:
|
||||||
if user_selection_ids:
|
if user_selection_ids:
|
||||||
course_session_users = user_selection_ids
|
course_session_users = user_selection_ids
|
||||||
|
|
@ -120,7 +126,7 @@ def get_assignment_completion_metrics(
|
||||||
average_passed = math.ceil(passed_count / participants_count * 100)
|
average_passed = math.ceil(passed_count / participants_count * 100)
|
||||||
|
|
||||||
return AssignmentCompletionMetricsType(
|
return AssignmentCompletionMetricsType(
|
||||||
_id=f"{course_session.id}-{assignment.id}", # noqa
|
_id=f"{course_session.id}-{assignment.id}@{urql_id_postfix}", # noqa
|
||||||
passed_count=passed_count, # noqa
|
passed_count=passed_count, # noqa
|
||||||
failed_count=failed_count, # noqa
|
failed_count=failed_count, # noqa
|
||||||
unranked_count=unranked_count, # noqa
|
unranked_count=unranked_count, # noqa
|
||||||
|
|
@ -132,6 +138,7 @@ def get_assignment_completion_metrics(
|
||||||
def create_record(
|
def create_record(
|
||||||
course_session_assignment: CourseSessionAssignment | CourseSessionEdoniqTest,
|
course_session_assignment: CourseSessionAssignment | CourseSessionEdoniqTest,
|
||||||
user_selection_ids: List[str] | None,
|
user_selection_ids: List[str] | None,
|
||||||
|
urql_id_postfix: str = "",
|
||||||
) -> AssignmentStatisticsRecordType:
|
) -> AssignmentStatisticsRecordType:
|
||||||
if isinstance(course_session_assignment, CourseSessionAssignment):
|
if isinstance(course_session_assignment, CourseSessionAssignment):
|
||||||
due_date = course_session_assignment.submission_deadline
|
due_date = course_session_assignment.submission_deadline
|
||||||
|
|
@ -142,7 +149,7 @@ def create_record(
|
||||||
|
|
||||||
return AssignmentStatisticsRecordType(
|
return AssignmentStatisticsRecordType(
|
||||||
# make sure it's unique, across all types of assignments!
|
# make sure it's unique, across all types of assignments!
|
||||||
_id=f"{course_session_assignment._meta.model_name}#{course_session_assignment.id}",
|
_id=f"{course_session_assignment._meta.model_name}#{course_session_assignment.id}@{urql_id_postfix}",
|
||||||
# noqa
|
# noqa
|
||||||
course_session_id=str(course_session_assignment.course_session.id), # noqa
|
course_session_id=str(course_session_assignment.course_session.id), # noqa
|
||||||
circle_id=learning_content.get_circle().id, # noqa
|
circle_id=learning_content.get_circle().id, # noqa
|
||||||
|
|
@ -155,6 +162,7 @@ def create_record(
|
||||||
course_session=course_session_assignment.course_session, # noqa
|
course_session=course_session_assignment.course_session, # noqa
|
||||||
assignment=learning_content.content_assignment, # noqa
|
assignment=learning_content.content_assignment, # noqa
|
||||||
user_selection_ids=user_selection_ids, # noqa
|
user_selection_ids=user_selection_ids, # noqa
|
||||||
|
urql_id_postfix=urql_id_postfix, # noqa
|
||||||
),
|
),
|
||||||
details_url=due_date.url_expert, # noqa
|
details_url=due_date.url_expert, # noqa
|
||||||
deadline=due_date.start, # noqa
|
deadline=due_date.start, # noqa
|
||||||
|
|
@ -166,7 +174,11 @@ def assignments(
|
||||||
course_session_selection_ids: graphene.List(graphene.ID),
|
course_session_selection_ids: graphene.List(graphene.ID),
|
||||||
user_selection_ids: List[str] | None = None,
|
user_selection_ids: List[str] | None = None,
|
||||||
circle_ids: List[graphene.ID] | None = None,
|
circle_ids: List[graphene.ID] | None = None,
|
||||||
|
urql_id: str = None,
|
||||||
) -> AssignmentsStatisticsType:
|
) -> AssignmentsStatisticsType:
|
||||||
|
if urql_id is None:
|
||||||
|
urql_id = str(course_id)
|
||||||
|
|
||||||
course_sessions = CourseSession.objects.filter(
|
course_sessions = CourseSession.objects.filter(
|
||||||
id__in=course_session_selection_ids,
|
id__in=course_session_selection_ids,
|
||||||
)
|
)
|
||||||
|
|
@ -176,21 +188,25 @@ def assignments(
|
||||||
for csa in query_competence_course_session_assignments(
|
for csa in query_competence_course_session_assignments(
|
||||||
[course_session.id], circle_ids
|
[course_session.id], circle_ids
|
||||||
):
|
):
|
||||||
record = create_record(csa, user_selection_ids)
|
record = create_record(csa, user_selection_ids, urql_id_postfix=urql_id)
|
||||||
records.append(record)
|
records.append(record)
|
||||||
|
|
||||||
for cset in query_competence_course_session_edoniq_tests(
|
for cset in query_competence_course_session_edoniq_tests(
|
||||||
[course_session.id], circle_ids
|
[course_session.id], circle_ids
|
||||||
):
|
):
|
||||||
record = create_record(
|
record = create_record(
|
||||||
course_session_assignment=cset, user_selection_ids=user_selection_ids
|
course_session_assignment=cset,
|
||||||
|
user_selection_ids=user_selection_ids,
|
||||||
|
urql_id_postfix=urql_id,
|
||||||
)
|
)
|
||||||
records.append(record)
|
records.append(record)
|
||||||
|
|
||||||
return AssignmentsStatisticsType(
|
return AssignmentsStatisticsType(
|
||||||
_id=course_id, # noqa
|
_id=urql_id, # noqa
|
||||||
records=sorted(records, key=lambda r: r.deadline), # noqa
|
records=sorted(records, key=lambda r: r.deadline), # noqa
|
||||||
summary=create_assignment_summary( # noqa
|
summary=create_assignment_summary( # noqa
|
||||||
course_id=course_id, metrics=[r.metrics for r in records] # noqa
|
course_id=course_id,
|
||||||
|
metrics=[r.metrics for r in records], # noqa
|
||||||
|
urql_id=urql_id, # noqa
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,11 @@ def attendance_day_presences(
|
||||||
course_id: graphene.ID,
|
course_id: graphene.ID,
|
||||||
course_session_selection_ids: graphene.List(graphene.ID),
|
course_session_selection_ids: graphene.List(graphene.ID),
|
||||||
circle_ids: List[graphene.ID] = None,
|
circle_ids: List[graphene.ID] = None,
|
||||||
|
urql_id: str = None,
|
||||||
) -> AttendanceDayPresencesStatisticsType:
|
) -> AttendanceDayPresencesStatisticsType:
|
||||||
|
if urql_id is None:
|
||||||
|
urql_id = str(course_id)
|
||||||
|
|
||||||
completed = CourseSessionAttendanceCourse.objects.filter(
|
completed = CourseSessionAttendanceCourse.objects.filter(
|
||||||
course_session_id__in=course_session_selection_ids,
|
course_session_id__in=course_session_selection_ids,
|
||||||
due_date__end__lt=timezone.now(),
|
due_date__end__lt=timezone.now(),
|
||||||
|
|
@ -76,7 +80,7 @@ def attendance_day_presences(
|
||||||
|
|
||||||
records.append(
|
records.append(
|
||||||
PresenceRecordStatisticsType(
|
PresenceRecordStatisticsType(
|
||||||
_id=f"attendance_day:{attendance_day.id}", # noqa
|
_id=f"{urql_id}:attendance_day:{attendance_day.id}", # noqa
|
||||||
course_session_id=course_session.id, # noqa
|
course_session_id=course_session.id, # noqa
|
||||||
generation=course_session.generation, # noqa
|
generation=course_session.generation, # noqa
|
||||||
circle_id=circle.id, # noqa
|
circle_id=circle.id, # noqa
|
||||||
|
|
@ -88,7 +92,7 @@ def attendance_day_presences(
|
||||||
)
|
)
|
||||||
|
|
||||||
summary = AttendanceSummaryStatisticsType(
|
summary = AttendanceSummaryStatisticsType(
|
||||||
_id=course_id, # noqa
|
_id=urql_id, # noqa
|
||||||
days_completed=len(records), # noqa
|
days_completed=len(records), # noqa
|
||||||
participants_present=calculate_avg_participation(records), # noqa
|
participants_present=calculate_avg_participation(records), # noqa
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ def competences(
|
||||||
course_slug: str,
|
course_slug: str,
|
||||||
user_selection_ids: List[str] | None = None,
|
user_selection_ids: List[str] | None = None,
|
||||||
circle_ids: List[str] | None = None,
|
circle_ids: List[str] | None = None,
|
||||||
|
urql_id_postfix: str = "",
|
||||||
) -> Tuple[List[CompetenceRecordStatisticsType], int, int]:
|
) -> Tuple[List[CompetenceRecordStatisticsType], int, int]:
|
||||||
completions = CourseCompletion.objects.filter(
|
completions = CourseCompletion.objects.filter(
|
||||||
course_session_id__in=course_session_selection_ids,
|
course_session_id__in=course_session_selection_ids,
|
||||||
|
|
@ -70,7 +71,7 @@ def competences(
|
||||||
if not circle:
|
if not circle:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
combined_id = f"{circle.id}-{completion.course_session.id}"
|
combined_id = f"{circle.id}-{completion.course_session.id}@{urql_id_postfix}"
|
||||||
|
|
||||||
competence_records.setdefault(combined_id, {}).setdefault(
|
competence_records.setdefault(combined_id, {}).setdefault(
|
||||||
learning_unit,
|
learning_unit,
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ class BaseStatisticsType(graphene.ObjectType):
|
||||||
course_session_selection_ids=root.course_session_selection_ids,
|
course_session_selection_ids=root.course_session_selection_ids,
|
||||||
user_selection_ids=user_selection_ids,
|
user_selection_ids=user_selection_ids,
|
||||||
circle_ids=root.get_circle_ids(_info),
|
circle_ids=root.get_circle_ids(_info),
|
||||||
|
urql_id=str(root._id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_circle_ids(self, info):
|
def get_circle_ids(self, info):
|
||||||
|
|
@ -133,6 +134,7 @@ class CourseStatisticsType(BaseStatisticsType):
|
||||||
course_id=root.course_id,
|
course_id=root.course_id,
|
||||||
course_session_selection_ids=root.course_session_selection_ids,
|
course_session_selection_ids=root.course_session_selection_ids,
|
||||||
circle_ids=root.get_circle_ids(info),
|
circle_ids=root.get_circle_ids(info),
|
||||||
|
urql_id=str(root._id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def resolve_feedback_responses(root, info) -> FeedbackStatisticsResponsesType:
|
def resolve_feedback_responses(root, info) -> FeedbackStatisticsResponsesType:
|
||||||
|
|
@ -141,6 +143,7 @@ class CourseStatisticsType(BaseStatisticsType):
|
||||||
course_id=root.course_id,
|
course_id=root.course_id,
|
||||||
course_slug=root.course_slug,
|
course_slug=root.course_slug,
|
||||||
circle_ids=root.get_circle_ids(info),
|
circle_ids=root.get_circle_ids(info),
|
||||||
|
urql_id=str(root._id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def resolve_competences(root, info) -> CompetencesStatisticsType:
|
def resolve_competences(root, info) -> CompetencesStatisticsType:
|
||||||
|
|
@ -156,6 +159,7 @@ class CourseStatisticsType(BaseStatisticsType):
|
||||||
],
|
],
|
||||||
user_selection_ids=user_selection_ids, # noqa
|
user_selection_ids=user_selection_ids, # noqa
|
||||||
circle_ids=root.get_circle_ids(info), # noqa
|
circle_ids=root.get_circle_ids(info), # noqa
|
||||||
|
urql_id_postfix=str(root._id), # noqa
|
||||||
)
|
)
|
||||||
return CompetencesStatisticsType(
|
return CompetencesStatisticsType(
|
||||||
_id=root._id, # noqa
|
_id=root._id, # noqa
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,11 @@ def feedback_responses(
|
||||||
course_id: graphene.ID,
|
course_id: graphene.ID,
|
||||||
course_slug: graphene.String,
|
course_slug: graphene.String,
|
||||||
circle_ids: List[graphene.ID] = None,
|
circle_ids: List[graphene.ID] = None,
|
||||||
|
urql_id: str = None,
|
||||||
) -> FeedbackStatisticsResponsesType:
|
) -> FeedbackStatisticsResponsesType:
|
||||||
|
if urql_id is None:
|
||||||
|
urql_id = str(course_id)
|
||||||
|
|
||||||
# Get all course sessions for this user in the given course
|
# Get all course sessions for this user in the given course
|
||||||
course_sessions = CourseSession.objects.filter(
|
course_sessions = CourseSession.objects.filter(
|
||||||
id__in=course_session_selection_ids,
|
id__in=course_session_selection_ids,
|
||||||
|
|
@ -65,6 +69,7 @@ def feedback_responses(
|
||||||
course_session_id=course_session.id,
|
course_session_id=course_session.id,
|
||||||
generation=course_session.generation,
|
generation=course_session.generation,
|
||||||
course_slug=str(course_slug),
|
course_slug=str(course_slug),
|
||||||
|
urql_id_postfix=urql_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -76,10 +81,10 @@ def feedback_responses(
|
||||||
avg = 0
|
avg = 0
|
||||||
|
|
||||||
return FeedbackStatisticsResponsesType(
|
return FeedbackStatisticsResponsesType(
|
||||||
_id=course_id, # noqa
|
_id=urql_id, # noqa
|
||||||
records=circle_feedbacks, # noqa
|
records=circle_feedbacks, # noqa
|
||||||
summary=FeedbackStatisticsSummaryType( # noqa
|
summary=FeedbackStatisticsSummaryType( # noqa
|
||||||
_id=course_id, # noqa
|
_id=urql_id, # noqa
|
||||||
satisfaction_average=avg, # noqa
|
satisfaction_average=avg, # noqa
|
||||||
satisfaction_max=4, # noqa
|
satisfaction_max=4, # noqa
|
||||||
total_responses=total_responses, # noqa
|
total_responses=total_responses, # noqa
|
||||||
|
|
@ -92,6 +97,7 @@ def circle_feedback_average(
|
||||||
course_session_id,
|
course_session_id,
|
||||||
generation: str,
|
generation: str,
|
||||||
course_slug: str,
|
course_slug: str,
|
||||||
|
urql_id_postfix: str = "",
|
||||||
):
|
):
|
||||||
circle_data = {}
|
circle_data = {}
|
||||||
records = []
|
records = []
|
||||||
|
|
@ -119,7 +125,7 @@ def circle_feedback_average(
|
||||||
|
|
||||||
records.append(
|
records.append(
|
||||||
FeedbackStatisticsRecordType(
|
FeedbackStatisticsRecordType(
|
||||||
_id=f"circle:{circle_id}-course_session:{course_session_id}", # noqa
|
_id=f"circle:{circle_id}-course_session:{course_session_id}@{urql_id_postfix}", # noqa
|
||||||
course_session_id=course_session_id, # noqa
|
course_session_id=course_session_id, # noqa
|
||||||
generation=generation, # noqa
|
generation=generation, # noqa
|
||||||
circle_id=circle_id, # noqa
|
circle_id=circle_id, # noqa
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,10 @@
|
||||||
<label>
|
<label>
|
||||||
evaluation score:
|
evaluation score:
|
||||||
<input type="text" name="assignment_evaluation_scores" placeholder="6,6,6,3,3">
|
<input type="text" name="assignment_evaluation_scores" placeholder="6,6,6,3,3">
|
||||||
|
</label><br>
|
||||||
|
<label>
|
||||||
|
points deducted:
|
||||||
|
<input type="number" name="assignment_points_deducted" min="0">
|
||||||
</label>
|
</label>
|
||||||
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
|
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
|
||||||
|
|
||||||
|
|
@ -84,6 +88,10 @@
|
||||||
<label>
|
<label>
|
||||||
max points:
|
max points:
|
||||||
<input type="number" name="edoniq_test_max_points" min="0">
|
<input type="number" name="edoniq_test_max_points" min="0">
|
||||||
|
</label><br>
|
||||||
|
<label>
|
||||||
|
points deducted:
|
||||||
|
<input type="number" name="edoniq_test_points_deducted" min="0">
|
||||||
</label>
|
</label>
|
||||||
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
|
<div style="margin-bottom: 8px; padding: 4px; border-bottom: 1px lightblue solid"></div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue