Skip to content
Snippets Groups Projects
views.py 10 KiB
Newer Older
from collections import OrderedDict
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, F, 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 _
from django.views.decorators.cache import cache_page
Nik | Klampfradler's avatar
Nik | Klampfradler committed

Tom Teichler's avatar
Tom Teichler committed
from biscuit.apps.chronos.models import LessonPeriod, Room, TimePeriod
from biscuit.apps.chronos.util import CalendarWeek, current_lesson_periods
mirabilos's avatar
mirabilos committed
from biscuit.core.models import Group, Person
from .forms import LessonDocumentationForm, PersonalNoteFormSet, SelectForm
from .models import LessonDocumentation, PersonalNote
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 = current_lesson_periods().filter(
            Q(substitutions__teachers=request.user.person) | Q(lesson__teachers=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.'))
Tom Teichler's avatar
Tom Teichler committed
    if datetime.combine(wanted_week[lesson_period.period.weekday - 1], lesson_period.period.time_start) > datetime.now():
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')

mirabilos's avatar
mirabilos committed
    # Find all persons in the associated groups that do not yet have a personal note for this lesson
    missing_persons = Person.objects.annotate(
        no_personal_notes=~Exists(PersonalNote.objects.filter(
            lesson_period=lesson_period,
            person__pk=OuterRef('pk')
        ))
    ).filter(
        member_of__in=Group.objects.filter(pk__in=lesson_period.lesson.groups.all()),
        is_active=True,
        no_personal_notes=True
    )

    # Create all missing personal notes
    PersonalNote.objects.bulk_create([
        PersonalNote(person=person, lesson_period=lesson_period,
                     week=wanted_week.week) for person in missing_persons  # FIXME Respect year as well
    ])

    # Create a formset that holds all personal notes for all persons in this lesson
    persons_qs = PersonalNote.objects.select_related('person').filter(
        lesson_period=lesson_period, week=wanted_week.week)  # FIXME Respect year as well
    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():
            personal_note_formset.save()

    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(
            has_documentation=Exists(LessonDocumentation.objects.filter(
                ~Q(topic__exact=''),
                lesson_period=OuterRef('pk'),
            lesson__date_start__lte=wanted_week[0] + timedelta(days=1) * (F('period__weekday') - 1),
            lesson__date_end__gte=wanted_week[0] + timedelta(days=1) * (F('period__weekday') - 1)
        ).select_related(
            'lesson', 'lesson__subject', 'period', 'room'
        ).prefetch_related(
            'lesson__groups', 'lesson__teachers', 'substitutions'
        ).extra(
            select={'_week': wanted_week.week}
        )

    teacher = None
    group = None
    room = None

    if request.GET.get('group', None) or request.GET.get('teacher', None) or request.GET.get('room', None):
        # Incrementally filter lesson periods by GET parameters
        if 'group' in request.GET and request.GET['group']:
            group = Group.objects.get(pk=request.GET['group'])
            lesson_periods = lesson_periods.filter(
                Q(lesson__groups__pk=int(request.GET['group'])) | Q(lesson__groups__parent_groups__pk=int(request.GET['group'])))
        if 'teacher' in request.GET and request.GET['teacher']:
            teacher = Person.objects.get(pk=request.GET['teacher'])
            lesson_periods = lesson_periods.filter(
                Q(substitutions__teachers__pk=int(request.GET['teacher']), substitutions__week=wanted_week.week) | Q(lesson__teachers__pk=int(request.GET['teacher'])))
        if 'room' in request.GET and request.GET['room']:
            room = Room.objects.get(pk=request.GET['room'])
            lesson_periods = lesson_periods.filter(
                room__pk=int(request.GET['room']))
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()
Tom Teichler's avatar
Tom Teichler committed
        lesson_periods = lesson_periods.filter(
            Q(lesson__groups__pk=int(request.GET['group'])) | Q(lesson__groups__parent_groups__pk=int(request.GET['group'])))

        # 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['group'] = group
    context['teacher'] = teacher
    context['room'] = room
    context['lesson_periods'] = lesson_periods
    context['persons'] = persons
    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.annotate(
        has_documentation=Exists(LessonDocumentation.objects.filter(
            ~Q(topic__exact=''),
            lesson_period=OuterRef('pk'),
        ))
    ).filter(
        lesson__date_start__gte=group.school.current_term.date_start,
        lesson__date_end__lte=group.school.current_term.date_end
    ).select_related(
        'lesson', 'lesson__subject', 'period', 'room'
    ).prefetch_related(
        'lesson__groups', 'lesson__teachers', 'substitutions', 'documentations'
    ).filter(
        Q(lesson__groups=group) | Q(lesson__groups__parent_groups=group)
    ).distinct()

    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))
    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)