From f85f91b55d9326868d6f81e2ae2ed4b3cadde7ae Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Thu, 13 Aug 2020 15:32:39 +0200
Subject: [PATCH] Add person overview

---
 aleksis/apps/alsijil/models.py                |   3 +
 .../alsijil/class_register/person.html        | 144 ++++++++++++++++++
 .../alsijil/class_register/week_view.html     |   7 +-
 aleksis/apps/alsijil/urls.py                  |   2 +
 aleksis/apps/alsijil/views.py                 |  82 +++++++++-
 5 files changed, 234 insertions(+), 4 deletions(-)
 create mode 100644 aleksis/apps/alsijil/templates/alsijil/class_register/person.html

diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index 0f0e72bb8..c21c138bd 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -69,6 +69,9 @@ class PersonalNote(ExtensibleModel):
     def save(self, *args, **kwargs):
         if self.excuse_type:
             self.excused = True
+        if not self.absent:
+            self.excused = False
+            self.excuse_type = None
         super().save(*args, **kwargs)
 
     class Meta:
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/person.html b/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
new file mode 100644
index 000000000..e73234c1f
--- /dev/null
+++ b/aleksis/apps/alsijil/templates/alsijil/class_register/person.html
@@ -0,0 +1,144 @@
+{# -*- engine:django -*- #}
+{% extends "core/base.html" %}
+{% load week_helpers %}
+{% load i18n %}
+
+{% block browser_title %}{% blocktrans %}Class register: person{% endblocktrans %}{% endblock %}
+
+
+{% block page_title %}
+  {% blocktrans with person=person %}
+    Class register overview for {{ person }}
+  {% endblocktrans %}
+{% endblock %}
+
+{% block content %}
+  <div class="row">
+  <div class="col s12 m12 l6">
+    <h5>{% trans "Unexcused absences" %}</h5>
+
+    <ul class="collection">
+      {% for note in unexcused_absences %}
+        {% period_to_date note.week note.lesson_period.period as note_date %}
+        <li class="collection-item">
+          <i class="material-icons left red-text">warning</i>
+          <p class="no-margin">{{ note_date }}, {{ note.lesson_period }}</p>
+          {% if note.remarks %}
+            <p class="no-margin"><em>{{ note.remarks }}</em></p>
+          {% endif %}
+        </li>
+      {% empty %}
+        <li class="collection-item flow-text">
+          {% trans "There are unexcused lessons." %}
+        </li>
+      {% endfor %}
+    </ul>
+  </div>
+  <div class="col s12 m12 l6">
+    <h5>{% trans "Relevant personal notes" %}</h5>
+    <ul class="collapsible">
+      <li>
+        <div>
+          <ul>
+            {% for note in personal_notes %}
+              {% ifchanged note.lesson_period.lesson.validity.school_term %}</ul></div></li>
+                <li {% if forloop.first %}class="active"{% endif %}>
+                <div class="collapsible-header"><i
+                        class="material-icons">date_range</i>{{ note.lesson_period.lesson.validity.school_term }}</div>
+                <div class="collapsible-body">
+                <ul class="collection">
+              {% endifchanged %}
+
+              {% ifchanged note.week %}
+                <li class="collection-item">
+                  <strong>{% blocktrans with week=note.week %}Week {{ week }}{% endblocktrans %}</strong>
+                </li>
+              {% endifchanged %}
+
+              {% period_to_date note.week note.lesson_period.period as note_date %}
+              {% ifchanged note_date %}
+                <li class="collection-item">
+                  <form action="" method="post" class="right" style="margin-top: -7px;">
+                    {% csrf_token %}
+                    {% trans "Mark all as" %}
+                    <input type="hidden" value="{{ note_date|date:"Y-m-d" }}" name="date">
+                    <button type="submit" class="btn-flat" name="excuse_type" value="e">
+                      {% trans "e" %}
+                    </button>
+                    {% for excuse_type in excuse_types %}
+                      <button type="submit" class="btn-flat" value="{{ excuse_type.pk }}" name="excuse_type">
+                        {{ excuse_type.short_name }}
+                      </button>
+                    {% endfor %}
+                  </form>
+                  <i class="material-icons left">schedule</i>
+                  {{ note_date }}
+
+
+                </li>
+              {% endifchanged %}
+
+              <li class="collection-item">
+                <div class="row no-margin">
+                  <div class="col s12 m1">
+                    {{ note.lesson_period.period.period }}.
+                  </div>
+
+                  <div class="col s12 m4">
+                    <i class="material-icons left">event_note</i>
+                    {{ note.lesson_period.get_subject.name }},
+                    {{ note.lesson_period.get_teacher_names }}
+                  </div>
+
+                  <div class="col s12 m7">
+                    {% if note.absent and not note.excused %}
+                      <form action="" method="post" class="right" style="margin-top: -7px;">
+                        {% csrf_token %}
+                        {% trans "Mark all as" %}
+                        <input type="hidden" value="{{ note.pk }}" name="personal_note">
+                        <button type="submit" class="btn-flat" name="excuse_type" value="e">
+                          {% trans "e" %}
+                        </button>
+                        {% for excuse_type in excuse_types %}
+                          <button type="submit" class="btn-flat" value="{{ excuse_type.pk }}" name="excuse_type">
+                            {{ excuse_type.short_name }}
+                          </button>
+                        {% endfor %}
+                      </form>
+                    {% endif %}
+
+                    {% if note.absent %}
+                      <div class="chip red white-text">
+                        {% trans 'Absent' %}
+                      </div>
+                    {% endif %}
+                    {% if note.excused %}
+                      <div class="chip green white-text">
+                        {% if note.excuse_type %}
+                          {{ note.excuse_type.name }}
+                        {% else %}
+                          {% trans 'Excused' %}
+                        {% endif %}
+                      </div>
+                    {% endif %}
+
+                    {% if note.late %}
+                      <div class="chip orange white-text">
+                        {% blocktrans with late=note.late %}{{ late }}' late{% endblocktrans %}
+                      </div>
+                    {% endif %}
+
+                    {% for extra_mark in note.extra_marks.all %}
+                      <div class="chip">{{ extra_mark.name }}</div>
+                    {% endfor %}
+
+                    <em>{{ note.remarks }}</em>
+
+                  </div>
+              </li>
+            {% endfor %}
+            </li>
+            </ul>
+            </div>
+  </div>
+{% endblock %}
diff --git a/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html b/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html
index d0761f810..ea549f7f1 100644
--- a/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html
+++ b/aleksis/apps/alsijil/templates/alsijil/class_register/week_view.html
@@ -35,7 +35,8 @@
 
 
   <div class="row">
-    <h4 class="col s12 m6">{% blocktrans with el=el week=week.week %}CW {{ week }}: {{ instance }}{% endblocktrans %} </h4>
+    <h4 class="col s12 m6">{% blocktrans with el=el week=week.week %}CW {{ week }}:
+      {{ instance }}{% endblocktrans %} </h4>
     {% include "chronos/partials/week_select.html" with wanted_week=week %}
   </div>
 
@@ -111,7 +112,9 @@
               {% blocktrans %}Personal notes{% endblocktrans %}
             </span>
             {% for person in persons %}
-              <h5 class="card-title">{{ person.person.full_name }}</h5>
+              <h5 class="card-title">
+                <a href="{% url "overview_person" person.person.pk %}">{{ person.person.full_name }}</a>
+              </h5>
               <p class="card-text">
                 {% trans "Absent" %}: {{ person.person.absences_count }}
                 ({{ person.person.unexcused_count }} {% trans "unexcused" %})
diff --git a/aleksis/apps/alsijil/urls.py b/aleksis/apps/alsijil/urls.py
index 7e7139c6c..2fc61a1c2 100644
--- a/aleksis/apps/alsijil/urls.py
+++ b/aleksis/apps/alsijil/urls.py
@@ -26,6 +26,8 @@ urlpatterns = [
     path(
         "print/group/<int:id_>", views.full_register_group, name="full_register_group"
     ),
+    path("person/<int:id_>/", views.overview_person, name="overview_person"),
+    path("me/", views.overview_person, name="overview_me"),
     path("absence/new", views.register_absence, name="register_absence"),
     path("extra_marks/", views.ExtraMarkListView.as_view(), name="extra_marks"),
     path(
diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py
index 9c90ede28..5a471b20a 100644
--- a/aleksis/apps/alsijil/views.py
+++ b/aleksis/apps/alsijil/views.py
@@ -20,7 +20,7 @@ from aleksis.apps.chronos.util.date import get_weeks_for_year, week_weekday_to_d
 from aleksis.core.mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView
 from aleksis.core.models import Group, Person, SchoolTerm
 from aleksis.core.util import messages
-from aleksis.core.util.core_helpers import get_site_preferences
+from aleksis.core.util.core_helpers import get_site_preferences, objectgetter_optional
 
 from .forms import (
     ExcuseTypeForm,
@@ -30,7 +30,7 @@ from .forms import (
     RegisterAbsenceForm,
     SelectForm,
 )
-from .models import ExcuseType, ExtraMark, LessonDocumentation
+from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote
 from .tables import ExcuseTypeTable, ExtraMarkTable
 
 
@@ -445,6 +445,84 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
     return render(request, "alsijil/print/full_register.html", context)
 
 
+def overview_person(request: HttpRequest, id_: Optional[int] = None) -> HttpResponse:
+    context = {}
+    person = objectgetter_optional(Person, default_eval="request.user.person")(
+        request, id_
+    )
+    context["person"] = person
+
+    if request.method == "POST":
+        if request.POST.get("excuse_type"):
+            # Get excuse type
+            excuse_type = request.POST["excuse_type"]
+            found = False
+            if excuse_type == "e":
+                excuse_type = None
+                found = True
+            else:
+                try:
+                    excuse_type = ExcuseType.objects.get(pk=int(excuse_type))
+                    found = True
+                except (ExcuseType.DoesNotExist, ValueError):
+                    pass
+
+            if found:
+                if request.POST.get("date"):
+                    # Mark absences on date as excused
+                    try:
+                        date = datetime.strptime(
+                            request.POST["date"], "%Y-%m-%d"
+                        ).date()
+
+                        notes = person.personal_notes.filter(
+                            week=date.isocalendar()[1],
+                            lesson_period__period__weekday=date.weekday(),
+                            lesson_period__lesson__validity__date_start__lte=date,
+                            lesson_period__lesson__validity__date_end__gte=date,
+                            absent=True,
+                            excused=False,
+                        )
+                        notes.update(excused=True, excuse_type=excuse_type)
+                        messages.success(
+                            request, _("The absences have been marked as excused.")
+                        )
+                    except ValueError:
+                        pass
+                elif request.POST.get("personal_note"):
+                    # Mark specific absence as excused
+                    try:
+                        note = PersonalNote.objects.get(
+                            pk=int(request.POST["personal_note"])
+                        )
+                        if note.absent:
+                            note.excused = True
+                            note.excuse_type = excuse_type
+                            note.save()
+                            messages.success(
+                                request, _("The absence has been marked as excused.")
+                            )
+                    except (PersonalNote.DoesNotExist, ValueError):
+                        pass
+
+                person.refresh_from_db()
+
+    unexcused_absences = person.personal_notes.filter(absent=True, excused=False)
+    context["unexcused_absences"] = unexcused_absences
+
+    personal_notes = person.personal_notes.filter(
+        Q(absent=True) | Q(late__gt=0) | ~Q(remarks="") | Q(extra_marks__isnull=False)
+    ).order_by(
+        "-lesson_period__lesson__validity__date_start",
+        "-week",
+        "lesson_period__period__weekday",
+        "lesson_period__period__period",
+    )
+    context["personal_notes"] = personal_notes
+    context["excuse_types"] = ExcuseType.objects.all()
+    return render(request, "alsijil/class_register/person.html", context)
+
+
 def register_absence(request: HttpRequest) -> HttpResponse:
     context = {}
 
-- 
GitLab