Newer
Older
from datetime import datetime, timedelta
from typing import Optional, Sequence
from django import forms
from django.http import HttpRequest
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget, Select2Widget
from guardian.shortcuts import get_objects_for_user

Jonathan Weth
committed
from material import Fieldset, Layout, Row
from aleksis.apps.chronos.managers import TimetableType
from aleksis.apps.chronos.models import LessonPeriod, Subject, TimePeriod
from aleksis.core.forms import ActionForm, ListActionForm
from aleksis.core.models import Group, Person, SchoolTerm
from aleksis.core.util.core_helpers import get_site_preferences
from aleksis.core.util.predicates import check_global_permission
from .actions import (
delete_personal_note,
mark_as_excuse_type_generator,
mark_as_excused,
mark_as_unexcused,
send_request_to_check_entry,
)
from .models import (
ExcuseType,
ExtraMark,
GroupRole,
GroupRoleAssignment,
LessonDocumentation,
PersonalNote,
)
class LessonDocumentationForm(forms.ModelForm):
class Meta:
model = LessonDocumentation

Jonathan Weth
committed
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["homework"].label = _("Homework for the next lesson")
if (
self.instance.lesson_period
and get_site_preferences()["alsijil__allow_carry_over_same_week"]
):
self.fields["carry_over_week"] = forms.BooleanField(
label=_("Carry over data to all other lessons with the same subject in this week"),
initial=True,
required=False,
)
def save(self, **kwargs):
lesson_documentation = super(LessonDocumentationForm, self).save(commit=True)
if (
get_site_preferences()["alsijil__allow_carry_over_same_week"]
and self.cleaned_data["carry_over_week"]
and (
lesson_documentation.topic
or lesson_documentation.homework
or lesson_documentation.group_note
)
and lesson_documentation.lesson_period
):
lesson_documentation.carry_over_data(
LessonPeriod.objects.filter(lesson=lesson_documentation.lesson_period.lesson)
)

