Newer
Older
from typing import Optional
from aleksis.apps.chronos.managers import TimetableType
from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk
from django.db.models import Count, Exists, OuterRef, Q, Sum
from django.http import Http404, HttpRequest, HttpResponse, HttpResponseNotFound
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import ugettext as _
from django_tables2 import RequestConfig
from rules.contrib.views import permission_required
from aleksis.apps.chronos.models import LessonPeriod
from aleksis.core.models import Group, Person, SchoolYear
from aleksis.core.util import messages
from aleksis.core.util.core_helpers import objectgetter_optional
from .forms import (
LessonDocumentationForm,
PersonalNoteFilterForm,
PersonalNoteFormSet,
RegisterAbsenceForm,
SelectForm,
)

Nik | Klampfradler
committed
from .models import LessonDocumentation, PersonalNoteFilter
from .tables import PersonalNoteFilterTable
from .util.alsijil_helpers import get_lesson_period_by_pk, get_lesson_periods_by_pk
@permission_required("alsijil.view_lesson", fn=get_lesson_period_by_pk)
def lesson(
request: HttpRequest,
year: Optional[int] = None,
week: Optional[int] = None,
period_id: Optional[int] = None,
) -> HttpResponse:
context = {}
lesson_period, wanted_week = get_lesson_period_by_pk(request, year, week, period_id)
if not (year and week and period_id):
if lesson_period:
"lesson_by_week_and_period", wanted_week.year, wanted_week.week, lesson_period.pk,
raise Http404(
_(
"You either selected an invalid lesson or there is currently no lesson in progress."
)
)
wanted_week[lesson_period.period.weekday - 1], lesson_period.period.time_start,
)
> datetime.now()
and not request.user.is_superuser
):
raise PermissionDenied(
_("You are not allowed to create a lesson documentation for a lesson in the future.")
context["lesson_period"] = lesson_period
context["week"] = wanted_week
context["day"] = wanted_week[lesson_period.period.weekday - 1]
# Create or get lesson documentation object; can be empty when first opening lesson
lesson_documentation, created = LessonDocumentation.objects.get_or_create(
lesson_period=lesson_period, week=wanted_week.week
)
lesson_documentation_form = LessonDocumentationForm(
request.POST or None, instance=lesson_documentation, prefix="lesson_documentation",
# Create a formset that holds all personal notes for all persons in this lesson
persons_qs = lesson_period.get_personal_notes(wanted_week)
personal_note_formset = PersonalNoteFormSet(
request.POST or None, queryset=persons_qs, prefix="personal_notes"
)
if lesson_documentation_form.is_valid():
lesson_documentation_form.save()
if personal_note_formset.is_valid():
instances = personal_note_formset.save()
# Iterate over personal notes and carry changed absences to following lessons
for instance in instances:
instance.person.mark_absent(
wanted_week[lesson_period.period.weekday - 1],
lesson_period.period.period + 1,
instance.absent,
context["lesson_documentation"] = lesson_documentation
context["lesson_documentation_form"] = lesson_documentation_form
context["personal_note_formset"] = personal_note_formset
return render(request, "alsijil/lesson.html", context)
@permission_required("alsijil.view_week", fn=get_lesson_periods_by_pk)
request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None, type_: Optional[str] = None, id_: Optional[int] = None
context = {}
lesson_periods, wanted_week, type_, instance = get_lesson_periods_by_pk(request, year, week, type_, id_)
# Add a form to filter the view
if type_:
initial = {type_.value: instance}
else:
initial = {}
select_form = SelectForm(request.POST or None, initial=initial)
if request.method == "POST":
if select_form.is_valid():
if "type_" not in select_form.cleaned_data:
return redirect("week_view_by_week", wanted_week.year, wanted_week.week)
else:
return redirect("week_view_by_week", wanted_week.year, wanted_week.week,
select_form.cleaned_data["type_"].value, select_form.cleaned_data["instance"].pk)
if type_ == TimetableType.GROUP:
group = instance
else:
group = None
# Aggregate all personal notes for this group and week
lesson_periods_pk = lesson_periods.values_list("pk", flat=True)
persons = (
Person.objects.filter(is_active=True)
.filter(member_of__lessons__lesson_periods__in=lesson_periods_pk)
.distinct()
.prefetch_related("personal_notes")
.annotate(
"personal_notes__absent",
filter=Q(
personal_notes__week=wanted_week.week,
personal_notes__absent=True,
),
),
"personal_notes__absent",
filter=Q(
personal_notes__week=wanted_week.week,
personal_notes__absent=True,
personal_notes__excused=False,
),
),
"personal_notes__late",
filter=Q(
personal_notes__week=wanted_week.week,
),
),
)
)
else:
persons = None
# Resort lesson periods manually because an union queryset doesn't support order_by
lesson_periods = sorted(lesson_periods, key=lambda x: (x.period.weekday, x.period.period))
context["week"] = wanted_week
context["lesson_periods"] = lesson_periods
context["persons"] = persons
context["group"] = group
context["select_form"] = select_form
week_prev = wanted_week - 1
week_next = wanted_week + 1
context["url_prev"] = "%s?%s" % (
reverse("week_view_by_week", args=[week_prev.year, week_prev.week]),
request.GET.urlencode(),
)
context["url_next"] = "%s?%s" % (
reverse("week_view_by_week", args=[week_next.year, week_next.week]),
request.GET.urlencode(),
)
return render(request, "alsijil/week_view.html", context)
@permission_required("alsijil.full_register_group", fn=objectgetter_optional(Group, None, False))
def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
context = {}
group = get_object_or_404(Group, pk=id_)
# Get all lesson periods for the selected group
lesson_periods = (
LessonPeriod.objects.filter_group(group)
.distinct()
.prefetch_related("documentations", "personal_notes")
weeks = CalendarWeek.weeks_within(
SchoolYear.current.date_start,
SchoolYear.current.date_end,
)
periods_by_day = {}
for lesson_period in lesson_periods:
for week in weeks:
day = week[lesson_period.period.weekday - 1]
if lesson_period.lesson.date_start <= day <= lesson_period.lesson.date_end:
filter(lambda d: d.week == week.week, lesson_period.documentations.all(),)
filter(lambda d: d.week == week.week, lesson_period.personal_notes.all(),)
substitution = lesson_period.get_substitution(week.week)
periods_by_day.setdefault(day, []).append(
(lesson_period, documentations, notes, substitution)
)
persons = group.members.annotate(
absences_count=Count("personal_notes__absent", filter=Q(personal_notes__absent=True)),
unexcused=Count(
"personal_notes__absent",
filter=Q(personal_notes__absent=True, personal_notes__excused=False),
),
tardiness=Sum("personal_notes__late"),

Nik | Klampfradler
committed
# FIXME Move to manager
personal_note_filters = PersonalNoteFilter.objects.all()
for personal_note_filter in personal_note_filters:
persons = persons.annotate(
**{
"_personal_notes_with_%s"
% personal_note_filter.identifier: Count(
"personal_notes__remarks",
filter=Q(personal_notes__remarks__iregex=personal_note_filter.regex),

Nik | Klampfradler
committed
)
context["persons"] = persons
context["personal_note_filters"] = personal_note_filters
context["group"] = group
context["weeks"] = weeks
context["periods_by_day"] = periods_by_day
context["today"] = date.today()
return render(request, "alsijil/print/full_register.html", context)
@permission_required("alsijil.register_absence")
def register_absence(request: HttpRequest) -> HttpResponse:
register_absence_form = RegisterAbsenceForm(request.POST or None)
if register_absence_form.is_valid():
person = register_absence_form.cleaned_data["person"]
start_date = register_absence_form.cleaned_data["date_start"]
end_date = register_absence_form.cleaned_data["date_end"]
from_period = register_absence_form.cleaned_data["from_period"]
absent = register_absence_form.cleaned_data["absent"]
excused = register_absence_form.cleaned_data["excused"]
remarks = register_absence_form.cleaned_data["remarks"]
# Mark person as absent
delta = end_date - start_date
from_period = from_period if i == 0 else 0
person.mark_absent(day, from_period, absent, excused, remarks)
messages.success(request, _("The absence has been saved."))
return redirect("index")
context["register_absence_form"] = register_absence_form
return render(request, "alsijil/register_absence.html", context)
@permission_required("alsijil.list_personal_note_filters")
def list_personal_note_filters(request: HttpRequest) -> HttpResponse:
context = {}
personal_note_filters = PersonalNoteFilter.objects.all()
# Prepare table
personal_note_filters_table = PersonalNoteFilterTable(personal_note_filters)
RequestConfig(request).configure(personal_note_filters_table)
context["personal_note_filters_table"] = personal_note_filters_table
return render(request, "alsijil/personal_note_filters.html", context)
@permission_required("alsijil.edit_personal_note_filter", fn=objectgetter_optional(PersonalNoteFilter, None, False))
def edit_personal_note_filter(request: HttpRequest, id: Optional["int"] = None) -> HttpResponse:
context = {}
if id:
personal_note_filter = PersonalNoteFilter.objects.get(id=id)
context["personal_note_filter"] = personal_note_filter
personal_note_filter_form = PersonalNoteFilterForm(
request.POST or None, instance=personal_note_filter
)
personal_note_filter_form = PersonalNoteFilterForm(request.POST or None)
if request.method == "POST":
if personal_note_filter_form.is_valid():
personal_note_filter_form.save(commit=True)
messages.success(request, _("The filter has been saved"))
return redirect("list_personal_note_filters")
context["personal_note_filter_form"] = personal_note_filter_form
return render(request, "alsijil/manage_personal_note_filter.html", context)
@permission_required("alsijil.delete_personal_note_filter", fn=objectgetter_optional(PersonalNoteFilter, None, False))
def delete_personal_note_filter(request: HttpRequest, id_: int) -> HttpResponse:
context = {}
personal_note_filter = get_object_or_404(PersonalNoteFilter, pk=id_)
PersonalNoteFilter.objects.filter(pk=id_).delete()
messages.success(request, _("The filter has been deleted."))
context["personal_note_filter"] = personal_note_filter
return redirect("list_personal_note_filters")