From e04cd0de489266a5eb6f3fa7e27278fef4dfbe24 Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Wed, 12 Feb 2020 18:03:09 +0100
Subject: [PATCH] Add header box with affected teachers and groups in
 substitution views

- On/off switchable via constance
- Included in substitutions.html and substitutions_print.html
- Add extra QuerySet functions (affected_lessons, affected_groups, affected_teachers)
---
 aleksis/apps/chronos/models.py                | 15 +++++++++
 aleksis/apps/chronos/settings.py              | 14 ++++++--
 .../chronos/static/css/chronos/timetable.css  |  4 +++
 .../templates/chronos/partials/headerbox.html | 33 +++++++++++++++++++
 .../templates/chronos/substitutions.html      | 17 +---------
 .../chronos/substitutions_print.html          | 15 +--------
 aleksis/apps/chronos/views.py                 |  9 +++--
 7 files changed, 72 insertions(+), 35 deletions(-)
 create mode 100644 aleksis/apps/chronos/templates/chronos/partials/headerbox.html

diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index 0f9c5f9e..82edaf53 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -205,6 +205,21 @@ class LessonSubstitutionQuerySet(LessonDataQuerySet):
     _period_path = "lesson_period__"
     _subst_path = ""
 
+    def affected_lessons(self):
+        """ Return all lessons which are affected by selected substitutions """
+
+        return Lesson.objects.filter(lesson_periods__substitutions__in=self)
+
+    def affected_teachers(self):
+        """ Return all teachers which are affected by selected substitutions (as substituted or substituting) """
+
+        return Person.objects.filter(Q(lessons_as_teacher__in=self.affected_lessons()) | Q(lesson_substitutions__in=self))
+
+    def affected_groups(self):
+        """ Return all groups which are affected by selected substitutions """
+
+        return Group.objects.filter(lessons__in=self.affected_lessons())
+
 
 class TimePeriod(models.Model):
     WEEKDAY_CHOICES = list(enumerate(i18n_day_names_lazy()))
diff --git a/aleksis/apps/chronos/settings.py b/aleksis/apps/chronos/settings.py
index 1ae08d71..2c861857 100644
--- a/aleksis/apps/chronos/settings.py
+++ b/aleksis/apps/chronos/settings.py
@@ -1,8 +1,18 @@
 from django.utils.translation import gettext_lazy as _
 
 CONSTANCE_CONFIG = {
-    "CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER": (2, _("Number of days shown on substitutions print view")),
+    "CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER": (
+        2,
+        _("Number of days shown on substitutions print view"),
+    ),
+    "CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX": (
+        True,
+        _("The header box shows affected teachers/groups."),
+    ),
 }
 CONSTANCE_CONFIG_FIELDSETS = {
-    "Chronos settings": ("CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER",),
+    "Chronos settings": (
+        "CHRONOS_SUBSTITUTIONS_PRINT_DAY_NUMBER",
+        "CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX",
+    ),
 }
diff --git a/aleksis/apps/chronos/static/css/chronos/timetable.css b/aleksis/apps/chronos/static/css/chronos/timetable.css
index 32427258..16846c60 100644
--- a/aleksis/apps/chronos/static/css/chronos/timetable.css
+++ b/aleksis/apps/chronos/static/css/chronos/timetable.css
@@ -108,3 +108,7 @@ table.substitutions td, table.substitutions th {
     margin: 2px;
     letter-spacing: 0.3pt;
 }
+
+.black-text-a a {
+    color: black;
+}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/headerbox.html b/aleksis/apps/chronos/templates/chronos/partials/headerbox.html
new file mode 100644
index 00000000..bc5bd0f3
--- /dev/null
+++ b/aleksis/apps/chronos/templates/chronos/partials/headerbox.html
@@ -0,0 +1,33 @@
+{% load i18n %}
+
+{% if affected_teachers and affected_groups %}
+  <div class="{% if not print %}card{% endif %}">
+    <div class="{% if not print %}card-content{% endif %}">
+      {% if affected_teachers %}
+        <div class="row no-margin">
+          <div class="col s12 m3">
+            <strong class="truncate">
+              {% trans "Affected teachers" %}
+            </strong>
+          </div>
+          <div class="col s12 m9 black-text-a">
+            {% include "chronos/partials/teachers.html" with teachers=affected_teachers %}
+          </div>
+        </div>
+      {% endif %}
+      {% if affected_groups %}
+        <div class="row no-margin">
+          <div class="col s12 m3">
+            <strong class="truncate">
+              {% trans "Affected groups" %}
+            </strong>
+          </div>
+          <div class="col s12 m9 black-text-a">
+            {% include "chronos/partials/groups.html" with groups=affected_groups %}
+          </div>
+        </div>
+      {% endif %}
+    </div>
+  </div>
+  {% if print %}<br/>{% endif %}
+{% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/substitutions.html b/aleksis/apps/chronos/templates/chronos/substitutions.html
index 32e98c56..0dc5e242 100644
--- a/aleksis/apps/chronos/templates/chronos/substitutions.html
+++ b/aleksis/apps/chronos/templates/chronos/substitutions.html
@@ -26,22 +26,7 @@
 
   <div class="row no-print">
     <div class="col s12 m6 l8">
-      {% if header_info.is_box_needed %}
-        <div class="card">
-          <div class="card-content">
-            {% for row in header_info.rows %}
-              <div class="row no-margin">
-                <div class="col s3">
-                  <strong class="truncate">{{ row.0 }}</strong>
-                </div>
-                <div class="col s9">
-                  {{ row.1 }}
-                </div>
-              </div>
-            {% endfor %}
-          </div>
-        </div>
-      {% endif %}
+      {% include "chronos/partials/headerbox.html" %}
 
       {#            {% include "chronos/hintsinsub.html" %}#}
     </div>
diff --git a/aleksis/apps/chronos/templates/chronos/substitutions_print.html b/aleksis/apps/chronos/templates/chronos/substitutions_print.html
index 2a4e474b..bc041f24 100644
--- a/aleksis/apps/chronos/templates/chronos/substitutions_print.html
+++ b/aleksis/apps/chronos/templates/chronos/substitutions_print.html
@@ -18,20 +18,7 @@
 
     {#    {% include "timetable/hintsinsubprint.html" %}#}
 
-    {#    <div style="margin-bottom: 20px">#}
-    {#        {% if c.header_info.is_box_needed %}#}
-    {#            {% for row in c.header_info.rows %}#}
-    {#                <div class="row no-margin">#}
-    {#                    <div class="col s3 no-padding">#}
-    {#                        <strong>{{ row.0 }}</strong>#}
-    {#                    </div>#}
-    {#                    <div class="col s9 no-padding">#}
-    {#                        {{ row.1 }}#}
-    {#                    </div>#}
-    {#                </div>#}
-    {#            {% endfor %}#}
-    {#        {% endif %}#}
-    {#    </div>#}
+    {% include "chronos/partials/headerbox.html" with affected_teachers=c.affected_teachers affected_groups=c.affected_groups print=1 %}
 
     <table class="substitutions">
       <thead>
diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py
index 388307c0..71deb5de 100644
--- a/aleksis/apps/chronos/views.py
+++ b/aleksis/apps/chronos/views.py
@@ -321,9 +321,12 @@ def substitutions(
         day_contexts = {wanted_day: {"day": wanted_day}}
 
     for day in day_contexts:
-        day_contexts[day]["substitutions"] = LessonSubstitution.objects.on_day(
-            day
-        ).order_by("lesson_period__lesson__groups", "lesson_period__period")
+        subs = LessonSubstitution.objects.on_day(day).order_by("lesson_period__lesson__groups", "lesson_period__period")
+        day_contexts[day]["substitutions"] = subs
+
+        if config.CHRONOS_SUBSTITUTIONS_SHOW_HEADER_BOX:
+            day_contexts[day]["affected_teachers"] = subs.affected_teachers()
+            day_contexts[day]["affected_groups"] = subs.affected_groups()
 
     if not is_print:
         context = day_contexts[wanted_day]
-- 
GitLab