Skip to content
Snippets Groups Projects
views.py 10.2 KiB
Newer Older
Tom Teichler's avatar
Tom Teichler committed
from datetime import date, datetime, timedelta
from typing import Optional

Nik | Klampfradler's avatar
Nik | Klampfradler committed
from django.contrib.auth.decorators import login_required
Tom Teichler's avatar
Tom Teichler committed
from django.core.exceptions import PermissionDenied
from django.db.models import Count, Exists, OuterRef, Q, Sum
Tom Teichler's avatar
Tom Teichler committed
from django.http import Http404, HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
Nik | Klampfradler's avatar
Nik | Klampfradler committed
from django.urls import reverse
from django.utils.translation import ugettext as _
Nik | Klampfradler's avatar
Nik | Klampfradler committed

from biscuit.apps.chronos.models import LessonPeriod
from biscuit.apps.chronos.util import CalendarWeek
mirabilos's avatar
mirabilos committed
from biscuit.core.models import Group, Person
Tom Teichler's avatar
Tom Teichler committed
from biscuit.core.decorators import admin_required
Tom Teichler's avatar
Tom Teichler committed
from biscuit.core.util import messages
from .forms import LessonDocumentationForm, PersonalNoteFormSet, RegisterAbsenceForm, SelectForm
from .models import LessonDocumentation, PersonalNoteFilter
Nik | Klampfradler's avatar
Nik | Klampfradler committed

@login_required
def lesson(request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None, period_id: Optional[int] = None) -> HttpResponse:
    if year and week and period_id:
Nik | Klampfradler's avatar
Nik | Klampfradler committed
        # Get a specific lesson period if provided in URL
        lesson_period = LessonPeriod.objects.get(pk=period_id)
        wanted_week = CalendarWeek(year=year, week=week)
Nik | Klampfradler's avatar
Nik | Klampfradler committed
        # Determine current lesson by current date and time
        lesson_period = LessonPeriod.objects.at_time().filter_teacher(request.user.person).first()
        wanted_week = CalendarWeek()
        if lesson_period:
            return redirect('lesson_by_week_and_period', wanted_week.year, wanted_week.week, lesson_period.pk)
        else:
            raise Http404(_('You either selected an invalid lesson or there is currently no lesson in progress.'))
    if datetime.combine(wanted_week[lesson_period.period.weekday - 1], lesson_period.period.time_start) > datetime.now() and not request.user.is_superuser:
Tom Teichler's avatar
Tom Teichler committed
        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='leson_documentation')


    # Create a formset that holds all personal notes for all persons in this lesson
Nik | Klampfradler's avatar
Nik | Klampfradler committed
    persons_qs = lesson_period.get_personal_notes(wanted_week)
    personal_note_formset = PersonalNoteFormSet(
        request.POST or None, queryset=persons_qs, prefix='personal_notes')

    if request.method == 'POST':
        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,
                    instance.excused
                )
    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)
Tom Teichler's avatar
Tom Teichler committed
def week_view(request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None) -> HttpResponse:
    if year and week:
        wanted_week = CalendarWeek(year=year, week=week)
    else:
        wanted_week = CalendarWeek()

    lesson_periods = LessonPeriod.objects.annotate(
Nik | Klampfradler's avatar
Nik | Klampfradler committed
        has_documentation=Exists(LessonDocumentation.objects.filter(
            ~Q(topic__exact=''),
            lesson_period=OuterRef('pk'),
            week=wanted_week.week
        ))
    ).in_week(wanted_week)
    group = None  # FIXME workaround for #38
    if request.GET.get('group', None) or request.GET.get('teacher', None) or request.GET.get('room', None):
        lesson_periods = lesson_periods.filter_from_query(request.GET)
        if 'group' in request.GET and request.GET['group']:
            group = Group.objects.get(pk=request.GET['group'])
        else:
            group = None
Tom Teichler's avatar
Tom Teichler committed
    elif hasattr(request, 'user') and hasattr(request.user, 'person'):
Tom Teichler's avatar
Tom Teichler committed
        group = request.user.person.owner_of.first()
        if group:
            lesson_periods = lesson_periods.filter_group(group)
        elif request.user.person.lessons_as_teacher.exists():
            lesson_periods = lesson_periods.filter_teacher(request.user.person)
        else:
            lesson_periods = lesson_periods.filter_participant(request.user.person)
Nik | Klampfradler's avatar
Nik | Klampfradler committed
    else:
        lesson_periods = None
Nik | Klampfradler's avatar
Nik | Klampfradler committed
    if lesson_periods:
        # Aggregate all personal notes for this group and week
        persons = Person.objects.filter(
            is_active=True
        ).filter(
            Q(member_of=group) | Q(member_of__parent_groups=group)
            absences=Count('personal_notes__absent', filter=Q(
                personal_notes__lesson_period__in=lesson_periods,
                personal_notes__week=wanted_week.week,
                personal_notes__absent=True
            )),
            unexcused=Count('personal_notes__absent', filter=Q(
                personal_notes__lesson_period__in=lesson_periods,
                personal_notes__week=wanted_week.week,
                personal_notes__absent=True,
                personal_notes__excused=False
            )),
            tardiness=Sum('personal_notes__late', filter=Q(
                personal_notes__lesson_period__in=lesson_periods,
                personal_notes__week=wanted_week.week
    # Add a form to filter the view
    select_form = SelectForm(request.GET or None)

    context['current_head'] = str(wanted_week)
    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
Tom Teichler's avatar
Tom Teichler committed
    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())
Tom Teichler's avatar
Tom Teichler committed
    return render(request, 'alsijil/week_view.html', context)


@login_required
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(group.school.current_term.date_start, group.school.current_term.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 and lesson_period.lesson.date_end >= day:
                documentations = list(filter(lambda d: d.week == week.week, lesson_period.documentations.all()))
                notes = list(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(
Nik | Klampfradler's avatar
Nik | Klampfradler committed
        absences=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')
    )
    # 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)
            )}
    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)
Tom Teichler's avatar
Tom Teichler committed


@admin_required
def register_absence(request: HttpRequest) -> HttpResponse:
Tom Teichler's avatar
Tom Teichler committed
    context = {}

    register_absence_form = RegisterAbsenceForm(request.POST or None)
Tom Teichler's avatar
Tom Teichler committed

    if request.method == 'POST':
        if register_absence_form.is_valid():
Tom Teichler's avatar
Tom Teichler committed
            # Get data from form
            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']
            starting_lesson = register_absence_form.cleaned_data['starting_lesson']
            absent = register_absence_form.cleaned_data['absent']
            excused = register_absence_form.cleaned_data['excused']
            remarks = register_absence_form.cleaned_data['remarks']
Tom Teichler's avatar
Tom Teichler committed
            # Mark person as absent
            delta = end_date - start_date
Tom Teichler's avatar
Tom Teichler committed
            for i in range(delta.days+1):
                starting_period = starting_lesson if i == 0 else 0
Tom Teichler's avatar
Tom Teichler committed
                day = start_date + timedelta(days=i)
Nik | Klampfradler's avatar
Nik | Klampfradler committed
                person.mark_absent(day, starting_period, absent, excused, remarks)
Tom Teichler's avatar
Tom Teichler committed

            messages.success(request, _('The absence has been saved.'))
Tom Teichler's avatar
Tom Teichler committed
            return redirect('index')
    context['register_absence_form'] = register_absence_form
    return render(request, 'alsijil/register_absence.html', context)