Jonathan Weth
committed
class PersonalNoteForm(forms.ModelForm):
class Meta:
model = PersonalNote
fields = ["absent", "tardiness", "excused", "excuse_type", "extra_marks", "remarks"]
person_name = forms.CharField(disabled=True)
def __init__(self, *args, **kwargs):
self.fields["person_name"].widget.attrs.update(
{"class": "alsijil-lesson-personal-note-name"}
)
self.fields["person_name"].widget = forms.HiddenInput()
if self.instance and getattr(self.instance, "person", None):
self.fields["person_name"].initial = str(self.instance.person)
class SelectForm(forms.Form):
group = forms.ModelChoiceField(
queryset=None,
label=_("Group"),
required=False,
widget=Select2Widget,
teacher = forms.ModelChoiceField(
queryset=None,
label=_("Teacher"),
required=False,
widget=Select2Widget,
def clean(self) -> dict:
data = super().clean()
if data.get("group") and not data.get("teacher"):
type_ = TimetableType.GROUP
instance = data["group"]
elif data.get("teacher") and not data.get("group"):
type_ = TimetableType.TEACHER
instance = data["teacher"]
elif not data.get("teacher") and not data.get("group"):
return data
else:
raise ValidationError(_("You can't select a group and a teacher both."))
data["type_"] = type_
data["instance"] = instance
return data
def __init__(self, request, *args, **kwargs):
self.request = request
super().__init__(*args, **kwargs)
group_qs = Group.get_groups_with_lessons()
# Filter selectable groups by permissions
if not check_global_permission(self.request.user, "alsijil.view_week"):
# 1) All groups the user is allowed to see the week view by object permissions
# 2) All groups the user is a member of an owner of

Hangzhi Yu
committed
# 3) If the corresponding preference is turned on:
# All groups that have a parent group the user is an owner of
group_qs.filter(
pk__in=get_objects_for_user(
self.request.user, "core.view_week_class_register_group", Group
).values_list("pk", flat=True)

Hangzhi Yu
committed
).union(
group_qs.filter(
Q(members=person) | Q(owners=person) | Q(parent_groups__owners=person)
if get_site_preferences()["alsijil__inherit_privileges_from_parent_group"]
else Q(members=person) | Q(owners=person)
)
)
# Flatten query by filtering groups by pk
self.fields["group"].queryset = Group.objects.filter(
pk__in=list(group_qs.values_list("pk", flat=True))
)
teacher_qs = Person.objects.annotate(lessons_count=Count("lessons_as_teacher")).filter(
lessons_count__gt=0
)
# Filter selectable teachers by permissions
if not check_global_permission(self.request.user, "alsijil.view_week"):

Hangzhi Yu
committed
# If the user hasn't got the global permission and the inherit privileges preference is
# turned off, the user is only allowed to see their own person. Otherwise, the user

Hangzhi Yu
committed
# is allowed to see all persons that teach lessons that the given groups attend.
if get_site_preferences()["alsijil__inherit_privileges_from_parent_group"]:
teacher_pks = []
for group in group_qs:
for lesson in group.lessons.all():
for teacher in lesson.teachers.all():
teacher_pks.append(teacher.pk)
teacher_qs = teacher_qs.filter(pk__in=teacher_pks)
else:
teacher_qs = teacher_qs.filter(pk=person.pk)
self.fields["teacher"].queryset = teacher_qs
PersonalNoteFormSet = forms.modelformset_factory(
PersonalNote, form=PersonalNoteForm, max_num=0, extra=0
)
class RegisterAbsenceForm(forms.Form):
Fieldset("", "person"),

Jonathan Weth
committed
Fieldset("", Row("date_start", "date_end"), Row("from_period", "to_period")),
Fieldset("", Row("absent", "excused"), Row("excuse_type"), Row("remarks")),
person = forms.ModelChoiceField(label=_("Person"), queryset=None, widget=Select2Widget)
date_start = forms.DateField(label=_("Start date"), initial=datetime.today)
date_end = forms.DateField(label=_("End date"), initial=datetime.today)

Jonathan Weth
committed
from_period = forms.ChoiceField(label=_("Start period"))
to_period = forms.ChoiceField(label=_("End period"))
absent = forms.BooleanField(label=_("Absent"), initial=True, required=False)
excused = forms.BooleanField(label=_("Excused"), initial=True, required=False)

Jonathan Weth
committed
excuse_type = forms.ModelChoiceField(
label=_("Excuse type"),
queryset=ExcuseType.objects.all(),
widget=Select2Widget,
required=False,
)
remarks = forms.CharField(label=_("Remarks"), max_length=30, required=False)
def __init__(self, request, *args, **kwargs):
self.request = request
super().__init__(*args, **kwargs)

Jonathan Weth
committed
period_choices = TimePeriod.period_choices
if self.request.user.has_perm("alsijil.register_absence"):
self.fields["person"].queryset = Person.objects.all()
else:
persons_qs = Person.objects.filter(
Q(
pk__in=get_objects_for_user(
self.request.user, "core.register_absence_person", Person
)
)
| Q(primary_group__owners=self.request.user.person)
| Q(
member_of__in=get_objects_for_user(
self.request.user, "core.register_absence_group", Group
)
)
).distinct()
self.fields["person"].queryset = persons_qs

Jonathan Weth
committed
self.fields["from_period"].choices = period_choices
self.fields["to_period"].choices = period_choices
self.fields["from_period"].initial = TimePeriod.period_min
self.fields["to_period"].initial = TimePeriod.period_max
class ExtraMarkForm(forms.ModelForm):
layout = Layout("short_name", "name")
model = ExtraMark
fields = ["short_name", "name"]
class ExcuseTypeForm(forms.ModelForm):
layout = Layout("short_name", "name", "count_as_absent")
class Meta:
model = ExcuseType
fields = ["short_name", "name", "count_as_absent"]
class PersonOverviewForm(ActionForm):
def get_actions(self):
return (
[mark_as_excused, mark_as_unexcused]
+ [
mark_as_excuse_type_generator(excuse_type)
for excuse_type in ExcuseType.objects.all()
]
+ [delete_personal_note]
)
layout = Layout("name", "icon", "colour")
class Meta:
class AssignGroupRoleForm(forms.ModelForm):
layout_base = ["groups", "person", "role", Row("date_start", "date_end")]
groups = forms.ModelMultipleChoiceField(
label=_("Group"),
required=True,
queryset=Group.objects.all(),
widget=ModelSelect2MultipleWidget(
model=Group,
search_fields=["name__icontains", "short_name__icontains"],
attrs={
"data-minimum-input-length": 0,
"class": "browser-default",
},
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
),
)
person = forms.ModelChoiceField(
label=_("Person"),
required=True,
queryset=Person.objects.all(),
widget=ModelSelect2Widget(
model=Person,
search_fields=[
"first_name__icontains",
"last_name__icontains",
"short_name__icontains",
],
attrs={"data-minimum-input-length": 0, "class": "browser-default"},
),
)
def __init__(self, request, *args, **kwargs):
self.request = request
initial = kwargs.get("initial", {})
# Build layout with or without groups field
base_layout = self.layout_base[:]
if "groups" in initial:
base_layout.remove("groups")
self.layout = Layout(*base_layout)
super().__init__(*args, **kwargs)
if "groups" in initial:
self.fields["groups"].required = False
# Filter persons and groups by permissions
if not self.request.user.has_perm("alsijil.assign_grouprole"): # Global permission
persons = Person.objects
if initial.get("groups"):
persons = persons.filter(member_of__in=initial["groups"])
if get_site_preferences()["alsijil__group_owners_can_assign_roles_to_parents"]:
persons = persons.filter(
Q(member_of__owners=self.request.user.person)
| Q(children__member_of__owners=self.request.user.person)
)
else:
persons = persons.filter(member_of__owners=self.request.user.person)

Jonathan Weth
committed
self.fields["person"].queryset = persons.distinct()

Jonathan Weth
committed
groups = (
Group.objects.for_current_school_term_or_all()

Hangzhi Yu
committed
.filter(
Q(owners=self.request.user.person)
| Q(parent_groups__owners=self.request.user.person)
if get_site_preferences()["alsijil__inherit_privileges_from_parent_group"]
else Q(owners=self.request.user.person)
)

Jonathan Weth
committed
.distinct()
)
self.fields["groups"].queryset = groups
def clean_groups(self):
"""Ensure that only permitted groups are used."""
return self.initial["groups"] if "groups" in self.initial else self.cleaned_data["groups"]
class Meta:
model = GroupRoleAssignment
fields = ["groups", "person", "role", "date_start", "date_end"]
class GroupRoleAssignmentEditForm(forms.ModelForm):
class Meta:
model = GroupRoleAssignment
fields = ["date_start", "date_end"]
class FilterRegisterObjectForm(forms.Form):
"""Form for filtering register objects in ``RegisterObjectTable``."""
layout = Layout(
Row("school_term", "date_start", "date_end"), Row("has_documentation", "group", "subject")
)
school_term = forms.ModelChoiceField(queryset=None, label=_("School term"))
has_documentation = forms.NullBooleanField(label=_("Has lesson documentation"))
group = forms.ModelChoiceField(queryset=None, label=_("Group"), required=False)
subject = forms.ModelChoiceField(queryset=None, label=_("Subject"), required=False)
date_start = forms.DateField(label=_("Start date"))
date_end = forms.DateField(label=_("End date"))
@classmethod
def get_initial(cls, has_documentation: Optional[bool] = None):
date_end = timezone.now().date()
date_start = date_end - timedelta(days=30)

Jonathan Weth
committed
school_term = SchoolTerm.current
# If there is no current school year, use last known school year.
if not school_term:
school_term = SchoolTerm.objects.all().last()
return {

Jonathan Weth
committed
"school_term": school_term,
"date_start": date_start,
"date_end": date_end,
"has_documentation": has_documentation,
}
def __init__(
self,
request: HttpRequest,
*args,
for_person: bool = True,
default_documentation: Optional[bool] = None,
groups: Optional[Sequence[Group]] = None,
**kwargs
):
self.request = request
person = self.request.user.person
kwargs["initial"] = self.get_initial(has_documentation=default_documentation)
super().__init__(*args, **kwargs)
self.fields["school_term"].queryset = SchoolTerm.objects.all()
if not groups and for_person:
groups = Group.objects.filter(
Q(lessons__teachers=person)
| Q(lessons__lesson_periods__substitutions__teachers=person)
| Q(events__teachers=person)
| Q(extra_lessons__teachers=person)

Jonathan Weth
committed
).distinct()
elif not for_person:
groups = Group.objects.all()
self.fields["group"].queryset = groups
# Filter subjects by selectable groups
subject_qs = Subject.objects.filter(
Q(lessons__groups__in=groups) | Q(extra_lessons__groups__in=groups)
).distinct()
self.fields["subject"].queryset = subject_qs
class RegisterObjectActionForm(ListActionForm):
"""Action form for managing register objects for use with ``RegisterObjectTable``."""
actions = [send_request_to_check_entry]