Show grades instead of points for certificates

This commit is contained in:
Daniel Egger 2024-05-03 17:12:15 +02:00
parent 8cab40f1d5
commit 4b9614d89d
7 changed files with 79 additions and 36 deletions

View File

@ -5,8 +5,8 @@ import CompetenceAssignmentRow from "@/pages/competence/CompetenceAssignmentRow.
import { computed } from "vue";
import ItProgress from "@/components/ui/ItProgress.vue";
import {
assignmentsMaxEvaluationPoints,
assignmentsUserPoints,
calcCompetenceCertificateGrade,
competenceCertificateProgressStatusCount,
} from "@/pages/competence/utils";
@ -18,14 +18,14 @@ const props = defineProps<{
frontendUrl?: string;
}>();
const totalPointsEvaluatedAssignments = computed(() => {
return assignmentsMaxEvaluationPoints(props.competenceCertificate.assignments);
});
const userPointsEvaluatedAssignments = computed(() => {
return assignmentsUserPoints(props.competenceCertificate.assignments);
});
const userGrade = computed(() => {
return calcCompetenceCertificateGrade(props.competenceCertificate.assignments);
});
const numAssignmentsEvaluated = computed(() => {
return props.competenceCertificate.assignments.filter((a) => {
return a.completion?.completion_status === "EVALUATION_SUBMITTED";
@ -75,10 +75,7 @@ const frontendUrl = computed(() => {
class="py-4"
:class="{ 'heading-1': props.detailView, 'heading-2': !props.detailView }"
>
{{ userPointsEvaluatedAssignments }}
</div>
<div class="ml-1">
{{ $t("assignment.von x Punkten", { x: totalPointsEvaluatedAssignments }) }}
{{ userGrade }}
</div>
</section>
<section v-else class="py-2">

View File

@ -5,8 +5,8 @@ import type { CompetenceCertificate } from "@/types";
import { useCertificateQuery } from "@/composables";
import CompetenceCertificateComponent from "@/pages/competence/CompetenceCertificateComponent.vue";
import {
assignmentsMaxEvaluationPoints,
assignmentsUserPoints,
calcCompetencesTotalGrade,
} from "@/pages/competence/utils";
import { useRoute } from "vue-router";
import { getCertificates } from "@/services/competence";
@ -44,8 +44,8 @@ const assignments = computed(() => {
return competenceCertificates?.value?.flatMap((cc) => cc.assignments);
});
const totalPointsEvaluatedAssignments = computed(() => {
return assignmentsMaxEvaluationPoints(assignments.value ?? []);
const totalGrade = computed(() => {
return calcCompetencesTotalGrade(competenceCertificates.value ?? []);
});
const userPointsEvaluatedAssignments = computed(() => {
@ -91,10 +91,7 @@ onMounted(async () => {
<section v-if="userPointsEvaluatedAssignments > 0" class="flex items-center">
<div class="heading-1 py-4">
{{ userPointsEvaluatedAssignments }}
</div>
<div class="pl-2">
{{ $t("assignment.von x Punkten", { x: totalPointsEvaluatedAssignments }) }}
{{ totalGrade }}
</div>
</section>
<section v-else class="my-4">

View File

@ -6,8 +6,9 @@ import { computed } from "vue";
import type { CompetenceCertificate } from "@/types";
import { useCurrentCourseSession } from "@/composables";
import {
assignmentsMaxEvaluationPoints,
assignmentsUserPoints,
calcCompetenceCertificateGrade,
calcCompetencesTotalGrade,
competenceCertificateProgressStatusCount,
} from "@/pages/competence/utils";
import ItProgress from "@/components/ui/ItProgress.vue";
@ -42,10 +43,6 @@ const allAssignments = computed(() => {
return competenceCertificates.value.flatMap((cc) => cc.assignments);
});
const totalPointsEvaluatedAssignments = computed(() => {
return assignmentsMaxEvaluationPoints(allAssignments.value);
});
const userPointsEvaluatedAssignments = computed(() => {
return assignmentsUserPoints(allAssignments.value);
});
@ -71,9 +68,8 @@ const router = useRouter();
<div v-if="userPointsEvaluatedAssignments > 0">
{{ $t("a.Zwischenstand") }} {{ $t("a.Gesamtpunktzahl") }}:
<span class="font-bold">
{{ userPointsEvaluatedAssignments }}
{{ calcCompetencesTotalGrade(competenceCertificates ?? []) }}
</span>
{{ $t("assignment.von x Punkten", { x: totalPointsEvaluatedAssignments }) }}
</div>
<div v-else>
{{ $t("a.competenceCertificateNoUserPoints") }}
@ -92,14 +88,12 @@ const router = useRouter();
{{ certificate.title }}
</div>
<div class="mt-4 lg:mt-0">
<span class="text-bold">
{{ assignmentsUserPoints(certificate.assignments) }}
<span
v-if="calcCompetenceCertificateGrade(certificate.assignments)"
class="text-bold"
>
{{ calcCompetenceCertificateGrade(certificate.assignments) }}
</span>
{{
$t("assignment.von x Punkten", {
x: assignmentsMaxEvaluationPoints(certificate.assignments),
})
}}
</div>
<div class="flex">
<div>

View File

@ -1,5 +1,6 @@
import type { StatusCount } from "@/components/ui/ItProgress.vue";
import type { CompetenceCertificateAssignment } from "@/types";
import { percentToGrade } from "@/services/assignmentService";
import type { CompetenceCertificate, CompetenceCertificateAssignment } from "@/types";
import _ from "lodash";
export function assignmentsMaxEvaluationPoints(
@ -20,6 +21,51 @@ export function assignmentsUserPoints(assignments: CompetenceCertificateAssignme
).toFixed(1);
}
export function calcCompetenceCertificateGrade(
assignments: CompetenceCertificateAssignment[]
) {
const evaluatedAssignments = assignments.filter(
(a) => a.completion?.completion_status === "EVALUATION_SUBMITTED"
);
const adjustedResults = evaluatedAssignments.map((a) => {
return (
((a.completion?.evaluation_points ?? 0) / a.max_points) *
a.competence_certificate_weight
);
});
const adjustedAssignmentCount = _.sum(
evaluatedAssignments.map((a) => a.competence_certificate_weight)
);
if (adjustedAssignmentCount === 0) {
return undefined;
}
return percentToGrade(_.sum(adjustedResults) / adjustedAssignmentCount);
}
export function calcCompetencesTotalGrade(
competenceCertificates: CompetenceCertificate[]
) {
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 percentToGrade(percentGraded);
}
export function competenceCertificateProgressStatusCount(
assignments: CompetenceCertificateAssignment[]
) {

View File

@ -90,3 +90,10 @@ export function pointsToGrade(points: number, maxPoints: number) {
const halfGrade = grade / 2;
return Math.min(halfGrade, 5) + 1;
}
export function percentToGrade(percent: number) {
// round to half-grades
const grade = Math.round(percent * 10);
const halfGrade = grade / 2;
return Math.min(halfGrade, 5) + 1;
}

View File

@ -392,6 +392,7 @@ export type ActionCompetence = Omit<
export interface CompetenceCertificateAssignment extends BaseCourseWagtailPage {
assignment_type: "CASEWORK" | "EDONIQ_TEST";
max_points: number;
competence_certificate_weight: number;
learning_content:
| (BaseCourseWagtailPage & {
circle: CircleLight;

View File

@ -4,15 +4,16 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assignment', '0012_auto_20240124_1004'),
("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'),
model_name="assignment",
name="competence_certificate_weight",
field=models.FloatField(
default=1.0, help_text="Gewichtung für den Kompetenznachweis"
),
),
]