tasks.py 6.57 KiB
from datetime import date
from typing import List, Optional
from django.db.models import Prefetch, Q
from django.utils.translation import gettext as _
from celery.result import allow_join_result
from celery.states import SUCCESS
from aleksis.apps.cursus.models import Course
from aleksis.apps.kolego.models.absence import AbsenceReason
from aleksis.core.models import Group, PDFFile
from aleksis.core.util.celery_progress import ProgressRecorder, recorded_task
from aleksis.core.util.pdf import generate_pdf_from_template
from .model_extensions import annotate_person_statistics_from_documentations
from .models import Documentation, ExtraMark, NewPersonalNote, ParticipationStatus
@recorded_task
def generate_full_register_printout(
groups: List[int],
file_object: int,
recorder: ProgressRecorder,
include_cover: Optional[bool] = True,
include_abbreviations: Optional[bool] = True,
include_members_table: Optional[bool] = True,
include_teachers_and_subjects_table: Optional[bool] = True,
include_person_overviews: Optional[bool] = True,
include_coursebook: Optional[bool] = True,
):
"""Generate a configurable register printout as PDF for a group."""
def prefetch_notable_participations(select_related=None, prefetch_related=None):
if not select_related:
select_related = []
if not prefetch_related:
prefetch_related = []
return Prefetch(
"participations",
to_attr="notable_participations",
queryset=ParticipationStatus.objects.filter(
Q(absence_reason__tags__short_name="class_register") | Q(tardiness__isnull=False)
)
.select_related("absence_reason", *select_related)
.prefetch_related(*prefetch_related),
)
def prefetch_personal_notes(name, select_related=None, prefetch_related=None):
if not select_related:
select_related = []
if not prefetch_related:
prefetch_related = []
return Prefetch(
name,
queryset=NewPersonalNote.objects.filter(Q(note__gt="") | Q(extra_mark__isnull=False))
.select_related("extra_mark", *select_related)
.prefetch_related(*prefetch_related),
)
context = {}
context["include_cover"] = include_cover
context["include_abbreviations"] = include_abbreviations
context["include_members_table"] = include_members_table
context["include_teachers_and_subjects_table"] = include_teachers_and_subjects_table
context["include_person_overviews"] = include_person_overviews
context["include_coursebook"] = include_coursebook
context["today"] = date.today()
_number_of_steps = 5 + len(groups)
recorder.set_progress(1, _number_of_steps, _("Loading data ..."))
groups = Group.objects.filter(pk__in=groups).order_by("name")
if include_cover:
groups = groups.select_related("school_term")
if include_abbreviations or include_members_table:
context["absence_reasons"] = AbsenceReason.objects.filter(
tags__short_name="class_register", count_as_absent=True
)
context["absence_reasons_not_counted"] = AbsenceReason.objects.filter(
tags__short_name="class_register", count_as_absent=False
)
context["extra_marks"] = ExtraMark.objects.all()
if include_members_table or include_person_overviews:
groups = groups.prefetch_related("members")
if include_teachers_and_subjects_table:
groups = groups.prefetch_related(
Prefetch("courses", queryset=Course.objects.select_related("subject")),
"courses__teachers",
"child_groups",
Prefetch("child_groups__courses", queryset=Course.objects.select_related("subject")),
"child_groups__courses__teachers",
)
recorder.set_progress(2, _number_of_steps, _("Loading groups ..."))
for i, group in enumerate(groups, start=1):
recorder.set_progress(
2 + i, _number_of_steps, _(f"Loading group {group.short_name or group.name} ...")
)
if include_members_table or include_person_overviews or include_coursebook:
documentations = Documentation.objects.filter(
Q(datetime_start__date__gte=group.school_term.date_start)
& Q(datetime_end__date__lte=group.school_term.date_end)
& Q(
pk__in=Documentation.objects.filter(course__groups=group)
.values_list("pk", flat=True)
.union(
Documentation.objects.filter(
course__groups__parent_groups=group
).values_list("pk", flat=True)
)
)
)
if include_members_table or include_person_overviews:
group.members_with_stats = annotate_person_statistics_from_documentations(
group.members.all(), documentations
)
if include_person_overviews:
doc_query_set = documentations.select_related("subject").prefetch_related("teachers")
group.members_with_stats = group.members_with_stats.prefetch_related(
prefetch_notable_participations(
prefetch_related=[Prefetch("related_documentation", queryset=doc_query_set)]
),
prefetch_personal_notes(
"new_personal_notes",
prefetch_related=[Prefetch("documentation", queryset=doc_query_set)],
),
)
if include_teachers_and_subjects_table:
group.as_list = [group]
if include_coursebook:
group.documentations = documentations.order_by(
"datetime_start"
).prefetch_related(
prefetch_notable_participations(select_related=["person"]),
prefetch_personal_notes("personal_notes", select_related=["person"]),
)
context["groups"] = groups
recorder.set_progress(3 + len(groups), _number_of_steps, _("Generating template ..."))
file_object, result = generate_pdf_from_template(
"alsijil/print/register_for_group.html",
context,
file_object=PDFFile.objects.get(pk=file_object),
)
recorder.set_progress(4 + len(groups), _number_of_steps, _("Generating PDF ..."))
with allow_join_result():
result.wait()
file_object.refresh_from_db()
if not result.status == SUCCESS and file_object.file:
raise Exception(_("PDF generation failed"))
recorder.set_progress(5 + len(groups), _number_of_steps)