Skip to content
Snippets Groups Projects
Verified Commit 46a485e8 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Add first real queries for class register statistics

parent d05affe7
No related branches found
No related tags found
1 merge request!361Resolve "Add statistics page for absences"
Pipeline #190337 failed
......@@ -91,6 +91,9 @@ export default {
...term,
};
},
skip() {
return !this.schoolTerm;
},
},
},
methods: {
......
fragment statistics on StatisticsByPersonType {
schoolTerm
participationCount
absenceCount
absenceReasons {
......@@ -27,7 +26,7 @@ fragment statistics on StatisticsByPersonType {
}
}
query statisticsByPerson($person: ID!, $term: ID) {
query statisticsByPerson($person: ID!, $term: ID!) {
statistics: statisticsByPerson(person: $person, term: $term) {
...statistics
}
......
......@@ -10,10 +10,11 @@ from calendarweek import CalendarWeek
from aleksis.apps.alsijil.managers import PersonalNoteQuerySet
from aleksis.apps.chronos.models import Event, ExtraLesson, LessonPeriod
from aleksis.core.models import Group, Person
from aleksis.apps.kolego.models import AbsenceReason
from aleksis.core.models import Group, Person, SchoolTerm
from aleksis.core.util.core_helpers import get_site_preferences
from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
from .models import Documentation, ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
def alsijil_url(
......@@ -493,3 +494,81 @@ def generate_person_list_with_class_register_statistics(
)
return persons
def annotate_person_statistics(
persons: QuerySet[Person], participations_filter: Q, personal_notes_filter: Q
) -> QuerySet[Person]:
"""Annotate a queryset of persons with class register statistics."""
persons = persons.annotate(
filtered_participation_statuses=FilteredRelation(
"participations",
condition=(participations_filter),
),
filtered_personal_notes=FilteredRelation(
"new_personal_notes",
condition=(personal_notes_filter),
),
).annotate(
participation_count=Count(
"filtered_participation_statuses",
filter=Q(filtered_participation_statuses__absence_reason__isnull=True),
distinct=True,
),
absence_count=Count(
"filtered_participation_statuses",
filter=Q(filtered_participation_statuses__absence_reason__count_as_absent=True),
distinct=True,
),
# tardiness=Sum("filtered_participation_statuses__tardiness"),
# tardiness_count=Count(
# "filtered_personal_notes",
# filter=Q(filtered_personal_notes__tardiness__gt=0),
# distinct=True,
# ),
)
persons = persons.order_by("last_name", "first_name")
for absence_reason in AbsenceReason.objects.all():
persons = persons.annotate(
**{
absence_reason.count_label: Count(
"filtered_participation_statuses",
filter=Q(
filtered_participation_statuses__absence_reason=absence_reason,
),
distinct=True,
)
}
)
for extra_mark in ExtraMark.objects.all():
persons = persons.annotate(
**{
extra_mark.count_label: Count(
"filtered_personal_notes",
filter=Q(filtered_personal_notes__extra_mark=extra_mark),
distinct=True,
)
}
)
return persons
def annotate_person_statistics_for_school_term(
persons: QuerySet[Person], school_term: SchoolTerm
) -> QuerySet[Person]:
"""Annotate a queryset of persons with class register statistics for a school term."""
documentations = Documentation.objects.filter(
participations__person__in=persons,
datetime_start__date__gte=school_term.date_start,
datetime_end__date__lte=school_term.date_end,
)
docs = list(documentations.values_list("pk", flat=True))
return annotate_person_statistics(
persons,
Q(participations__related_documentation__in=docs),
Q(new_personal_notes__documentation__in=docs),
)
......@@ -8,11 +8,12 @@ import graphene
from aleksis.apps.chronos.models import LessonEvent
from aleksis.apps.cursus.models import Course
from aleksis.apps.cursus.schema import CourseType
from aleksis.core.models import Group, Person
from aleksis.core.models import Group, Person, SchoolTerm
from aleksis.core.schema.base import FilterOrderList
from aleksis.core.schema.group import GroupType
from aleksis.core.util.core_helpers import has_person
from ..model_extensions import annotate_person_statistics_for_school_term
from ..models import Documentation
from .absences import (
AbsencesBatchCreateMutation,
......@@ -58,7 +59,7 @@ class Query(graphene.ObjectType):
statistics_by_person = graphene.Field(
StatisticsByPersonType,
person=graphene.ID(required=True),
term=graphene.ID(required=False),
term=graphene.ID(required=True),
)
documentations_by_person = graphene.List(
DocumentationByPersonType,
......@@ -68,7 +69,7 @@ class Query(graphene.ObjectType):
statistics_by_group = graphene.List(
StatisticsByPersonType,
group=graphene.ID(required=True),
term=graphene.ID(required=False),
term=graphene.ID(required=True),
)
def resolve_documentations_by_course_id(root, info, course_id, **kwargs):
......@@ -190,9 +191,11 @@ class Query(graphene.ObjectType):
return lessons_for_person
@staticmethod
def resolve_statistics_by_person(root, info, person, term=None):
# TODO: Annotate person with necessary information for term.
return Person.objects.get(id=person)
def resolve_statistics_by_person(root, info, person, term):
school_term = SchoolTerm.objects.get(id=term)
return annotate_person_statistics_for_school_term(
Person.objects.filter(id=person), school_term
).first()
@staticmethod
def resolve_documentations_by_person(root, info, person, term=None):
......@@ -200,9 +203,11 @@ class Query(graphene.ObjectType):
return Person.objects.get(id=person)
@staticmethod
def resolve_statistics_by_group(root, info, group, term=None):
# TODO: Annotate persons with necessary information for term.
return Group.objects.get(id=group).members.all()
def resolve_statistics_by_group(root, info, group, term):
school_term = SchoolTerm.objects.get(id=term)
members = Group.objects.get(id=group).members.all()
return annotate_person_statistics_for_school_term(members, school_term)
class Mutation(graphene.ObjectType):
......
import graphene
from aleksis.apps.cursus.models import Subject
from aleksis.apps.cursus.schema import SubjectType
from aleksis.apps.kolego.models.absence import AbsenceReason
from aleksis.apps.kolego.schema.absence import AbsenceReasonType
......@@ -15,10 +16,10 @@ class AbsenceReasonWithCountType(graphene.ObjectType):
count = graphene.Int()
def resolve_absence_reason(root, info):
return root
return root["absence_reason"]
def resolve_count(root, info):
return 6
return root["count"]
class ExtraMarkWithCountType(graphene.ObjectType):
......@@ -26,14 +27,13 @@ class ExtraMarkWithCountType(graphene.ObjectType):
count = graphene.Int()
def resolve_extra_mark(root, info):
return root
return root["extra_mark"]
def resolve_count(root, info):
return 7
return root["count"]
class StatisticsByPersonType(graphene.ObjectType):
school_term = graphene.Int()
participation_count = graphene.Int()
absence_count = graphene.Int()
absence_reasons = graphene.List(AbsenceReasonWithCountType)
......@@ -41,19 +41,11 @@ class StatisticsByPersonType(graphene.ObjectType):
tardiness_count = graphene.Int()
extra_marks = graphene.List(ExtraMarkWithCountType)
def resolve_school_term(root, info):
return 4
def resolve_participation_count(root, info):
return 3
def resolve_absence_count(root, info):
return 1
def resolve_absence_reasons(root, info):
# TODO: Return actual AbsenceReasons
# Needed by resolve_absence_count as well.
return AbsenceReason.objects.all()
return [
dict(absence_reason=reason, count=getattr(root, reason.count_label))
for reason in AbsenceReason.objects.all()
]
def resolve_tardiness_sum(root, info):
return 17
......@@ -62,8 +54,10 @@ class StatisticsByPersonType(graphene.ObjectType):
return 5
def resolve_extra_marks(root, info):
# TODO: Return actual ExtraMarks
return ExtraMark.objects.all()
return [
dict(extra_mark=extra_mark, count=getattr(root, extra_mark.count_label))
for extra_mark in ExtraMark.objects.all()
]
class DocumentationByPersonType(graphene.ObjectType):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment