Skip to content
Snippets Groups Projects
Verified Commit c9a56bb2 authored by magicfelix's avatar magicfelix Committed by Jonathan Weth
Browse files

Adapt substitution PDF to new data model

diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index 35f4e01..f348c00 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -1221,6 +1221,7 @@ class AutomaticPlan(LiveDocument):
         from aleksis.apps.chronos.util.chronos_helpers import get_substitutions_context_data  # noqa

         context = get_substitutions_context_data(
+            wanted_day=date.today(),
             request=None,
             is_print=True,
             number_of_days=self.number_of_days,
diff --git a/aleksis/apps/chronos/templates/chronos/partials/rooms.html b/aleksis/apps/chronos/templates/chronos/partials/rooms.html
new file mode 100644
index 0000000..1e42988
--- /dev/null
+++ b/aleksis/apps/chronos/templates/chronos/partials/rooms.html
@@ -0,0 +1,8 @@
+{% for room in rooms %}
+  <span data-position="bottom" class="tooltipped"
+        data-tooltip="{{ room }}">
+      <a href="{% url "timetable" "room" room.pk %}">
+          {{ room.short_name }}{% if not forloop.last %},{% endif %}
+      </a>
+  </span>
+{% endfor %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/badge.html b/aleksis/apps/chronos/templates/chronos/partials/subs/badge.html
index c04c9dd..c807244 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/badge.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/badge.html
@@ -2,6 +2,4 @@

 {% if sub.cancelled %}
   <span class="badge new green">{% trans "Cancelled" %}</span>
-{% elif item.el.cancelled_for_teachers %}
-  <span class="badge new green">{% trans "Cancelled for teachers" %}</span>
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html b/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html
index 833a24b..e59b37f 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html
@@ -1,11 +1,5 @@
-{% if item.type == "substitution" %}
-  {% if item.el.cancelled or item.el.cancelled_for_teachers %}
-    green-text
-  {% else %}
-    black-text
-  {% endif %}
-{% elif item.type == "supervision_substitution" %}
-  blue-text
-{% elif item.type == "event" %}
-  purple-text
+{% if item.el.cancelled %}
+  green-text
+{% else %}
+  black-text
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html b/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html
index d1a4da9..079e0f0 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html
@@ -1,5 +1,15 @@
-{% if type == "substitution" %}
-  {% include "chronos/partials/groups.html" with groups=el.lesson_period.lesson.groups.all %}
-{% elif type == "extra_lesson" or type == "event" %}
+{% if el.cancelled and el.amends.groups.all %}
+  {% include "chronos/partials/groups.html" with groups=el.amends.groups.all %}
+{% elif el.groups.all and el.amends.groups.all %}
+  <s>
+    {% include "chronos/partials/groups.html" with groups=el.amends.groups.all %}
+  </s>
+  →
+  <strong>
+    {% include "chronos/partials/groups.html" with groups=el.groups.all %}
+  </strong>
+{% elif el.groups.all and not el.amends.groups.all %}
   {% include "chronos/partials/groups.html" with groups=el.groups.all %}
+{% elif el.amends.groups.all %}
+  {% include "chronos/partials/groups.html" with groups=el.amends.groups.all %}
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/period.html b/aleksis/apps/chronos/templates/chronos/partials/subs/period.html
index ef1d283..4ba7706 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/period.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/period.html
@@ -1,19 +1,7 @@
 <strong>
-  {% if type == "substitution" and item.start_period == item.end_period %}
-    {{ el.lesson_period.period.period }}.
-  {% elif type == "substitution"  %}
-    {{ item.start_period }}.–{{ item.end_period }}.
-  {% elif type == "extra_lesson" %}
-    {{ el.period.period }}.
-  {% elif type == "event" %}
-    {% if el.period_from_on_day == el.period_to_on_day %}
-      {{ el.period_from_on_day }}.
-    {% else %}
-      {{ el.period_from_on_day }}.–{{ el.period_to_on_day }}.
-    {% endif %}
-  {% elif type == "supervision_substitution" %}
-    {% with break=el.supervision.break_item %}
-      {{ break.after_period_number }}./{{ break.before_period_number }}.
-    {% endwith %}
+  {% if el.datetime_start %}
+    {{ el.datetime_start.time }} - {{ el.datetime_end.time }}
+  {% elif el.date_start %}
+    {{ el.date_start }} - {{ el.date_end }}
   {% endif %}
 </strong>
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
index 94f2d35..123faba 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html
@@ -1,39 +1,15 @@
-{% if type == "substitution" %}
-  {% if el.cancelled or el.cancelled_for_teachers %}
-    {# Cancelled lesson: no room #}
-  {% elif el.room and el.lesson_period.room %}
-    {# New and old room available #}
-    <span class="tooltipped" data-position="bottom"
-          data-tooltip="{{ el.lesson_period.room.name }} → {{ el.room.name }}"
-          title="{{ el.lesson_period.room.name }} → {{ el.room.name }}">
-      <a href="{% url "timetable" "room" el.lesson_period.room.pk %}">
-          <s>{{ el.lesson_period.room.short_name }}</s>
-      </a>
-      →
-      <a href="{% url "timetable" "room" el.room.pk %}">
-          <strong>{{ el.room.short_name }}</strong>
-      </a>
-    </span>
-  {% elif el.room and not el.lesson_period.room %}
-    {# Only new room available #}
-    {% include "chronos/partials/room.html" with room=el.room %}
-  {% elif not el.room and not el.lesson_period.room %}
-    {# Nothing to view #}
-  {% else %}
-    {# Only old room available #}
-    {% include "chronos/partials/room.html" with room=el.lesson_period.room %}
-  {% endif %}
-{% elif type == "supervision_substitution" %}
-  {% with supervision=el.supervision %}
-    <span data-position="bottom" class="tooltipped"
-          data-tooltip="{{ supervision.area.name }}" title="{{ supervision.area.name }}">
-      {{ supervision.area.short_name }}
-    </span>
-  {% endwith %}
-{% elif type == "extra_lesson" %}
-  {% include "chronos/partials/room.html" with room=el.room %}
-{% elif type == "event" %}
-  {% for room in el.rooms.all %}
-    {% include "chronos/partials/room.html" with room=room %}{% if not forloop.last %},{% endif %}
-  {% endfor %}
+{% if el.cancelled and el.amends.rooms.all %}
+  {% include "chronos/partials/rooms.html" with rooms=el.amends.rooms.all %}
+{% elif el.rooms.all and el.amends.rooms.all %}
+  <s>
+    {% include "chronos/partials/rooms.html" with rooms=el.amends.rooms.all %}
+  </s>
+  →
+  <strong>
+    {% include "chronos/partials/rooms.html" with rooms=el.rooms.all %}
+  </strong>
+{% elif el.rooms.all and not el.amends.rooms.all %}
+  {% include "chronos/partials/rooms.html" with rooms=el.rooms.all %}
+{% elif el.amends.rooms.all %}
+  {% include "chronos/partials/rooms.html" with rooms=el.amends.rooms.all %}
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/rooms.html b/aleksis/apps/chronos/templates/chronos/partials/subs/rooms.html
new file mode 100644
index 0000000..123faba
--- /dev/null
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/rooms.html
@@ -0,0 +1,15 @@
+{% if el.cancelled and el.amends.rooms.all %}
+  {% include "chronos/partials/rooms.html" with rooms=el.amends.rooms.all %}
+{% elif el.rooms.all and el.amends.rooms.all %}
+  <s>
+    {% include "chronos/partials/rooms.html" with rooms=el.amends.rooms.all %}
+  </s>
+  →
+  <strong>
+    {% include "chronos/partials/rooms.html" with rooms=el.rooms.all %}
+  </strong>
+{% elif el.rooms.all and not el.amends.rooms.all %}
+  {% include "chronos/partials/rooms.html" with rooms=el.rooms.all %}
+{% elif el.amends.rooms.all %}
+  {% include "chronos/partials/rooms.html" with rooms=el.amends.rooms.all %}
+{% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html
index 1b7a3c5..dcbfa4b 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html
@@ -1,28 +1,28 @@
 {% load i18n %}

-{% if type == "substitution" %}
-  {% if not el.lesson_period.lesson.subject and not el.subject %}
-  {% elif el.cancelled or el.cancelled_for_teachers %}
-    <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.lesson_period.lesson.subject.name }}">
-    <s>{{ el.lesson_period.lesson.subject.short_name }}</s>
-  </span>
-  {% elif el.subject and el.lesson_period.lesson.subject %}
-    <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.lesson_period.lesson.subject.name }}">
-    <s>{{ el.lesson_period.lesson.subject.short_name }}</s>
-  </span>
-    →
-    <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.subject.name }}">
-    <strong>{{ el.subject.short_name }}</strong>
-  </span>
-  {% elif el.subject and not el.lesson_period.lesson.subject %}
-    {% include "chronos/partials/subject.html" with subject=el.subject %}
-  {% else %}
-    {% include "chronos/partials/subject.html" with subject=el.lesson_period.lesson.subject %}
+{% if not el.amends.subject and not el.subject %}
+  {% if el.amends.title %}
+    <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.amends.title }}">
+    <s>{{ el.amends.title }}</s>
   {% endif %}
-{% elif type == "supervision_substitution" %}
-  {% trans "Supervision" %}
-{% elif type == "extra_lesson" %}
+  {% if el.title %}
+    <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.title }}">
+    <s>{{ el.title }}</s>
+  {% endif %}
+{% elif el.cancelled %}
+  <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.subject.short_name }}">
+  <s>{{ el.subject.short_name }}</s>
+</span>
+{% elif el.subject and el.amends.subject %}
+  <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.amends.subject.name }}">
+  <s>{{ el.amends.subject.short_name }}</s>
+</span>
+  →
+  <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.subject.name }}">
+  <strong>{{ el.subject.short_name }}</strong>
+</span>
+{% elif el.subject and not el.amends.subject %}
   {% include "chronos/partials/subject.html" with subject=el.subject %}
-{% elif type == "event" %}
-  {% trans "Event" %}
+{% else %}
+  {% include "chronos/partials/subject.html" with subject=el.amends.subject %}
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html b/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html
index 4fa80d8..9e08eeb 100644
--- a/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html
+++ b/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html
@@ -1,27 +1,15 @@
-{% if type == "substitution" %}
-  {% if el.cancelled and el.lesson_period.lesson.teachers.all %}
-    {% include "chronos/partials/teachers.html" with teachers=el.lesson_period.lesson.teachers.all %}
-  {% elif el.teachers.all and el.lesson_period.lesson.teachers.all %}
-    <s>
-      {% include "chronos/partials/teachers.html" with teachers=el.lesson_period.lesson.teachers.all %}
-    </s>
-    →
-    <strong>
-      {% include "chronos/partials/teachers.html" with teachers=el.teachers.all %}
-    </strong>
-  {% elif el.teachers.all and not el.lesson_period.lesson.teachers.all %}
-    {% include "chronos/partials/teachers.html" with teachers=el.teachers.all %}
-  {% elif el.lesson_period.lesson.teachers.all %}
-    {% include "chronos/partials/teachers.html" with teachers=el.lesson_period.lesson.teachers.all %}
-  {% endif %}
-{% elif type == "supervision_substitution" %}
+{% if el.cancelled and el.amends.teachers.all %}
+  {% include "chronos/partials/teachers.html" with teachers=el.amends.teachers.all %}
+{% elif el.teachers.all and el.amends.teachers.all %}
   <s>
-    {% include "chronos/partials/teachers.html" with teachers=el.supervision.teachers %}
+    {% include "chronos/partials/teachers.html" with teachers=el.amends.teachers.all %}
   </s>
   →
   <strong>
-    {% include "chronos/partials/teachers.html" with teachers=el.teachers %}
+    {% include "chronos/partials/teachers.html" with teachers=el.teachers.all %}
   </strong>
-{% elif type == "extra_lesson" or type == "event" %}
+{% elif el.teachers.all and not el.amends.teachers.all %}
   {% include "chronos/partials/teachers.html" with teachers=el.teachers.all %}
+{% elif el.amends.teachers.all %}
+  {% include "chronos/partials/teachers.html" with teachers=el.amends.teachers.all %}
 {% endif %}
diff --git a/aleksis/apps/chronos/templates/chronos/substitutions_print.html b/aleksis/apps/chronos/templates/chronos/substitutions_print.html
index 743a223..10243a1 100644
--- a/aleksis/apps/chronos/templates/chronos/substitutions_print.html
+++ b/aleksis/apps/chronos/templates/chronos/substitutions_print.html
@@ -18,13 +18,13 @@

     {% include "core/partials/announcements.html" with announcements=c.announcements show_recipients=1 %}

-    {% include "chronos/partials/headerbox.html" with affected_teachers=c.affected_teachers affected_groups=c.affected_groups absent_teachers=c.absent_teachers absent_groups=c.absent_groups print=1 %}
+    {% include "chronos/partials/headerbox.html" with absent_teachers=c.absent_teachers absent_groups=c.absent_groups print=1 %}

     <table class="substitutions">
       <thead>
       <tr>
-        <th><i class="material-icons iconify center" data-icon="mdi:account-multiple-outline"></i></th>
-        <th><i class="material-icons iconify center" data-icon="mdi:clock-outline"></i></th>
+        <th>{% blocktrans %}Groups{% endblocktrans %}</th>
+        <th>{% blocktrans %}Time{% endblocktrans %}</th>
         <th>{% blocktrans %}Teachers{% endblocktrans %}</th>
         <th>{% blocktrans %}Subject{% endblocktrans %}</th>
         <th>{% blocktrans %}Room{% endblocktrans %}</th>
@@ -47,7 +47,7 @@

       <tbody>
       {% for item in c.substitutions %}
-        {% ifchanged item.el.lesson_period.lesson.groups_to_show_names %}
+        {% ifchanged item.el.group_names %}
           </tbody>
           <tbody class="{% cycle "striped" "not-striped" %}">
         {% endifchanged %}
@@ -66,7 +66,7 @@
             {% include "chronos/partials/subs/subject.html" with type=item.type el=item.el %}
           </td>
           <td>
-            {% include "chronos/partials/subs/room.html" with type=item.type el=item.el %}
+            {% include "chronos/partials/subs/rooms.html" with type=item.type el=item.el %}
           </td>
           <td>
             {% include "chronos/partials/subs/badge.html" with sub=item.el %}
diff --git a/aleksis/apps/chronos/util/build.py b/aleksis/apps/chronos/util/build.py
index 625998d..7946a3e 100644
--- a/aleksis/apps/chronos/util/build.py
+++ b/aleksis/apps/chronos/util/build.py
@@ -3,6 +3,7 @@ from datetime import date
 from typing import Union

 from django.apps import apps
+from django.db.models import Q

 from calendarweek import CalendarWeek

@@ -10,6 +11,7 @@ from aleksis.apps.chronos.managers import TimetableType
 from aleksis.core.models import Group, Person, Room

 LessonPeriod = apps.get_model("chronos", "LessonPeriod")
+LessonEvent = apps.get_model("chronos", "LessonEvent")
 TimePeriod = apps.get_model("chronos", "TimePeriod")
 Break = apps.get_model("chronos", "Break")
 Supervision = apps.get_model("chronos", "Supervision")
@@ -383,84 +385,25 @@ def build_timetable(
 def build_substitutions_list(wanted_day: date) -> list[dict]:
     rows = []

-    subs = LessonSubstitution.objects.on_day(wanted_day).order_by(
-        "lesson_period__lesson__groups", "lesson_period__period"
+    subs = (
+        LessonEvent.objects.exclude(amends=None)
+        .filter(Q(datetime_start__date=wanted_day) | Q(date_start=wanted_day))
+        .order_by("datetime_start", "date_start")
     )

-    start_period = None
     for i, sub in enumerate(subs):
-        if not sub.cancelled_for_teachers:
-            sort_a = sub.lesson_period.lesson.groups_to_show_names
-        else:
-            sort_a = f"Z.{sub.lesson_period.lesson.teacher_names}"
-
-        # Get next substitution
-        next_sub = subs[i + 1] if i + 1 < len(subs) else None
-
-        # Check if next substitution is equal with this substitution
-        if (
-            next_sub
-            and sub.comment == next_sub.comment
-            and sub.cancelled == next_sub.cancelled
-            and sub.subject == next_sub.subject
-            and sub.room == next_sub.room
-            and sub.lesson_period.lesson == next_sub.lesson_period.lesson
-            and set(sub.teachers.all()) == set(next_sub.teachers.all())
-        ):
-            if not start_period:
-                start_period = sub.lesson_period.period.period
-            continue
+        sort_a = sub.group_names
+
+        # FIXME? Looks hacky. sub.amends returns a CalendarEvent, but a LessonEvent is needed
+        sub.amends = LessonEvent.objects.get(pk=sub.amends.pk)

         row = {
             "type": "substitution",
             "sort_a": sort_a,
-            "sort_b": str(sub.lesson_period.period.period),
+            "sort_b": str(sub.datetime_start if sub.datetime_start else sub.date_start),
             "el": sub,
-            "start_period": start_period if start_period else sub.lesson_period.period.period,
-            "end_period": sub.lesson_period.period.period,
-        }
-
-        if start_period:
-            start_period = None
-
-        rows.append(row)
-
-    # Get supervision substitutions
-    super_subs = SupervisionSubstitution.objects.filter(date=wanted_day)
-
-    for super_sub in super_subs:
-        row = {
-            "type": "supervision_substitution",
-            "sort_a": f"Z.{super_sub.teacher}",
-            "sort_b": str(super_sub.supervision.break_item.after_period_number),
-            "el": super_sub,
-        }
-        rows.append(row)
-
-    # Get extra lessons
-    extra_lessons = ExtraLesson.objects.on_day(wanted_day)
-
-    for extra_lesson in extra_lessons:
-        row = {
-            "type": "extra_lesson",
-            "sort_a": str(extra_lesson.group_names),
-            "sort_b": str(extra_lesson.period.period),
-            "el": extra_lesson,
         }
-        rows.append(row)
-
-    # Get events
-    events = Event.objects.on_day(wanted_day).annotate_day(wanted_day)

-    for event in events:
-        sort_a = event.group_names if event.groups.all() else f"Z.{event.teacher_names}"
-
-        row = {
-            "type": "event",
-            "sort_a": sort_a,
-            "sort_b": str(event.period_from_on_day),
-            "el": event,
-        }
         rows.append(row)

     # Sort all items
diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py
index 8e16401..4c36d3a 100644
--- a/aleksis/apps/chronos/util/chronos_helpers.py
+++ b/aleksis/apps/chronos/util/chronos_helpers.py
@@ -1,4 +1,4 @@
-from datetime import datetime, timedelta
+from datetime import date, datetime, timedelta
 from typing import TYPE_CHECKING, Optional

 from django.db.models import Count, Q
@@ -160,10 +160,8 @@ def get_rooms(user: "User"):

 def get_substitutions_context_data(
+    wanted_day: date,
     request: Optional[HttpRequest] = None,
-    year: Optional[int] = None,
-    month: Optional[int] = None,
-    day: Optional[int] = None,
     is_print: bool = False,
     number_of_days: Optional[int] = None,
     show_header_box: Optional[bool] = None,
@@ -171,12 +169,6 @@ def get_substitutions_context_data(
     """Get context data for the substitutions table."""
     context = {}

-    if day:
-        wanted_day = timezone.datetime(year=year, month=month, day=day).date()
-        wanted_day = TimePeriod.get_next_relevant_day(wanted_day)
-    else:
-        wanted_day = TimePeriod.get_next_relevant_day(timezone.now().date(), timezone.now().time())
-
     day_number = (
         number_of_days or get_site_preferences()["chronos__substitutions_print_number_of_days"]
     )
@@ -191,7 +183,7 @@ def get_substitutions_context_data(
         next_day = wanted_day
         for _i in range(day_number):
             day_contexts[next_day] = {"day": next_day}
-            next_day = TimePeriod.get_next_relevant_day(next_day + timedelta(days=1))
+            next_day = next_day + timedelta(days=1)
     else:
         day_contexts = {wanted_day: {"day": wanted_day}}

@@ -202,22 +194,9 @@ def get_substitutions_context_data(
         day_contexts[day]["announcements"] = Announcement.for_timetables().on_date(day)

         if show_header_box:
-            subs = LessonSubstitution.objects.on_day(day).order_by(
-                "lesson_period__lesson__groups", "lesson_period__period"
-            )
             absences = Absence.objects.on_day(day)
             day_contexts[day]["absent_teachers"] = absences.absent_teachers()
             day_contexts[day]["absent_groups"] = absences.absent_groups()
-            day_contexts[day]["affected_teachers"] = subs.affected_teachers()
-            affected_groups = subs.affected_groups()
-            if get_site_preferences()["chronos__affected_groups_parent_groups"]:
-                groups_with_parent_groups = affected_groups.filter(parent_groups__isnull=False)
-                groups_without_parent_groups = affected_groups.filter(parent_groups__isnull=True)
-                affected_groups = Group.objects.filter(
-                    Q(child_groups__pk__in=groups_with_parent_groups.values_list("pk", flat=True))
-                    | Q(pk__in=groups_without_parent_groups.values_list("pk", flat=True))
-                ).distinct()
-            day_contexts[day]["affected_groups"] = affected_groups

     if not is_print:
         context = day_contexts[wanted_day]
@@ -226,10 +205,6 @@ def get_substitutions_context_data(
             "dest": reverse("substitutions"),
         }

-        context["url_prev"], context["url_next"] = TimePeriod.get_prev_next_by_day(
-            wanted_day, "substitutions_by_date"
-        )
-
     else:
         context["days"] = day_contexts

diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py
index a45997c..ee01e35 100644
--- a/aleksis/apps/chronos/views.py
+++ b/aleksis/apps/chronos/views.py
@@ -1,3 +1,4 @@
+from datetime import date, datetime
 from typing import Optional

 from django.http import HttpRequest, HttpResponse
parent a5df2c07
No related branches found
No related tags found
1 merge request!315Resolve "Substitutions PDF for new data model"
Showing
with 109 additions and 215 deletions
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment