diff --git a/aleksis/apps/alsijil/forms.py b/aleksis/apps/alsijil/forms.py index 445f4a2055948ecd4b73a2c27586bc20838af6e6..ca1e2e5bffa70497f3522cde05cbb264b81be51b 100644 --- a/aleksis/apps/alsijil/forms.py +++ b/aleksis/apps/alsijil/forms.py @@ -7,9 +7,10 @@ from django.utils.translation import gettext_lazy as _ from django_select2.forms import Select2Widget from guardian.shortcuts import get_objects_for_user -from material import Layout, Row +from material import Fieldset, Layout, Row from aleksis.apps.chronos.managers import TimetableType +from aleksis.apps.chronos.models import TimePeriod from aleksis.core.models import Group, Person from aleksis.core.util.predicates import check_global_permission @@ -99,23 +100,29 @@ PersonalNoteFormSet = forms.modelformset_factory( class RegisterAbsenceForm(forms.Form): layout = Layout( - Row("date_start", "date_end"), - Row("from_period"), - Row("absent", "excused"), - Row("person"), - Row("remarks"), + Fieldset("", "person"), + Fieldset("", Row("date_start", "date_end"), Row("from_period", "to_period")), + Fieldset("", Row("absent", "excused"), Row("excuse_type"), Row("remarks")), ) date_start = forms.DateField(label=_("Start date"), initial=datetime.today) date_end = forms.DateField(label=_("End date"), initial=datetime.today) - from_period = forms.IntegerField(label=_("From period"), initial=0, min_value=0) person = forms.ModelChoiceField(label=_("Person"), queryset=None, widget=Select2Widget) + 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) + 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, *args, **kwargs): self.request = kwargs.pop("request") super().__init__(*args, **kwargs) + period_choices = TimePeriod.period_choices if check_global_permission(self.request.user, "alsijil.register_absence"): self.fields["person"].queryset = Person.objects.all() else: @@ -130,6 +137,10 @@ class RegisterAbsenceForm(forms.Form): ) ) ) + 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): diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py index d840b273b6cc3ddd8b8cbc682219836247fffc03..ce997f31a12e056bf72aad7abe21242941e850fe 100644 --- a/aleksis/apps/alsijil/model_extensions.py +++ b/aleksis/apps/alsijil/model_extensions.py @@ -21,6 +21,7 @@ def mark_absent( excused: bool = False, excuse_type: Optional[ExcuseType] = None, remarks: str = "", + to_period: Optional[int] = None, ): """Mark a person absent for all lessons in a day, optionally starting with a selected period number. @@ -40,6 +41,9 @@ def mark_absent( period__period__gte=from_period ) + if to_period: + lesson_periods = lesson_periods.filter(period__period__lte=to_period) + # Create and update all personal notes for the discovered lesson periods for lesson_period in lesson_periods: personal_note, created = PersonalNote.objects.update_or_create( @@ -104,7 +108,10 @@ def get_personal_notes(self, persons: QuerySet, wanted_week: CalendarWeek): personal_note.groups_of_person.set(personal_note.person.member_of.all()) return PersonalNote.objects.select_related("person").filter( - lesson_period=self, week=wanted_week.week, year=wanted_week.year + lesson_period=self, + week=wanted_week.week, + year=wanted_week.year, + person__in=persons, ) diff --git a/aleksis/apps/alsijil/rules.py b/aleksis/apps/alsijil/rules.py index 79ea32519c451f158100da1f67d6ea6ffb3a1ec6..73d8879cf26c1f6e8281c41deaecced0d33a5547 100644 --- a/aleksis/apps/alsijil/rules.py +++ b/aleksis/apps/alsijil/rules.py @@ -10,17 +10,17 @@ from aleksis.core.util.predicates import ( from .util.predicates import ( has_any_object_absence, has_lesson_group_object_perm, - has_personal_note_group_perm, has_person_group_object_perm, + has_personal_note_group_perm, is_group_member, is_group_owner, is_lesson_parent_group_owner, is_lesson_participant, is_lesson_teacher, is_own_personal_note, + is_person_group_owner, is_personal_note_lesson_parent_group_owner, is_personal_note_lesson_teacher, - is_person_group_owner, ) # View lesson @@ -36,6 +36,15 @@ add_perm("alsijil.view_lesson", view_lesson_predicate) # View lesson in menu add_perm("alsijil.view_lesson_menu", has_person) +# View lesson personal notes +view_lesson_personal_notes_predicate = has_person & ( + has_global_perm("alsijil.view_personalnote") + | has_lesson_group_object_perm("core.view_personalnote_group") + | is_lesson_teacher + | is_lesson_parent_group_owner +) +add_perm("alsijil.view_lesson_personalnote", view_lesson_personal_notes_predicate) + # View personal note view_personal_note_predicate = has_person & ( has_global_perm("alsijil.view_personalnote") @@ -95,8 +104,7 @@ add_perm("alsijil.view_week_personalnote", view_week_personal_notes_predicate) # View register absence page view_register_absence_predicate = has_person & ( - has_global_perm("alsijil.register_absence") - | has_any_object_absence + has_global_perm("alsijil.register_absence") | has_any_object_absence ) add_perm("alsijil.view_register_absence", view_register_absence_predicate) diff --git a/aleksis/apps/alsijil/templates/alsijil/absences/register.html b/aleksis/apps/alsijil/templates/alsijil/absences/register.html index 908f3b32194889eb206244a30bf3b843a8193309..b14890e21438a6e452c4fa3e6c595f99d5b856dd 100644 --- a/aleksis/apps/alsijil/templates/alsijil/absences/register.html +++ b/aleksis/apps/alsijil/templates/alsijil/absences/register.html @@ -2,8 +2,8 @@ {% extends "core/base.html" %} {% load material_form i18n static %} -{% block browser_title %}{% blocktrans %}Manage absence{% endblocktrans %}{% endblock %} -{% block page_title %}{% blocktrans %}Manage absence{% endblocktrans %}{% endblock %} +{% block browser_title %}{% blocktrans %}Register absence{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Register absence{% endblocktrans %}{% endblock %} {% block content %} @@ -13,4 +13,12 @@ {% include "core/partials/save_button.html" %} </form> + <script> + $(document).ready(function () { + $("#id_date_start").change(function () { + $("#id_date_end").val($("#id_date_start").val()); + $("#id_date_end").change(); + }); + }); + </script> {% endblock %} diff --git a/aleksis/apps/alsijil/util/predicates.py b/aleksis/apps/alsijil/util/predicates.py index 6540aeb5dffd1368058a087da92c2d3bbf303f33..3e62d17a2a3f338fa7c8f3a94dd8eb6c2a8fa821 100644 --- a/aleksis/apps/alsijil/util/predicates.py +++ b/aleksis/apps/alsijil/util/predicates.py @@ -7,8 +7,8 @@ from rules import predicate from aleksis.apps.chronos.models import LessonPeriod from aleksis.core.models import Group, Person -from aleksis.core.util.predicates import check_object_permission from aleksis.core.util.core_helpers import get_site_preferences +from aleksis.core.util.predicates import check_object_permission from ..models import PersonalNote @@ -21,10 +21,10 @@ def is_lesson_teacher(user: User, obj: LessonPeriod) -> bool: in the lesson or the substitution linked to the given LessonPeriod. """ if obj: - return ( - user.person in obj.lesson.teachers.all() - or user.person in Person.objects.filter(lesson_substitutions=obj.get_substitution()) - ) + sub = obj.get_substitution() + if sub and sub in user.person.lesson_substitutions.all(): + return True + return user.person in obj.lesson.teachers.all() return True @@ -154,7 +154,10 @@ def is_own_personal_note(user: User, obj: PersonalNote) -> bool: Is configurable via dynamic preferences. """ if hasattr(obj, "person"): - if get_site_preferences()["alsijil__view_own_personal_notes"] and obj.person is user.person: + if ( + get_site_preferences()["alsijil__view_own_personal_notes"] + and obj.person is user.person + ): return True return False @@ -168,9 +171,8 @@ def is_personal_note_lesson_teacher(user: User, obj: PersonalNote) -> bool: """ if hasattr(obj, "lesson_period"): if hasattr(obj.lesson_period, "lesson"): - return ( - user.person in obj.lesson_period.lesson.teachers.all() - or user.person in Person.objects.filter(lesson_substitutions=obj.lesson_period.get_substitution()) + return user.person in obj.lesson_period.lesson.teachers.all() or user.person in Person.objects.filter( + lesson_substitutions=obj.lesson_period.get_substitution() ) return False return False @@ -186,7 +188,9 @@ def is_personal_note_lesson_parent_group_owner(user: User, obj: PersonalNote) -> """ if hasattr(obj, "lesson_period"): if hasattr(obj.lesson_period, "lesson"): - return obj.lesson_period.lesson.groups.filter(parent_groups__owners=user.person).exists() + return obj.lesson_period.lesson.groups.filter( + parent_groups__owners=user.person + ).exists() return False return False @@ -200,5 +204,7 @@ def has_any_object_absence(user: User) -> bool: return True if Person.objects.filter(member_of__owners=user.person).exists(): return True - if Person.objects.filter(member_of__in=get_objects_for_user(user, "core.register_absence_group", Group)).exists(): + if Person.objects.filter( + member_of__in=get_objects_for_user(user, "core.register_absence_group", Group) + ).exists(): return True diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 34ace3a0b599f7585c6723fa0529353509b6cb32..c9b1877cf55e767777263142e60ae51101ce7a34 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -14,7 +14,7 @@ from reversion.views import RevisionMixin from rules.contrib.views import PermissionRequiredMixin, permission_required from aleksis.apps.chronos.managers import TimetableType -from aleksis.apps.chronos.models import LessonPeriod +from aleksis.apps.chronos.models import LessonPeriod, TimePeriod from aleksis.apps.chronos.util.chronos_helpers import get_el_by_pk from aleksis.apps.chronos.util.date import get_weeks_for_year, week_weekday_to_date from aleksis.core.mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView @@ -107,9 +107,9 @@ def lesson( ) # Create a formset that holds all personal notes for all persons in this lesson - persons = Person.objects + persons = Person.objects.all() if not request.user.has_perm("alsijil.view_lesson_personalnote", lesson_period): - persons = persons.filter(pk=request.user.pk) + persons = persons.filter(pk=request.user.person.pk) persons_qs = lesson_period.get_personal_notes(persons, wanted_week) personal_note_formset = PersonalNoteFormSet( request.POST or None, queryset=persons_qs, prefix="personal_notes" @@ -485,19 +485,33 @@ def register_absence(request: HttpRequest) -> HttpResponse: 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"] + to_period = register_absence_form.cleaned_data["to_period"] absent = register_absence_form.cleaned_data["absent"] excused = register_absence_form.cleaned_data["excused"] + excuse_type = register_absence_form.cleaned_data["excuse_type"] remarks = register_absence_form.cleaned_data["remarks"] # Mark person as absent delta = end_date - start_date for i in range(delta.days + 1): - from_period = from_period if i == 0 else 0 + from_period_on_day = from_period if i == 0 else TimePeriod.period_min + to_period_on_day = ( + to_period if i == delta.days else TimePeriod.period_max + ) day = start_date + timedelta(days=i) - person.mark_absent(day, from_period, absent, excused, remarks) + + person.mark_absent( + day, + from_period_on_day, + absent, + excused, + excuse_type, + remarks, + to_period_on_day, + ) messages.success(request, _("The absence has been saved.")) - return redirect("index") + return redirect("register_absence") context["register_absence_form"] = register_absence_form