diff --git a/aleksis/apps/alsijil/model_extensions.py b/aleksis/apps/alsijil/model_extensions.py index 820a58c82b140e026e4a3e62ffda73e96ea2cbfb..7ab840196988b80bffa63c9c4d3a5aa20aedffef 100644 --- a/aleksis/apps/alsijil/model_extensions.py +++ b/aleksis/apps/alsijil/model_extensions.py @@ -26,12 +26,18 @@ def mark_absent( excuse_type: Optional[ExcuseType] = None, remarks: str = "", to_period: Optional[int] = None, + dry_run: bool = False, ): """Mark a person absent for all lessons in a day, optionally starting with a selected period number. This function creates `PersonalNote` objects for every `LessonPeriod` the person participates in on the selected day and marks them as absent/excused. + :param dry_run: With this activated, the function won't change any data + and just return the count of affected lessons + + :return: Count of affected lesson periods + ..note:: Only available when AlekSIS-App-Alsijil is installed. :Date: 2019-11-10 @@ -51,32 +57,39 @@ def mark_absent( 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: - sub = lesson_period.get_substitution() - if sub and sub.cancelled: - continue - - with reversion.create_revision(): - set_user(get_request().user) - personal_note, created = ( - PersonalNote.objects.select_related(None) - .prefetch_related(None) - .update_or_create( - person=self, - lesson_period=lesson_period, - week=wanted_week.week, - year=wanted_week.year, - defaults={"absent": absent, "excused": excused, "excuse_type": excuse_type,}, + if not dry_run: + for lesson_period in lesson_periods: + sub = lesson_period.get_substitution() + if sub and sub.cancelled: + continue + + with reversion.create_revision(): + set_user(get_request().user) + personal_note, created = ( + PersonalNote.objects.select_related(None) + .prefetch_related(None) + .update_or_create( + person=self, + lesson_period=lesson_period, + week=wanted_week.week, + year=wanted_week.year, + defaults={ + "absent": absent, + "excused": excused, + "excuse_type": excuse_type, + }, + ) ) - ) - personal_note.groups_of_person.set(self.member_of.all()) - - if remarks: - if personal_note.remarks: - personal_note.remarks += "; %s" % remarks - else: - personal_note.remarks = remarks - personal_note.save() + personal_note.groups_of_person.set(self.member_of.all()) + + if remarks: + if personal_note.remarks: + personal_note.remarks += "; %s" % remarks + else: + personal_note.remarks = remarks + personal_note.save() + + return lesson_periods.count() @LessonPeriod.method diff --git a/aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html b/aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html new file mode 100644 index 0000000000000000000000000000000000000000..449276cdc9781d2a5760e823e882e7582292eac9 --- /dev/null +++ b/aleksis/apps/alsijil/templates/alsijil/absences/register_confirm.html @@ -0,0 +1,84 @@ +{# -*- engine:django -*- #} +{% extends "core/base.html" %} +{% load material_form i18n static %} + +{% block browser_title %}{% blocktrans %}Confirm: Register absence{% endblocktrans %}{% endblock %} +{% block page_title %}{% blocktrans %}Confirm: Register absence{% endblocktrans %}{% endblock %} + +{% block content %} + <p class="flow-text"> + {% blocktrans %} + Do you really want to register the following absence? + {% endblocktrans %} + </p> + <div class="card"> + <div class="card-content"> + <div class="card-title"> + {{ person }} + </div> + <div class="collection"> + <div class="collection-item"> + <i class="material-icons left">date_range</i> + {{ form_data.date_start }}, {{ form_data.from_period }}. – {{ form_data.date_end }}, {{ form_data.to_period }}. + {% if form_data.date_start != form_data.date_end %} + <div class="alert warning"> + <div> + <i class="material-icons left">warning</i> + {% blocktrans %} + As the length of this absence is longer than one day, + please double check the correctness of your entry. + {% endblocktrans %} + </div> + </div> + {% endif %} + </div> + <div class="collection-item"> + <i class="material-icons left">list</i> + {% blocktrans with count=affected_lessons %} {{ count }} affected lessons {% endblocktrans %} + {% if affected_lessons == 0 %} + <div class="alert error"> + <div> + <i class="material-icons left">error</i> + {% blocktrans %} + There are no affected lessons. Registering this absence won't have any effect. + {% endblocktrans %} + </div> + </div> + {% endif %} + </div> + <div class="collection-item"> + <i class="material-icons left">label</i> + {% if form_data.absent %} + <span class="chip red white-text">{% trans "Absent" %}</span> + {% if form_data.excused and form_data.excuse_type %} + <span class="chip green white-text">{{ form_data.excuse_type.name }}</span> + {% elif form_data.excused %} + <span class="chip green white-text">{% trans "Excused" %}</span> + {% endif %} + {% else %} + {% trans "Reset status to 'not absent'" %} + {% endif %} + </div> + {% if form_data.remarks %} + <div class="collection-item"> + <i class="material-icons left">edit</i> + {{ form_data.remarks }} + </div> + {% endif %} + </div> + </div> + </div> + + <form method="post"> + {% csrf_token %} + <div class="hide"> + {% form form=form %}{% endform %} + </div> + <input type="hidden" name="confirmed" value="1"> + {% include "core/partials/save_button.html" %} + <a class="btn red waves-effect waves-light" href="{% url "register_absence" person.pk %}"> + <i class="material-icons left">cancel</i> + {% trans "Cancel" %} + </a> + </form> +{% endblock %} diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 5c0d38ca8bb239db6acd65e53b1d5b2600b35c8b..94ced39954a571ea25187d1ed454c651c17763bd 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -677,6 +677,8 @@ def register_absence(request: HttpRequest, id_: int) -> HttpResponse: register_absence_form = RegisterAbsenceForm(request.POST or None) if request.method == "POST" and register_absence_form.is_valid(): + confirmed = request.POST.get("confirmed", "0") == "1" + # Get data from form # person = register_absence_form.cleaned_data["person"] start_date = register_absence_form.cleaned_data["date_start"] @@ -689,18 +691,35 @@ def register_absence(request: HttpRequest, id_: int) -> HttpResponse: remarks = register_absence_form.cleaned_data["remarks"] # Mark person as absent + affected_count = 0 delta = end_date - start_date for i in range(delta.days + 1): 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_on_day, absent, excused, excuse_type, remarks, to_period_on_day, + affected_count += person.mark_absent( + day, + from_period_on_day, + absent, + excused, + excuse_type, + remarks, + to_period_on_day, + dry_run=not confirmed, ) - messages.success(request, _("The absence has been saved.")) - return redirect("overview_person", person.pk) + if not confirmed: + # Show confirmation page + context = {} + context["affected_lessons"] = affected_count + context["person"] = person + context["form_data"] = register_absence_form.cleaned_data + context["form"] = register_absence_form + return render(request, "alsijil/absences/register_confirm.html", context) + else: + messages.success(request, _("The absence has been saved.")) + return redirect("overview_person", person.pk) context["person"] = person context["register_absence_form"] = register_absence_form