feat: kompetenznavi overview
This commit is contained in:
parent
0d402d7912
commit
964c1f7276
|
|
@ -8,7 +8,7 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const hasFeedbackReceived = computed(() => {
|
||||
return props.summary.feedback_assessment?.provider_user !== undefined;
|
||||
return props.summary.feedback_assessment?.submitted_by_provider ?? false;
|
||||
});
|
||||
|
||||
const feedbackProviderAvatar = computed(() => {
|
||||
|
|
@ -46,19 +46,19 @@ const feedbackProviderName = computed(() => {
|
|||
</div>
|
||||
<div class="cell">
|
||||
<SmileyCell
|
||||
:count="props.summary.self_assessment.counts.pass + 1"
|
||||
:count="props.summary.self_assessment.counts.pass"
|
||||
smiley="it-icon-smiley-happy"
|
||||
/>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<SmileyCell
|
||||
:count="props.summary.self_assessment.counts.fail + 1"
|
||||
:count="props.summary.self_assessment.counts.fail"
|
||||
smiley="it-icon-smiley-thinking"
|
||||
/>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<SmileyCell
|
||||
:count="props.summary.self_assessment.counts.unknown + 1"
|
||||
:count="props.summary.self_assessment.counts.unknown"
|
||||
smiley="it-icon-smiley-neutral"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ import { COMPETENCE_NAVI_CERTIFICATE_QUERY } from "@/graphql/queries";
|
|||
import { useQuery } from "@urql/vue";
|
||||
import { computed } from "vue";
|
||||
import type { CompetenceCertificate } from "@/types";
|
||||
import { useCurrentCourseSession, useCourseDataWithCompletion } from "@/composables";
|
||||
import { useCurrentCourseSession } from "@/composables";
|
||||
import {
|
||||
assignmentsMaxEvaluationPoints,
|
||||
assignmentsUserPoints,
|
||||
competenceCertificateProgressStatusCount,
|
||||
} from "@/pages/competence/utils";
|
||||
import { useSelfEvaluationFeedbackSummaries } from "@/services/selfEvaluationFeedback";
|
||||
import ItProgress from "@/components/ui/ItProgress.vue";
|
||||
import { calcPerformanceCriteriaStatusCount } from "@/services/competence";
|
||||
|
||||
const props = defineProps<{
|
||||
courseSlug: string;
|
||||
|
|
@ -20,7 +20,6 @@ const props = defineProps<{
|
|||
log.debug("CompetenceIndexPage setup", props);
|
||||
|
||||
const courseSession = useCurrentCourseSession();
|
||||
const courseData = useCourseDataWithCompletion(props.courseSlug);
|
||||
|
||||
const certificatesQuery = useQuery({
|
||||
query: COMPETENCE_NAVI_CERTIFICATE_QUERY,
|
||||
|
|
@ -49,9 +48,23 @@ const userPointsEvaluatedAssignments = computed(() => {
|
|||
return assignmentsUserPoints(allAssignments.value);
|
||||
});
|
||||
|
||||
const performanceCriteriaStatusCount = computed(() => {
|
||||
return calcPerformanceCriteriaStatusCount(courseData.flatPerformanceCriteria.value);
|
||||
});
|
||||
const selfEvaluationFeedbackSummaries = useSelfEvaluationFeedbackSummaries(
|
||||
useCurrentCourseSession().value.id
|
||||
);
|
||||
|
||||
const selfAssessmentCounts = computed(
|
||||
() => selfEvaluationFeedbackSummaries.aggregates.value?.self_assessment
|
||||
);
|
||||
|
||||
const feedbackEvaluationCounts = computed(
|
||||
() => selfEvaluationFeedbackSummaries.aggregates.value?.feedback_assessment
|
||||
);
|
||||
|
||||
const isFeedbackEvaluationVisible = computed(
|
||||
() =>
|
||||
selfEvaluationFeedbackSummaries.aggregates.value?.feedback_assessment_visible ??
|
||||
false
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -80,7 +93,7 @@ const performanceCriteriaStatusCount = computed(() => {
|
|||
<div
|
||||
v-for="certificate in competenceCertificates"
|
||||
:key="certificate.id"
|
||||
class="flex flex-col justify-between border-b py-4 first:border-t lg:flex-row lg:items-center"
|
||||
class="flex flex-col justify-between py-4 lg:flex-row lg:items-center"
|
||||
:data-cy="`certificate-${certificate.slug}`"
|
||||
>
|
||||
<div class="text-bold text-xl">
|
||||
|
|
@ -130,16 +143,15 @@ const performanceCriteriaStatusCount = computed(() => {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Self-evaluation -->
|
||||
<section class="mb-4 bg-white px-8 py-4 lg:mb-8 lg:py-8">
|
||||
<h3 class="mb-4 border-b pb-4 lg:border-0 lg:pb-0">
|
||||
<h3 class="mb-4 pb-4 lg:pb-0">
|
||||
{{ $t("a.Selbsteinschätzungen") }}
|
||||
</h3>
|
||||
<ul
|
||||
class="mb-6 flex flex-col lg:flex-row lg:items-center lg:justify-between lg:gap-8"
|
||||
>
|
||||
<li
|
||||
class="mb-4 inline-block flex-1 border-b pb-4 lg:mb-0 lg:w-1/3 lg:border-b-0 lg:border-r lg:pb-0"
|
||||
>
|
||||
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||
<h5 class="mb-4 text-gray-700">«{{ $t("selfEvaluation.no") }}»</h5>
|
||||
<div class="flex flex-row items-center">
|
||||
<it-icon-smiley-thinking class="h-16 w-16"></it-icon-smiley-thinking>
|
||||
|
|
@ -147,13 +159,11 @@ const performanceCriteriaStatusCount = computed(() => {
|
|||
class="ml-4 inline-block text-7xl font-bold"
|
||||
data-cy="self-evaluation-fail"
|
||||
>
|
||||
{{ performanceCriteriaStatusCount.FAIL }}
|
||||
{{ selfAssessmentCounts?.fail }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="mb-4 inline-block flex-1 border-b pb-4 lg:mb-0 lg:w-1/3 lg:border-b-0 lg:border-r lg:pb-0"
|
||||
>
|
||||
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||
<h5 class="mb-4 text-gray-700">«{{ $t("selfEvaluation.yes") }}»</h5>
|
||||
<div class="flex flex-row items-center">
|
||||
<it-icon-smiley-happy class="h-16 w-16"></it-icon-smiley-happy>
|
||||
|
|
@ -161,11 +171,11 @@ const performanceCriteriaStatusCount = computed(() => {
|
|||
class="ml-4 inline-block text-7xl font-bold"
|
||||
data-cy="self-evaluation-success"
|
||||
>
|
||||
{{ performanceCriteriaStatusCount.SUCCESS }}
|
||||
{{ selfAssessmentCounts?.pass }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex-1 border-b pb-4 lg:mb-0 lg:w-1/3 lg:border-b-0 lg:pb-0">
|
||||
<li class="flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||
<h5 class="mb-4 text-gray-700">{{ $t("competences.notAssessed") }}</h5>
|
||||
<div class="flex flex-row items-center">
|
||||
<it-icon-smiley-neutral class="h-16 w-16"></it-icon-smiley-neutral>
|
||||
|
|
@ -173,12 +183,59 @@ const performanceCriteriaStatusCount = computed(() => {
|
|||
class="ml-4 inline-block text-7xl font-bold"
|
||||
data-cy="self-evaluation-unknown"
|
||||
>
|
||||
{{ performanceCriteriaStatusCount.UNKNOWN }}
|
||||
{{ selfAssessmentCounts?.unknown }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Feedback evaluation -->
|
||||
<template v-if="isFeedbackEvaluationVisible">
|
||||
<h3 class="mb-4 border-t pb-4 pt-4 lg:pb-0">
|
||||
{{ $t("a.Fremdeinschätzungen") }}
|
||||
</h3>
|
||||
<ul
|
||||
class="mb-6 flex flex-col lg:flex-row lg:items-center lg:justify-between lg:gap-8"
|
||||
>
|
||||
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||
<h5 class="mb-4 text-gray-700">«{{ $t("receivedEvaluation.no") }}»</h5>
|
||||
<div class="flex flex-row items-center">
|
||||
<it-icon-smiley-thinking class="h-16 w-16"></it-icon-smiley-thinking>
|
||||
<p
|
||||
class="ml-4 inline-block text-7xl font-bold"
|
||||
data-cy="self-evaluation-fail"
|
||||
>
|
||||
{{ feedbackEvaluationCounts?.fail }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="mb-4 inline-block flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||
<h5 class="mb-4 text-gray-700">«{{ $t("receivedEvaluation.yes") }}»</h5>
|
||||
<div class="flex flex-row items-center">
|
||||
<it-icon-smiley-happy class="h-16 w-16"></it-icon-smiley-happy>
|
||||
<p
|
||||
class="ml-4 inline-block text-7xl font-bold"
|
||||
data-cy="self-evaluation-success"
|
||||
>
|
||||
{{ feedbackEvaluationCounts?.pass }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="flex-1 pb-4 lg:mb-0 lg:w-1/3 lg:pb-0">
|
||||
<h5 class="mb-4 text-gray-700">{{ $t("competences.notAssessed") }}</h5>
|
||||
<div class="flex flex-row items-center">
|
||||
<it-icon-smiley-neutral class="h-16 w-16"></it-icon-smiley-neutral>
|
||||
<p
|
||||
class="ml-4 inline-block text-7xl font-bold"
|
||||
data-cy="self-evaluation-unknown"
|
||||
>
|
||||
{{ feedbackEvaluationCounts?.unknown }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<div>
|
||||
<router-link
|
||||
:to="`/course/${props.courseSlug}/competence/criteria`"
|
||||
|
|
|
|||
|
|
@ -32,6 +32,17 @@ const summaries = computed(() => {
|
|||
);
|
||||
});
|
||||
|
||||
const headerTitle = computed(() => {
|
||||
const canHaveFeedback =
|
||||
selfEvaluationFeedbackSummaries.aggregates.value?.feedback_assessment_visible ??
|
||||
false;
|
||||
if (canHaveFeedback) {
|
||||
return t("a.Selbst- und Fremdeinschätzungen");
|
||||
} else {
|
||||
return t("a.Selbsteinschätzungen");
|
||||
}
|
||||
});
|
||||
|
||||
log.info("SelfEvaluationsAndFeedbackPage created");
|
||||
</script>
|
||||
|
||||
|
|
@ -39,7 +50,7 @@ log.info("SelfEvaluationsAndFeedbackPage created");
|
|||
<div v-if="isLoaded">
|
||||
<div class="container-large">
|
||||
<div class="col flex items-center justify-between pb-4">
|
||||
<h2 class="py-4">{{ $t("a.Selbst- und Fremdeinschätzungen") }}</h2>
|
||||
<h2 class="py-4">{{ headerTitle }}</h2>
|
||||
<ItDropdownSelect
|
||||
v-model="selectedCircle"
|
||||
class="text-bold w-24 min-w-[18rem] border-2 border-gray-300"
|
||||
|
|
|
|||
|
|
@ -33,8 +33,10 @@ interface FeedbackSummaryCounts {
|
|||
|
||||
export interface FeedbackSummaryAggregates {
|
||||
// totals across all learning units in the course session
|
||||
self_assessment_counts: FeedbackSummaryCounts;
|
||||
feedback_assessment_counts: FeedbackSummaryCounts;
|
||||
self_assessment: FeedbackSummaryCounts;
|
||||
feedback_assessment: FeedbackSummaryCounts;
|
||||
// does this course have any feedback?
|
||||
feedback_assessment_visible: boolean;
|
||||
}
|
||||
|
||||
interface FeedbackAssessmentSummary {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ from vbv_lernwelt.self_evaluation_feedback.views import (
|
|||
urlpatterns = [
|
||||
# /requester/* URLs -> For the user who requests feedback
|
||||
path(
|
||||
"requester/<int:course_session_id>/feedbacks/summaries",
|
||||
"requester/<signed_int:course_session_id>/feedbacks/summaries",
|
||||
get_self_evaluation_feedbacks_as_requester,
|
||||
name="get_self_evaluation_feedbacks_as_requester",
|
||||
),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ class AssessmentCounts(NamedTuple):
|
|||
fail_count: int
|
||||
unknown_count: int
|
||||
|
||||
@property
|
||||
def total_count(self):
|
||||
return self.pass_count + self.fail_count + self.unknown_count
|
||||
|
||||
|
||||
def get_self_evaluation_feedback_counts(
|
||||
feedback: SelfEvaluationFeedback,
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ from vbv_lernwelt.self_evaluation_feedback.serializers import (
|
|||
SelfEvaluationFeedbackSerializer,
|
||||
)
|
||||
from vbv_lernwelt.self_evaluation_feedback.utils import (
|
||||
calculate_aggregate,
|
||||
get_self_assessment_counts,
|
||||
get_self_evaluation_feedback_counts,
|
||||
AssessmentCounts,
|
||||
)
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
|
@ -105,7 +105,6 @@ def get_self_evaluation_feedbacks_as_requester(request, course_session_id: int):
|
|||
all_feedback_assessment_counts = []
|
||||
|
||||
for learning_unit in LearningUnit.objects.filter(
|
||||
feedback_user=LearningUnitPerformanceFeedbackType.MENTOR_FEEDBACK.value,
|
||||
course_category__course=course_session.course,
|
||||
):
|
||||
# this is not a problem in real life, but in the test environment
|
||||
|
|
@ -161,12 +160,34 @@ def get_self_evaluation_feedbacks_as_requester(request, course_session_id: int):
|
|||
}
|
||||
)
|
||||
|
||||
self_assessment_counts_aggregate = calculate_aggregate(
|
||||
counts=all_self_assessment_counts
|
||||
self_assessment_counts_aggregate = AssessmentCounts(
|
||||
pass_count=sum(x.pass_count for x in all_self_assessment_counts),
|
||||
fail_count=sum(x.fail_count for x in all_self_assessment_counts),
|
||||
unknown_count=sum(x.unknown_count for x in all_self_assessment_counts),
|
||||
)
|
||||
received_feedback_counts_aggregate = AssessmentCounts(
|
||||
pass_count=sum(x.pass_count for x in all_feedback_assessment_counts),
|
||||
fail_count=sum(x.fail_count for x in all_feedback_assessment_counts),
|
||||
unknown_count=sum(x.unknown_count for x in all_feedback_assessment_counts),
|
||||
)
|
||||
|
||||
feedback_assessment_counts_aggregate = calculate_aggregate(
|
||||
counts=all_feedback_assessment_counts
|
||||
# pad the feedback counts with unknowns for the
|
||||
# learning units where we have no feedback yet
|
||||
feedback_assessment_counts_aggregate = AssessmentCounts(
|
||||
pass_count=received_feedback_counts_aggregate.pass_count,
|
||||
fail_count=received_feedback_counts_aggregate.fail_count,
|
||||
unknown_count=self_assessment_counts_aggregate.total_count
|
||||
- received_feedback_counts_aggregate.total_count
|
||||
+ received_feedback_counts_aggregate.unknown_count,
|
||||
)
|
||||
|
||||
# check if there are any learning units with mentor feedback
|
||||
feedback_assessment_visible = (
|
||||
LearningUnit.objects.filter(
|
||||
feedback_user=LearningUnitPerformanceFeedbackType.MENTOR_FEEDBACK.value,
|
||||
course_category__course=course_session.course,
|
||||
).count()
|
||||
> 0
|
||||
)
|
||||
|
||||
return Response(
|
||||
|
|
@ -176,6 +197,7 @@ def get_self_evaluation_feedbacks_as_requester(request, course_session_id: int):
|
|||
Circle.objects.filter(id__in=circle_ids).values("id", "title")
|
||||
),
|
||||
"aggregates": {
|
||||
"feedback_assessment_visible": feedback_assessment_visible,
|
||||
"feedback_assessment": {
|
||||
"pass": feedback_assessment_counts_aggregate.pass_count,
|
||||
"fail": feedback_assessment_counts_aggregate.fail_count,
|
||||
|
|
|
|||
Loading…
Reference in New Issue