WIP: Filter statistics by circle

This commit is contained in:
Christian Cueni 2024-04-15 13:48:56 +02:00
parent 121f7c227a
commit 68d44f950f
9 changed files with 63 additions and 7 deletions

View File

@ -1,14 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { useDashboardStore } from "@/stores/dashboard"; import { useDashboardStore } from "@/stores/dashboard";
import { computed } from "vue"; import { computed, onMounted } from "vue";
import AttendanceSummaryBox from "@/components/dashboard/AttendanceSummaryBox.vue"; import AttendanceSummaryBox from "@/components/dashboard/AttendanceSummaryBox.vue";
import type { CourseStatisticsType } from "@/gql/graphql"; import type { CourseStatisticsType } from "@/gql/graphql";
import AssignmentSummaryBox from "@/components/dashboard/AssignmentSummaryBox.vue"; import AssignmentSummaryBox from "@/components/dashboard/AssignmentSummaryBox.vue";
import FeedbackSummaryBox from "@/components/dashboard/FeedbackSummaryBox.vue"; import FeedbackSummaryBox from "@/components/dashboard/FeedbackSummaryBox.vue";
import CompetenceSummaryBox from "@/components/dashboard/CompetenceSummaryBox.vue"; import CompetenceSummaryBox from "@/components/dashboard/CompetenceSummaryBox.vue";
const dashboardStore = useDashboardStore(); const props = defineProps<{
courseId: string;
}>();
const dashboardStore = useDashboardStore();
const statistics = computed(() => { const statistics = computed(() => {
return dashboardStore.currentDashBoardData as CourseStatisticsType; return dashboardStore.currentDashBoardData as CourseStatisticsType;
}); });
@ -28,6 +31,10 @@ const competenceSummary = computed(() => {
const feebackSummary = computed(() => { const feebackSummary = computed(() => {
return statistics.value.feedback_responses.summary; return statistics.value.feedback_responses.summary;
}); });
onMounted(async () => {
await dashboardStore.loadStatisticsData(props.courseId);
});
</script> </script>
<template> <template>
@ -47,7 +54,7 @@ const feebackSummary = computed(() => {
/> />
</div> </div>
<div <div
class="flex flex-col flex-wrap gap-x-5 border-b border-gray-300 pb-8 align-top last:border-0 md:flex-row" class="flex flex-col flex-wrap gap-x-5 border-b border-gray-300 align-top last:border-0 md:flex-row"
> >
<FeedbackSummaryBox <FeedbackSummaryBox
:feedback-count="feebackSummary.total_responses" :feedback-count="feebackSummary.total_responses"

View File

@ -72,6 +72,12 @@ export const useDashboardStore = defineStore("dashboard", () => {
} }
}; };
const loadStatisticsData = async (id: string) => {
const data = await fetchStatisticData(id);
dashBoardDataCache[id] = data;
currentDashBoardData.value = data;
};
return { return {
dashboardConfigs, dashboardConfigs,
currentDashboardConfig, currentDashboardConfig,
@ -80,5 +86,6 @@ export const useDashboardStore = defineStore("dashboard", () => {
loadDashboardDetails, loadDashboardDetails,
currentDashBoardData, currentDashBoardData,
loading, loading,
loadStatisticsData,
}; };
}); });

View File

@ -26,6 +26,7 @@ from vbv_lernwelt.iam.permissions import (
can_view_course_session_progress, can_view_course_session_progress,
) )
from vbv_lernwelt.learning_mentor.models import LearningMentor from vbv_lernwelt.learning_mentor.models import LearningMentor
from vbv_lernwelt.learnpath.models import Circle
class DashboardQuery(graphene.ObjectType): class DashboardQuery(graphene.ObjectType):
@ -56,9 +57,25 @@ class DashboardQuery(graphene.ObjectType):
course_session_ids.update( course_session_ids.update(
group.course_session.all().values_list("id", flat=True) group.course_session.all().values_list("id", flat=True)
) )
course_session_ids = set()
# let's assume that users are not supervisors in one region/group and trainer in another
if not course_session_ids: if not course_session_ids:
return None circle_ids = set()
circle_ids.update(
Circle.objects.filter(
expert__user=user, expert__role=CourseSessionUser.Role.EXPERT
).values_list("id", flat=True)
)
course_session_ids = CourseSession.objects.filter(
course=course,
coursesessionuser__user=user,
coursesessionuser__role=CourseSessionUser.Role.EXPERT,
).values_list("id", flat=True)
setattr(info.context, "circle_ids", list(circle_ids))
# get trainer courses & circles
# return None
return CourseStatisticsType( return CourseStatisticsType(
_id=course.id, # noqa _id=course.id, # noqa

View File

@ -161,6 +161,7 @@ def assignments(
course_id: graphene.ID(required=True), course_id: graphene.ID(required=True),
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,
) -> AssignmentsStatisticsType: ) -> AssignmentsStatisticsType:
course_sessions = CourseSession.objects.filter( course_sessions = CourseSession.objects.filter(
id__in=course_session_selection_ids, id__in=course_session_selection_ids,
@ -175,6 +176,8 @@ def assignments(
], ],
learning_content__content_assignment__competence_certificate__isnull=False, learning_content__content_assignment__competence_certificate__isnull=False,
): ):
if circle_ids and csa.learning_content.get_circle().id not in circle_ids:
continue
record = create_record( record = create_record(
course_session_assignment=csa, user_selection_ids=user_selection_ids course_session_assignment=csa, user_selection_ids=user_selection_ids
) )
@ -184,6 +187,8 @@ def assignments(
course_session=course_session, course_session=course_session,
learning_content__content_assignment__competence_certificate__isnull=False, learning_content__content_assignment__competence_certificate__isnull=False,
): ):
if circle_ids and cset.learning_content.get_circle().id not in circle_ids:
continue
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
) )

View File

@ -37,6 +37,7 @@ class AttendanceDayPresencesStatisticsType(graphene.ObjectType):
def attendance_day_presences( 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,
) -> AttendanceDayPresencesStatisticsType: ) -> AttendanceDayPresencesStatisticsType:
completed = CourseSessionAttendanceCourse.objects.filter( completed = CourseSessionAttendanceCourse.objects.filter(
course_session_id__in=course_session_selection_ids, course_session_id__in=course_session_selection_ids,
@ -48,6 +49,9 @@ def attendance_day_presences(
for attendance_day in completed: for attendance_day in completed:
circle = attendance_day.learning_content.get_parent_circle() circle = attendance_day.learning_content.get_parent_circle()
if circle_ids and circle.id not in circle_ids:
continue
course_session = attendance_day.course_session course_session = attendance_day.course_session
url = f"/course/{course_session.course.slug}/cockpit/attendance?id={attendance_day.learning_content.id}&courseSessionId={course_session.id}" url = f"/course/{course_session.course.slug}/cockpit/attendance?id={attendance_day.learning_content.id}&courseSessionId={course_session.id}"
@ -85,7 +89,7 @@ def attendance_day_presences(
summary = AttendanceSummaryStatisticsType( summary = AttendanceSummaryStatisticsType(
_id=course_id, # noqa _id=course_id, # noqa
days_completed=completed.count(), # noqa days_completed=len(records), # noqa
participants_present=calculate_avg_participation(records), # noqa participants_present=calculate_avg_participation(records), # noqa
) )

View File

@ -35,6 +35,7 @@ def competences(
course_session_selection_ids: List[str], course_session_selection_ids: List[str],
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,
) -> 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,
@ -54,7 +55,8 @@ def competences(
circles = { circles = {
lu.id: c lu.id: c
for lu in learning_units.values() for lu in learning_units.values()
if lu is not None and (c := lu.get_circle()) is not None if (lu is not None and (c := lu.get_circle()) is not None)
and (circle_ids is None or c.id in circle_ids)
} }
for completion in completions: for completion in completions:
@ -65,6 +67,9 @@ def competences(
circle = circles.get(learning_unit.id) circle = circles.get(learning_unit.id)
if not circle:
continue
combined_id = f"{circle.id}-{completion.course_session.id}" combined_id = f"{circle.id}-{completion.course_session.id}"
competence_records.setdefault(combined_id, {}).setdefault( competence_records.setdefault(combined_id, {}).setdefault(

View File

@ -106,8 +106,12 @@ class BaseStatisticsType(graphene.ObjectType):
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,
user_selection_ids=user_selection_ids, user_selection_ids=user_selection_ids,
circle_ids=root.get_circle_ids(_info),
) )
def get_circle_ids(self, info):
return getattr(info.context, "circle_ids", None)
class CourseStatisticsType(BaseStatisticsType): class CourseStatisticsType(BaseStatisticsType):
course_session_properties = graphene.Field( course_session_properties = graphene.Field(
@ -128,6 +132,7 @@ class CourseStatisticsType(BaseStatisticsType):
return attendance_day_presences( return attendance_day_presences(
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),
) )
def resolve_feedback_responses(root, info) -> FeedbackStatisticsResponsesType: def resolve_feedback_responses(root, info) -> FeedbackStatisticsResponsesType:
@ -135,6 +140,7 @@ class CourseStatisticsType(BaseStatisticsType):
course_session_selection_ids=root.course_session_selection_ids, course_session_selection_ids=root.course_session_selection_ids,
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),
) )
def resolve_competences(root, info) -> CompetencesStatisticsType: def resolve_competences(root, info) -> CompetencesStatisticsType:
@ -149,6 +155,7 @@ class CourseStatisticsType(BaseStatisticsType):
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
) )
return CompetencesStatisticsType( return CompetencesStatisticsType(
_id=root._id, # noqa _id=root._id, # noqa

View File

@ -37,6 +37,7 @@ def feedback_responses(
course_session_selection_ids: graphene.List(graphene.ID), course_session_selection_ids: graphene.List(graphene.ID),
course_id: graphene.ID, course_id: graphene.ID,
course_slug: graphene.String, course_slug: graphene.String,
circle_ids: List[graphene.ID] = None,
) -> FeedbackStatisticsResponsesType: ) -> FeedbackStatisticsResponsesType:
# 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(
@ -53,6 +54,9 @@ def feedback_responses(
feedback_user__in=feedback_users(course_session.id), feedback_user__in=feedback_users(course_session.id),
) )
if circle_ids and len(circle_ids):
fbs = fbs.filter(circle__id__in=circle_ids)
total_responses += len(fbs) total_responses += len(fbs)
circle_feedbacks.extend( circle_feedbacks.extend(

View File

@ -242,7 +242,7 @@ def get_widgets_for_course(
if is_uk: if is_uk:
widgets.append(WidgetType.COMPETENCE_CERTIFICATE_WIDGET.value) widgets.append(WidgetType.COMPETENCE_CERTIFICATE_WIDGET.value)
if role_key == RoleKeyType.SUPERVISOR: if role_key in [RoleKeyType.SUPERVISOR, RoleKeyType.TRAINER] and is_uk:
widgets.append(WidgetType.UK_STATISTICS_WIDGET.value) widgets.append(WidgetType.UK_STATISTICS_WIDGET.value)
if is_mentor: if is_mentor: