Filter for list, add average grade

This commit is contained in:
Daniel Egger 2024-07-27 10:20:54 +02:00
parent b7231fb1b7
commit 29c42f3512
12 changed files with 204 additions and 71 deletions

View File

@ -4,27 +4,53 @@ import ItDropdownSelect from "@/components/ui/ItDropdownSelect.vue";
import { useTranslation } from "i18next-vue"; import { useTranslation } from "i18next-vue";
import type { StatisticsCourseSessionPropertiesType } from "@/gql/graphql"; import type { StatisticsCourseSessionPropertiesType } from "@/gql/graphql";
import type { StatisticsFilterItem } from "@/types"; import type { StatisticsFilterItem } from "@/types";
import _ from "lodash";
const { t } = useTranslation(); const { t } = useTranslation();
const props = defineProps<{ const props = defineProps<{
items: StatisticsFilterItem[]; items: StatisticsFilterItem[];
courseSessionProperties: StatisticsCourseSessionPropertiesType; courseSessionProperties: StatisticsCourseSessionPropertiesType;
hideCircleFilter?: boolean;
}>(); }>();
defineExpose({ getFilteredItems }); defineExpose({ getFilteredItems });
const regionFilter = computed(() => {
const regions = _.uniq(
props.courseSessionProperties.sessions.map((session) => session.region)
);
const f = regions.map((region) => ({
name: `Region: ${region}`,
id: region,
}));
return [
{
name: `${t("Region")}: ${t("a.Alle")}`,
id: "_all",
},
...f,
];
});
const sessionFilter = computed(() => { const sessionFilter = computed(() => {
const f = props.courseSessionProperties.sessions.map((session) => ({ let values = props.courseSessionProperties.sessions.map((session) => ({
name: `${t("a.Durchfuehrung")}: ${session.name}`, name: session.name,
region: session.region,
id: session.id, id: session.id,
})); }));
return [{ name: t("a.AlleDurchführungen"), id: "_all" }, ...f];
// filter by selected region
if (regionFilterValue.value.id !== "_all") {
values = values.filter((cs) => cs.region === regionFilterValue.value.id);
}
return [{ name: t("a.AlleDurchführungen"), id: "_all" }, ...values];
}); });
const generationFilter = computed(() => { const generationFilter = computed(() => {
const f = props.courseSessionProperties.generations.map((generation) => ({ const f = props.courseSessionProperties.generations.map((generation) => ({
name: `${t("a.Generation")}: ${generation}`, name: generation,
id: generation, id: generation,
})); }));
return [{ name: t("a.AlleGenerationen"), id: "_all" }, ...f]; return [{ name: t("a.AlleGenerationen"), id: "_all" }, ...f];
@ -32,12 +58,13 @@ const generationFilter = computed(() => {
const circleFilter = computed(() => { const circleFilter = computed(() => {
const f = props.courseSessionProperties.circles.map((circle) => ({ const f = props.courseSessionProperties.circles.map((circle) => ({
name: `Circle: ${circle.name}`, name: circle.name,
id: circle.id, id: circle.id,
})); }));
return [{ name: t("a.AlleCircle"), id: "_all" }, ...f]; return [{ name: t("a.AlleCircle"), id: "_all" }, ...f];
}); });
const regionFilterValue = ref(regionFilter.value[0]);
const sessionFilterValue = ref(sessionFilter.value[0]); const sessionFilterValue = ref(sessionFilter.value[0]);
const generationFilterValue = ref(generationFilter.value[0]); const generationFilterValue = ref(generationFilter.value[0]);
const circleFilterValue = ref(circleFilter.value[0]); const circleFilterValue = ref(circleFilter.value[0]);
@ -48,12 +75,21 @@ watch(
sessionFilterValue.value = sessionFilter.value[0]; sessionFilterValue.value = sessionFilter.value[0];
generationFilterValue.value = generationFilter.value[0]; generationFilterValue.value = generationFilter.value[0];
circleFilterValue.value = circleFilter.value[0]; circleFilterValue.value = circleFilter.value[0];
regionFilterValue.value = regionFilter.value[0];
}, },
{ deep: true } { deep: true }
); );
watch(regionFilterValue, () => {
console.log("regionFilterValue", regionFilterValue.value);
sessionFilterValue.value = sessionFilter.value[0];
});
const filteredItems = computed(() => { const filteredItems = computed(() => {
return props.items.filter((item) => { return props.items.filter((item) => {
const regionMatch =
regionFilterValue.value.id === "_all" ||
item.region === regionFilterValue.value.id;
const sessionMatch = const sessionMatch =
sessionFilterValue.value.id === "_all" || sessionFilterValue.value.id === "_all" ||
item.course_session_id === sessionFilterValue.value.id; item.course_session_id === sessionFilterValue.value.id;
@ -64,7 +100,7 @@ const filteredItems = computed(() => {
circleFilterValue.value.id === "_all" || circleFilterValue.value.id === "_all" ||
item.circle_id === circleFilterValue.value.id; item.circle_id === circleFilterValue.value.id;
return sessionMatch && generationMatch && circleMatch; return regionMatch && sessionMatch && generationMatch && circleMatch;
}); });
}); });
@ -75,30 +111,45 @@ function getFilteredItems() {
<template> <template>
<div> <div>
<div class="flex flex-col space-x-2 lg:flex-row"> <div class="flex flex-col space-x-2 lg:flex-row border-b">
<ItDropdownSelect
v-if="regionFilter.length > 2"
v-model="regionFilterValue"
class="min-w-[12rem]"
:items="regionFilter"
borderless
></ItDropdownSelect>
<ItDropdownSelect <ItDropdownSelect
v-model="sessionFilterValue" v-model="sessionFilterValue"
class="min-w-[18rem]" class="min-w-[12rem]"
:items="sessionFilter" :items="sessionFilter"
borderless borderless
></ItDropdownSelect> ></ItDropdownSelect>
<ItDropdownSelect <ItDropdownSelect
v-model="generationFilterValue" v-model="generationFilterValue"
class="min-w-[18rem]" class="min-w-[12rem]"
:items="generationFilter" :items="generationFilter"
borderless borderless
></ItDropdownSelect> ></ItDropdownSelect>
<ItDropdownSelect <ItDropdownSelect
v-if="!props.hideCircleFilter"
v-model="circleFilterValue" v-model="circleFilterValue"
class="min-w-[18rem]" class="min-w-[12rem]"
:items="circleFilter" :items="circleFilter"
borderless borderless
></ItDropdownSelect> ></ItDropdownSelect>
</div> </div>
<div v-for="item in filteredItems" :key="item._id" class="px-5"> <slot name="header"></slot>
<div class="border-t border-gray-500 py-4"> <section>
<slot :item="item"></slot> <div
v-for="item in filteredItems"
:key="item._id"
class="mx-6 border-t border-gray-500 first:border-t-0"
>
<div class="py-4">
<slot :item="item"></slot>
</div>
</div> </div>
</div> </section>
</div> </div>
</template> </template>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -52,9 +52,11 @@ type AssignmentStatisticsRecordType {
course_session_assignment_id: ID! course_session_assignment_id: ID!
circle_id: ID! circle_id: ID!
generation: String! generation: String!
region: String!
assignment_type_translation_key: String! assignment_type_translation_key: String!
assignment_title: String! assignment_title: String!
deadline: DateTime! deadline: DateTime!
course_session_title: String
competence_certificate_id: ID competence_certificate_id: ID
competence_certificate_title: String competence_certificate_title: String
metrics: AssignmentCompletionMetricsType! metrics: AssignmentCompletionMetricsType!
@ -98,6 +100,7 @@ type StatisticsCourseSessionPropertiesType {
type StatisticsCourseSessionDataType { type StatisticsCourseSessionDataType {
id: ID! id: ID!
name: String! name: String!
region: String!
} }
type StatisticsCircleDataType { type StatisticsCircleDataType {
@ -122,6 +125,7 @@ type PresenceRecordStatisticsType {
_id: ID! _id: ID!
course_session_id: ID! course_session_id: ID!
generation: String! generation: String!
region: String!
circle_id: ID! circle_id: ID!
due_date: DateTime! due_date: DateTime!
participants_present: Int! participants_present: Int!
@ -145,6 +149,7 @@ type FeedbackStatisticsRecordType {
_id: ID! _id: ID!
course_session_id: ID! course_session_id: ID!
generation: String! generation: String!
region: String!
circle_id: ID! circle_id: ID!
satisfaction_average: Float! satisfaction_average: Float!
satisfaction_max: Int! satisfaction_max: Int!
@ -175,6 +180,7 @@ type CompetenceRecordStatisticsType {
_id: ID! _id: ID!
course_session_id: ID! course_session_id: ID!
generation: String! generation: String!
region: String!
title: String! title: String!
circle_id: ID! circle_id: ID!
success_count: Int! success_count: Int!

View File

@ -370,6 +370,7 @@ export const DASHBOARD_COURSE_STATISTICS = graphql(`
sessions { sessions {
id id
name name
region
} }
generations generations
circles { circles {
@ -390,6 +391,7 @@ export const DASHBOARD_COURSE_STATISTICS = graphql(`
_id _id
course_session_id course_session_id
generation generation
region
circle_id circle_id
due_date due_date
participants_present participants_present
@ -408,6 +410,7 @@ export const DASHBOARD_COURSE_STATISTICS = graphql(`
_id _id
course_session_id course_session_id
generation generation
region
circle_id circle_id
experts experts
satisfaction_average satisfaction_average
@ -436,6 +439,7 @@ export const DASHBOARD_COURSE_STATISTICS = graphql(`
course_session_assignment_id course_session_assignment_id
circle_id circle_id
generation generation
region
assignment_title assignment_title
assignment_type_translation_key assignment_type_translation_key
competence_certificate_title competence_certificate_title
@ -465,6 +469,7 @@ export const DASHBOARD_COURSE_STATISTICS = graphql(`
_id _id
course_session_id course_session_id
generation generation
region
circle_id circle_id
title title
success_count success_count
@ -490,6 +495,7 @@ export const DASHBOARD_MENTOR_COMPETENCE_SUMMARY = graphql(`
sessions { sessions {
id id
name name
region
} }
generations generations
circles { circles {
@ -510,8 +516,10 @@ export const DASHBOARD_MENTOR_COMPETENCE_SUMMARY = graphql(`
_id _id
course_session_id course_session_id
course_session_assignment_id course_session_assignment_id
course_session_title
circle_id circle_id
generation generation
region
assignment_title assignment_title
assignment_type_translation_key assignment_type_translation_key
competence_certificate_id competence_certificate_id

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import log from "loglevel"; import log from "loglevel";
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, type Ref, ref } from "vue";
import { import {
courseIdForCourseSlug, courseIdForCourseSlug,
fetchMentorCompetenceSummary, fetchMentorCompetenceSummary,
@ -10,6 +10,8 @@ import { useDashboardStore } from "@/stores/dashboard";
import LoadingSpinner from "@/components/ui/LoadingSpinner.vue"; import LoadingSpinner from "@/components/ui/LoadingSpinner.vue";
import _ from "lodash"; import _ from "lodash";
import { percentToRoundedGrade } from "@/services/assignmentService"; import { percentToRoundedGrade } from "@/services/assignmentService";
import StatisticFilterList from "@/components/dashboard/StatisticFilterList.vue";
import type { StatisticsFilterItem } from "@/types";
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
@ -32,6 +34,22 @@ const courseSessionName = (courseSessionId: string) => {
); );
}; };
const statisticFilter: Ref<typeof StatisticFilterList | null> = ref(null);
const filteredItems = computed(() => {
if (!statisticFilter.value) {
return [];
}
return statisticFilter.value.getFilteredItems();
});
const totalAverageGrade = computed(() => {
return percentToRoundedGrade(
_.sumBy(filteredItems.value, (i) => {
return (i as GroupedAssignmentEntry).averageEvaluationPercent ?? 0;
}) / filteredItems.value.length,
false
);
});
onMounted(async () => { onMounted(async () => {
await dashboardStore.loadDashboardDetails(); await dashboardStore.loadDashboardDetails();
courseId.value = courseIdForCourseSlug( courseId.value = courseIdForCourseSlug(
@ -53,26 +71,31 @@ onMounted(async () => {
loading.value = false; loading.value = false;
}); });
type GroupedAssignments = { interface GroupedAssignmentEntry extends StatisticsFilterItem {
competenceCertificateId: string; competenceCertificateId: string;
competenceCertificateTitle: string; competenceCertificateTitle: string;
generation: string; course_session_title: string;
courseSessionId: string;
assignments: AssignmentStatisticsRecordType[]; assignments: AssignmentStatisticsRecordType[];
sumAverageEvaluationPercent: number; sumAverageEvaluationPercent: number;
averageEvaluationPercent: number | null; averageEvaluationPercent: number | null;
averageGrade: number | null; averageGrade: number | null;
}; }
function isGroupedAssignmentEntry(
item: StatisticsFilterItem
): item is GroupedAssignmentEntry {
return (item as GroupedAssignmentEntry).competenceCertificateId !== undefined;
}
const courseSessionCompetenceAssignments = computed(() => { const courseSessionCompetenceAssignments = computed(() => {
let resultArray = [] as GroupedAssignments[]; let resultArray = [] as GroupedAssignmentEntry[];
// group assignments by competence and course session // group assignments by competence and course session
for (const assignment of agentAssignmentData.value?.assignments.records ?? []) { for (const assignment of agentAssignmentData.value?.assignments.records ?? []) {
const entry = resultArray.find( const entry = resultArray.find(
(r) => (r) =>
r.competenceCertificateId === assignment.competence_certificate_id && r.competenceCertificateId === assignment.competence_certificate_id &&
r.courseSessionId === assignment.course_session_id r.course_session_id === assignment.course_session_id
); );
if (entry) { if (entry) {
if (assignment.metrics.ranking_completed) { if (assignment.metrics.ranking_completed) {
@ -80,14 +103,18 @@ const courseSessionCompetenceAssignments = computed(() => {
} }
} else { } else {
const newEntry = { const newEntry = {
_id: `${assignment.competence_certificate_id}-${assignment.course_session_id}`,
competenceCertificateId: assignment.competence_certificate_id ?? "", competenceCertificateId: assignment.competence_certificate_id ?? "",
competenceCertificateTitle: assignment.competence_certificate_title ?? "", competenceCertificateTitle: assignment.competence_certificate_title ?? "",
generation: assignment.generation ?? "", generation: assignment.generation ?? "",
courseSessionId: assignment.course_session_id ?? "", region: assignment.region ?? "",
course_session_id: assignment.course_session_id ?? "",
course_session_title: assignment.course_session_title ?? "",
assignments: [] as AssignmentStatisticsRecordType[], assignments: [] as AssignmentStatisticsRecordType[],
sumAverageEvaluationPercent: 0, sumAverageEvaluationPercent: 0,
averageEvaluationPercent: null, averageEvaluationPercent: null,
averageGrade: null, averageGrade: null,
circle_id: assignment.circle_id ?? "",
}; };
if (assignment && assignment.metrics.ranking_completed) { if (assignment && assignment.metrics.ranking_completed) {
newEntry.assignments.push(assignment); newEntry.assignments.push(assignment);
@ -114,7 +141,11 @@ const courseSessionCompetenceAssignments = computed(() => {
}); });
entry.averageGrade = percentToRoundedGrade(entry.averageEvaluationPercent, false); entry.averageGrade = percentToRoundedGrade(entry.averageEvaluationPercent, false);
} }
return resultArray; return _.orderBy(
resultArray,
["course_session_title", "competenceCertificateTitle"],
["asc", "asc"]
);
}); });
</script> </script>
@ -129,48 +160,61 @@ const courseSessionCompetenceAssignments = computed(() => {
<span>{{ $t("general.back") }}</span> <span>{{ $t("general.back") }}</span>
</router-link> </router-link>
<div> <div>
<h2>{{ $t("a.Kompetenznachweise") }}</h2> <h2 class="mb-8">{{ $t("a.Kompetenznachweise") }}</h2>
<div class="bg-white px-4 py-2"> <div class="bg-white py-2">
<section <StatisticFilterList
class="flex flex-col space-x-0 border-b bg-white lg:flex-row lg:space-x-3" v-if="
></section> agentAssignmentData?.course_session_properties &&
<div courseSessionCompetenceAssignments?.length
v-for="entry in courseSessionCompetenceAssignments" "
:key="entry.courseSessionId" ref="statisticFilter"
:data-cy="`entry-${entry.courseSessionId}`" :course-session-properties="agentAssignmentData?.course_session_properties"
class="flex flex-col justify-between gap-4 border-b p-2 last:border-b-0 md:flex-row md:items-center md:justify-between md:gap-16" :items="courseSessionCompetenceAssignments"
:hide-circle-filter="true"
> >
<div class="w-full flex-auto md:w-1/2"> <template #header>
{{ entry.competenceCertificateTitle }} <div class="heading-3 border-b px-6 py-4">
<br /> {{ $t("a.Durchschnittsnote") }}: {{ totalAverageGrade }}
{{ $t("a.Durchführung") }} «{{ </div>
courseSessionName(entry.courseSessionId) </template>
}}» <template #default="{ item: item }">
</div> <div
v-if="isGroupedAssignmentEntry(item)"
<div class="flex flex-auto items-center gap-2 md:w-1/4"> class="flex flex-col justify-between gap-4 border-b last:border-b-0 md:flex-row md:items-center md:justify-between md:gap-16"
<div>{{ $t("a.Durchschnittsnote") }}:</div> >
<div class="min-w-12 text-center"> <div class="w-full flex-auto md:w-1/2">
<div <span class="text-bold">
class="rounded px-2 py-1 font-bold" {{ item.competenceCertificateTitle }}
:class="{ 'bg-red-400': (entry.averageGrade ?? 4) < 4 }" </span>
> <br />
{{ entry.averageGrade }} {{ $t("a.Durchführung") }} «{{
courseSessionName(item.course_session_id)
}}»
</div>
<div class="flex flex-auto items-center gap-2 md:w-1/4">
<div>{{ $t("a.Durchschnittsnote") }}:</div>
<div class="min-w-12 text-center">
<div
class="rounded px-2 py-1 font-bold"
:class="{ 'bg-red-400': (item.averageGrade ?? 4) < 4 }"
>
{{ item.averageGrade }}
</div>
</div>
</div>
<div class="w-full flex-auto items-end md:w-1/4 md:text-end">
<router-link
class="underline"
:to="`/statistic/${props.agentRole}/${props.courseSlug}/competence-grade/${item.course_session_id}/${item.competenceCertificateId}`"
data-cy="basebox.detailsLink"
>
{{ $t("a.Details anschauen") }}
</router-link>
</div> </div>
</div> </div>
</div> </template>
</StatisticFilterList>
<div class="w-full flex-auto items-end md:w-1/4 md:text-end">
<router-link
class="underline"
:to="`/statistic/${props.agentRole}/${props.courseSlug}/competence-grade/${entry.courseSessionId}/${entry.competenceCertificateId}`"
data-cy="basebox.detailsLink"
>
{{ $t("a.Details anschauen") }}
</router-link>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -623,6 +623,7 @@ export type DashboardPersonsPageMode = "default" | "competenceMetrics";
export interface StatisticsFilterItem { export interface StatisticsFilterItem {
_id: string; _id: string;
course_session_id: string; course_session_id: string;
region: string;
generation: string; generation: string;
circle_id: string; circle_id: string;
} }

View File

@ -36,9 +36,11 @@ class AssignmentStatisticsRecordType(graphene.ObjectType):
course_session_assignment_id = graphene.ID(required=True) course_session_assignment_id = graphene.ID(required=True)
circle_id = graphene.ID(required=True) circle_id = graphene.ID(required=True)
generation = graphene.String(required=True) generation = graphene.String(required=True)
region = graphene.String(required=True)
assignment_type_translation_key = graphene.String(required=True) assignment_type_translation_key = graphene.String(required=True)
assignment_title = graphene.String(required=True) assignment_title = graphene.String(required=True)
deadline = graphene.DateTime(required=True) deadline = graphene.DateTime(required=True)
course_session_title = graphene.String()
competence_certificate_id = graphene.ID() competence_certificate_id = graphene.ID()
competence_certificate_title = graphene.String() competence_certificate_title = graphene.String()
metrics = graphene.Field(AssignmentCompletionMetricsType, required=True) metrics = graphene.Field(AssignmentCompletionMetricsType, required=True)
@ -171,9 +173,11 @@ def create_record(
# 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}@{urql_id_postfix}", # noqa _id=f"{course_session_assignment._meta.model_name}#{course_session_assignment.id}@{urql_id_postfix}", # noqa
course_session_id=str(course_session_assignment.course_session.id), # noqa course_session_id=str(course_session_assignment.course_session.id), # noqa
course_session_title=course_session_assignment.course_session.title, # noqa
circle_id=learning_content.get_circle().id, # noqa circle_id=learning_content.get_circle().id, # noqa
course_session_assignment_id=str(course_session_assignment.id), # noqa course_session_assignment_id=str(course_session_assignment.id), # noqa
generation=course_session_assignment.course_session.generation, # noqa generation=course_session_assignment.course_session.generation, # noqa
region=course_session_assignment.course_session.region, # noqa
assignment_type_translation_key=due_date.assignment_type_translation_key, # noqa assignment_type_translation_key=due_date.assignment_type_translation_key, # noqa
competence_certificate_id=str(competence_certificate.id), # noqa competence_certificate_id=str(competence_certificate.id), # noqa
competence_certificate_title=competence_certificate.title, # noqa competence_certificate_title=competence_certificate.title, # noqa

View File

@ -19,6 +19,7 @@ class PresenceRecordStatisticsType(graphene.ObjectType):
_id = graphene.ID(required=True) _id = graphene.ID(required=True)
course_session_id = graphene.ID(required=True) course_session_id = graphene.ID(required=True)
generation = graphene.String(required=True) generation = graphene.String(required=True)
region = graphene.String(required=True)
circle_id = graphene.ID(required=True) circle_id = graphene.ID(required=True)
due_date = graphene.DateTime(required=True) due_date = graphene.DateTime(required=True)
participants_present = graphene.Int(required=True) participants_present = graphene.Int(required=True)
@ -83,6 +84,7 @@ def attendance_day_presences(
_id=f"{urql_id}: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
region=course_session.region, # noqa
circle_id=circle.id, # noqa circle_id=circle.id, # noqa
due_date=attendance_day.due_date.end, # noqa due_date=attendance_day.due_date.end, # noqa
participants_present=participants_present, # noqa participants_present=participants_present, # noqa
@ -98,7 +100,9 @@ def attendance_day_presences(
) )
return AttendanceDayPresencesStatisticsType( return AttendanceDayPresencesStatisticsType(
summary=summary, records=records, _id=course_id # noqa summary=summary,
records=records,
_id=course_id, # noqa
) )

View File

@ -16,6 +16,7 @@ class CompetenceRecordStatisticsType(graphene.ObjectType):
_id = graphene.ID(required=True) _id = graphene.ID(required=True)
course_session_id = graphene.ID(required=True) course_session_id = graphene.ID(required=True)
generation = graphene.String(required=True) generation = graphene.String(required=True)
region = graphene.String(required=True)
title = graphene.String(required=True) title = graphene.String(required=True)
circle_id = graphene.ID(required=True) circle_id = graphene.ID(required=True)
success_count = graphene.Int(required=True) success_count = graphene.Int(required=True)
@ -80,6 +81,7 @@ def competences(
title=learning_unit.title, # noqa title=learning_unit.title, # noqa
course_session_id=completion.course_session.id, # noqa course_session_id=completion.course_session.id, # noqa
generation=completion.course_session.generation, # noqa generation=completion.course_session.generation, # noqa
region=completion.course_session.region, # noqa
circle_id=circle.id, # noqa circle_id=circle.id, # noqa
success_count=0, # noqa success_count=0, # noqa
fail_count=0, # noqa fail_count=0, # noqa

View File

@ -26,6 +26,7 @@ from vbv_lernwelt.learnpath.models import Circle
class StatisticsCourseSessionDataType(graphene.ObjectType): class StatisticsCourseSessionDataType(graphene.ObjectType):
id = graphene.ID(required=True) id = graphene.ID(required=True)
name = graphene.String(required=True) name = graphene.String(required=True)
region = graphene.String(required=True)
class StatisticsCircleDataType(graphene.ObjectType): class StatisticsCircleDataType(graphene.ObjectType):
@ -129,6 +130,7 @@ class BaseStatisticsType(graphene.ObjectType):
StatisticsCourseSessionDataType( StatisticsCourseSessionDataType(
id=course_session.id, # noqa id=course_session.id, # noqa
name=course_session.title, # noqa name=course_session.title, # noqa
region=course_session.region, # noqa
) )
) )
generations.add(course_session.generation) generations.add(course_session.generation)
@ -199,7 +201,8 @@ class CourseStatisticsType(BaseStatisticsType):
records, success_total, fail_total = competences( records, success_total, fail_total = competences(
course_slug=str(root.course_slug), course_slug=str(root.course_slug),
course_session_selection_ids=[ course_session_selection_ids=[
str(cs) for cs in root.course_session_selection_ids # noqa str(cs)
for cs in root.course_session_selection_ids # noqa
], ],
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

View File

@ -18,6 +18,7 @@ class FeedbackStatisticsRecordType(graphene.ObjectType):
_id = graphene.ID(required=True) _id = graphene.ID(required=True)
course_session_id = graphene.ID(required=True) course_session_id = graphene.ID(required=True)
generation = graphene.String(required=True) generation = graphene.String(required=True)
region = graphene.String(required=True)
circle_id = graphene.ID(required=True) circle_id = graphene.ID(required=True)
satisfaction_average = graphene.Float(required=True) satisfaction_average = graphene.Float(required=True)
satisfaction_max = graphene.Int(required=True) satisfaction_max = graphene.Int(required=True)
@ -68,6 +69,7 @@ def feedback_responses(
feedbacks=fbs, feedbacks=fbs,
course_session_id=course_session.id, course_session_id=course_session.id,
generation=course_session.generation, generation=course_session.generation,
region=course_session.region,
course_slug=str(course_slug), course_slug=str(course_slug),
urql_id_postfix=urql_id, urql_id_postfix=urql_id,
) )
@ -96,6 +98,7 @@ def circle_feedback_average(
feedbacks: List[FeedbackResponse], feedbacks: List[FeedbackResponse],
course_session_id, course_session_id,
generation: str, generation: str,
region: str,
course_slug: str, course_slug: str,
urql_id_postfix: str = "", urql_id_postfix: str = "",
): ):
@ -128,6 +131,7 @@ def circle_feedback_average(
_id=f"circle:{circle_id}-course_session:{course_session_id}@{urql_id_postfix}", # 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
region=region, # noqa
circle_id=circle_id, # noqa circle_id=circle_id, # noqa
satisfaction_average=data["total"] / data["count"], # noqa satisfaction_average=data["total"] / data["count"], # noqa
satisfaction_max=4, # noqa satisfaction_max=4, # noqa