Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • AlekSIS/official/AlekSIS-App-Chronos
  • sunweaver/AlekSIS-App-Chronos
  • sggua/AlekSIS-App-Chronos
  • tincmeKdenka/AlekSIS-App-Chronos
  • ligquamacti/AlekSIS-App-Chronos
  • 1crotatilhe/AlekSIS-App-Chronos
  • 1compluningi/AlekSIS-App-Chronos
  • starwardcarfi/AlekSIS-App-Chronos
  • ceohecholeg/AlekSIS-App-Chronos
  • 7quecontranchi/AlekSIS-App-Chronos
  • 8evsubcesza/AlekSIS-App-Chronos
  • unscinKibdzu/AlekSIS-App-Chronos
  • delucPchondmu/AlekSIS-App-Chronos
13 results
Show changes
Commits on Source (14)
  • Jonathan Weth's avatar
    Verified
    d77a165f
  • Jonathan Weth's avatar
    Verified
    a5df2c07
  • magicfelix's avatar
    Adapt substitution PDF to new data model · c9a56bb2
    magicfelix authored and Jonathan Weth's avatar Jonathan Weth committed
    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
    Verified
    c9a56bb2
  • Jonathan Weth's avatar
    Fix URLs and view for substitutions print · f138d585
    Jonathan Weth authored
    Verified
    f138d585
  • Jonathan Weth's avatar
    Verified
    7f59fc60
  • Jonathan Weth's avatar
    Verified
    f9e06f5a
  • Jonathan Weth's avatar
    Delete old template files · 811abe64
    Jonathan Weth authored
    Verified
    811abe64
  • Jonathan Weth's avatar
    Verified
    8c6aa254
  • Jonathan Weth's avatar
    Reformat · 0179d302
    Jonathan Weth authored
    Verified
    0179d302
  • Jonathan Weth's avatar
    Fix spacing in substitutions print · f88dcb6a
    Jonathan Weth authored
    Verified
    f88dcb6a
  • Jonathan Weth's avatar
  • Jonathan Weth's avatar
    Fix end time in substitutions print · b4dd65f4
    Jonathan Weth authored
    Verified
    b4dd65f4
  • Jonathan Weth's avatar
    Fix querying of supervisions · 2aa7a0e9
    Jonathan Weth authored
    Verified
    2aa7a0e9
  • Jonathan Weth's avatar
    Merge branch '215-substitutions-pdf-for-new-data-model' into 'master' · 3211fca8
    Jonathan Weth authored
    Resolve "Substitutions PDF for new data model"
    
    Closes #215
    
    See merge request !315
    3211fca8
Showing
with 129 additions and 311 deletions
......@@ -32,5 +32,21 @@ export default {
fullWidth: true,
},
},
{
path: "substitutions/print/",
component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
name: "chronos.substitutions",
props: {
byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
},
},
{
path: "substitutions/print/:date/",
component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
name: "chronos.substitutionsByDate",
props: {
byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
},
},
],
};
......@@ -946,3 +946,11 @@ class LessonEventQuerySet(RecurrencePolymorphicQuerySet):
def not_amending(self) -> "LessonEventQuerySet":
"""Get all lesson events that are not amending other events."""
return self.filter(amends__isnull=True)
def amending(self) -> "LessonEventQuerySet":
"""Get all lesson events that are amending other events."""
return self.filter(amends__isnull=False)
class SupervisionEventQuerySet(LessonEventQuerySet):
pass
......@@ -42,6 +42,7 @@ from aleksis.apps.chronos.managers import (
LessonPeriodQuerySet,
LessonSubstitutionManager,
LessonSubstitutionQuerySet,
SupervisionEventQuerySet,
SupervisionManager,
SupervisionQuerySet,
SupervisionSubstitutionManager,
......@@ -1209,20 +1210,26 @@ class AutomaticPlan(LiveDocument):
@property
def current_start_day(self) -> date:
"""Get first day which should be shown in the PDF."""
return TimePeriod.get_next_relevant_day(timezone.now().date(), datetime.now().time())
from aleksis.apps.chronos.util.chronos_helpers import get_next_relevant_day
return get_next_relevant_day(timezone.now())
@property
def current_end_day(self) -> date:
"""Get last day which should be shown in the PDF."""
return self.current_start_day + timedelta(days=self.number_of_days - 1)
from aleksis.apps.chronos.util.chronos_helpers import get_next_relevant_day
day = self.current_start_day
for _i in range(self.number_of_days - 1):
day = get_next_relevant_day(day)
return day
def get_context_data(self) -> dict[str, Any]:
"""Get context data for generating the substitutions PDF."""
from aleksis.apps.chronos.util.chronos_helpers import get_substitutions_context_data # noqa
context = get_substitutions_context_data(
request=None,
is_print=True,
wanted_day=date.today(),
number_of_days=self.number_of_days,
show_header_box=self.show_header_box,
)
......@@ -1248,11 +1255,16 @@ class AutomaticPlan(LiveDocument):
continue
# Check if the changed object is relevant for the time period of the PDF file
if isinstance(version.object, Event):
if not version.object.amends:
return
if version.object.datetime_start:
date_start = version.object.datetime_start.date()
date_end = version.object.datetime_end.date()
else:
date_start = version.object.date_start
date_end = version.object.date_end
else:
date_start = date_end = version.object.date
if date_start <= self.current_end_day and date_end >= self.current_start_day:
update = True
break
......@@ -1537,9 +1549,15 @@ class LessonEvent(CalendarEvent):
@classmethod
def get_objects(
cls, request: HttpRequest | None = None, params: dict[str, any] | None = None, **kwargs
cls,
request: HttpRequest | None = None,
params: dict[str, any] | None = None,
no_effect: bool = False,
**kwargs,
) -> Iterable:
"""Return all objects that should be included in the calendar."""
if no_effect:
return super().get_objects(request, params, **kwargs)
objs = (
super()
.get_objects(request, params, **kwargs)
......@@ -1556,6 +1574,7 @@ class LessonEvent(CalendarEvent):
type_ = params.get("type", None)
not_amended = params.get("not_amended", False)
not_amending = params.get("not_amending", False)
amending = params.get("amending", False)
own = params.get("own", False)
if not_amended:
......@@ -1564,6 +1583,9 @@ class LessonEvent(CalendarEvent):
if not_amending:
objs = objs.not_amending()
if amending:
objs = objs.amending()
if request and "own" in params:
if own:
objs = objs.for_person(request.user.person)
......@@ -1599,7 +1621,7 @@ class SupervisionEvent(LessonEvent):
name = "supervision"
verbose_name = _("Supervisions")
objects = RecurrencePolymorphicManager.from_queryset(LessonEventQuerySet)()
objects = RecurrencePolymorphicManager.from_queryset(SupervisionEventQuerySet)()
@classmethod
def value_title(cls, reference_object: LessonEvent, request: HttpRequest | None = None) -> str:
......@@ -1624,10 +1646,22 @@ class SupervisionEvent(LessonEvent):
cls, request: HttpRequest | None = None, params: dict[str, any] | None = None, **kwargs
) -> Iterable:
"""Return all objects that should be included in the calendar."""
objs = super().get_objects(request, params, **kwargs).instance_of(cls)
objs = super().get_objects(request, params, no_effect=True, **kwargs)
if params:
obj_id = int(params.get("id", 0))
type_ = params.get("type", None)
not_amended = params.get("not_amended", False)
not_amending = params.get("not_amending", False)
amending = params.get("amending", False)
if not_amended:
objs = objs.not_amended()
if not_amending:
objs = objs.not_amending()
if amending:
objs = objs.amending()
if type_ and obj_id:
if type_ == "TEACHER":
......
from datetime import time
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from calendarweek.django import i18n_day_name_choices_lazy
from colorfield.widgets import ColorWidget
from dynamic_preferences.preferences import Section
from dynamic_preferences.types import (
BooleanPreference,
IntegerPreference,
ModelMultipleChoicePreference,
MultipleChoicePreference,
StringPreference,
TimePreference,
)
......@@ -52,6 +55,34 @@ class ShortenGroupsLimit(IntegerPreference):
)
@site_preferences_registry.register
class SubstitutionsRelevantDays(MultipleChoicePreference):
"""Relevant days which have substitution plans."""
section = chronos
name = "substitutions_relevant_days"
default = [0, 1, 2, 3, 4]
verbose_name = _("Relevant days for substitution plans")
required = True
choices = i18n_day_name_choices_lazy()
def validate(self, value):
for v in value:
if int(v) not in self.get_choice_values():
raise ValidationError(f"{v} is not a valid choice")
@site_preferences_registry.register
class SubstitutionsDayChangeTime(TimePreference):
"""Time when substitution plans should switch to the next day."""
section = chronos
name = "substitutions_day_change_time"
default = time(18, 0)
verbose_name = _("Time when substitution plans switch to the next day")
required = True
@site_preferences_registry.register
class SubstitutionsPrintNumberOfDays(IntegerPreference):
section = chronos
......
from rules import add_perm
from aleksis.core.util.predicates import (
has_any_object,
has_global_perm,
has_object_perm,
has_person,
)
from .models import LessonSubstitution
from .util.predicates import has_any_timetable_object, has_room_timetable_perm, has_timetable_perm
# View timetable overview
......@@ -42,10 +40,7 @@ delete_substitution_predicate = has_person & (
add_perm("chronos.delete_substitution_rule", delete_substitution_predicate)
# View substitutions
view_substitutions_predicate = has_person & (
has_global_perm("chronos.view_lessonsubstitution")
| has_any_object("chronos.view_lessonsubstitution", LessonSubstitution)
)
view_substitutions_predicate = has_person & (has_global_perm("chronos.view_lessonsubstitution"))
add_perm("chronos.view_substitutions_rule", view_substitutions_predicate)
# View all supervisions per day
......
......@@ -2,13 +2,13 @@ from datetime import timezone
import graphene
from graphene_django import DjangoObjectType
from graphene_django_cud.mutations import (
DjangoBatchCreateMutation,
DjangoBatchDeleteMutation,
DjangoBatchPatchMutation,
)
from aleksis.core.models import Group, Person, Room
from aleksis.core.schema.base import (
BaseBatchCreateMutation,
BaseBatchDeleteMutation,
BaseBatchPatchMutation,
)
from ..models import LessonEvent
from ..util.chronos_helpers import get_groups, get_rooms, get_teachers
......@@ -80,7 +80,7 @@ class DatetimeTimezoneMixin:
return value
class AmendLessonBatchCreateMutation(DatetimeTimezoneMixin, DjangoBatchCreateMutation):
class AmendLessonBatchCreateMutation(DatetimeTimezoneMixin, BaseBatchCreateMutation):
class Meta:
model = LessonEvent
permissions = ("chronos.edit_substitution_rule",)
......@@ -98,12 +98,13 @@ class AmendLessonBatchCreateMutation(DatetimeTimezoneMixin, DjangoBatchCreateMut
@classmethod
def before_save(cls, root, info, input, created_objects): # noqa: A002
super().before_save(root, info, input, created_objects)
for obj in created_objects:
obj.timezone = obj.amends.timezone
return created_objects
class AmendLessonBatchPatchMutation(DatetimeTimezoneMixin, DjangoBatchPatchMutation):
class AmendLessonBatchPatchMutation(DatetimeTimezoneMixin, BaseBatchPatchMutation):
class Meta:
model = LessonEvent
permissions = ("chronos.edit_substitution_rule",)
......@@ -111,12 +112,13 @@ class AmendLessonBatchPatchMutation(DatetimeTimezoneMixin, DjangoBatchPatchMutat
@classmethod
def before_save(cls, root, info, input, updated_objects): # noqa: A002
super().before_save(root, info, input, updated_objects)
for obj in updated_objects:
obj.timezone = obj.amends.timezone
return updated_objects
class AmendLessonBatchDeleteMutation(DjangoBatchDeleteMutation):
class AmendLessonBatchDeleteMutation(BaseBatchDeleteMutation):
class Meta:
model = LessonEvent
permissions = ("chronos.delete_substitution_rule",)
......
{% load static %}
{% if not display_date_only %}
<script type="text/javascript" src="{% static "js/helper.js" %}"></script>
{{ datepicker|json_script:"datepicker_data" }}
<script type="text/javascript" src="{% static "js/chronos/date_select.js" %}"></script>
{% endif %}
<div class="col s2 no-padding">
<a class="waves-effect waves-secondary btn-flat btn-flat-medium left" href="{{ url_prev }}">
<i class="material-icons iconify center" data-icon="mdi:chevron-left"></i>
</a>
</div>
{% if display_date_only %}
<div class="col s8">
<span class="card-title center-block" id="date">
{{ day|date:"l" }}, {{ day }}
</span>
</div>
{% else %}
<div class="col s8 no-padding">
<input type="text" class="datepicker center-align" id="date">
</div>
{% endif %}
<div class="col s2 no-padding">
<a class="waves-effect waves-secondary btn-flat btn-flat-medium right" href="{{ url_next }}">
<i class="material-icons iconify center" data-icon="mdi:chevron-right"></i>
</a>
</div>
<div class="card lesson-card {% if active_day and week_day == active_day %} z-depth-5 active {% endif %}">
<div class="card-content">
{% for element in elements %}
{% if element.label_ == "lesson_period" %}
{% include "chronos/partials/lesson.html" with lesson_period=element %}
{% elif element.label_ == "extra_lesson" and smart %}
{% include "chronos/partials/extra_lesson.html" with extra_lesson=element %}
{% elif element.label_ == "event" and smart %}
{% include "chronos/partials/event.html" with event=element %}
{% endif %}
{% endfor %}
</div>
</div>
<div class="lesson-with-event">
<p>
{# Teacher or room > Display groups #}
{% if type.value == "teacher" or type.value == "room" %}
{% include "chronos/partials/groups.html" with groups=event.groups.all %}
{% endif %}
{# Class or room > Display teachers #}
{% if type.value == "room" or type.value == "group" %}
{% include "chronos/partials/teachers.html" with teachers=event.teachers.all %}
{% endif %}
{# Teacher or class > Display rooms #}
{% if type.value == "teacher" or type.value == "group" %}
{% for room in event.rooms.all %}
{% include "chronos/partials/room.html" with room=room %}{% if not forloop.last %},{% endif %}
{% endfor %}
{% endif %}
{% if type.value == "teacher" and not event.groups.all and not event.rooms.all and event.title %}
<em>{{ event.title }}</em>
{% elif type.value == "group" and not event.teachers.all and not event.groups.all and event.title %}
<em>{{ event.title }}</em>
{% elif type.value == "room" and not event.teachers.all and not event.groups.all and event.title %}
<em>{{ event.title }}</em>
{% elif event.title %}
<br/>
<small>
<em>{{ event.title }}</em>
</small>
{% endif %}
</p>
</div>
<div class="lesson-with-sub"
style="{% include "chronos/partials/subject_colour.html" with subject=extra_lesson.subject %}">
<p>
{# Teacher or room > Display groups #}
{% if type.value == "teacher" or type.value == "room" %}
{% include "chronos/partials/groups.html" with groups=extra_lesson.groups.all %}
{% endif %}
{# Class or room > Display teachers #}
{% if type.value == "room" or type.value == "group" %}
{% include "chronos/partials/teachers.html" with teachers=extra_lesson.teachers.all %}
{% endif %}
{% include "chronos/partials/subject.html" with subject=extra_lesson.subject %}
{# Teacher or class > Display rooms #}
{% if type.value == "teacher" or type.value == "group" %}
{% include "chronos/partials/room.html" with room=extra_lesson.room %}
{% endif %}
{% if extra_lesson.comment %}
<br/>
<small>
<em>{{ extra_lesson.comment }}</em>
</small>
{% endif %}
</p>
</div>
<a href="{% url "timetable" "group" item.pk %}">
{{ item.short_name }}{% if not forloop.last %},{% endif %}
</a>
{{ item.short_name }}{% if not forloop.last %},{% endif %}
{% if groups.count == 1 and groups.0.parent_groups.all and request.site.preferences.chronos__use_parent_groups %}
{% include "chronos/partials/groups_part.html" with groups=groups.0.parent_groups.all no_collapsible=no_collapsible %}
{% include "chronos/partials/groups_part.html" with groups=groups.0.parent_groups.all %}
{% else %}
{% include "chronos/partials/groups_part.html" with groups=groups no_collapsible=no_collapsible %}
{% endif %}
{% if groups.count > request.site.preferences.chronos__shorten_groups_limit and request.user.person.preferences.chronos__shorten_groups and not no_collapsible %}
{% include "components/text_collapsible.html" with template="chronos/partials/group.html" qs=groups %}
{% else %}
{% for group in groups %}
{% include "chronos/partials/group.html" with item=group %}
{% endfor %}
{% endif %}
{% for group in groups %}
{% include "chronos/partials/group.html" with item=group %}
{% endfor %}
{% load i18n %}
{% if affected_teachers or affected_groups or absent_teachers or absent_groups %}
<div class="{% if not print %}card{% endif %}">
<div class="{% if not print %}card-content{% endif %}">
<div>
<div>
{% if absent_teachers %}
<div class="row no-margin">
<div class="col {% if not print %}s12 m3{% else %}s3{% endif %}">
<div class="col">
<strong class="truncate">
{% trans "Absent teachers" %}
</strong>
</div>
<div class="col {% if not print %}s12 m9{% else %}s9{% endif %} black-text-a">
<div class="col">
{% include "chronos/partials/teachers.html" with teachers=absent_teachers %}
</div>
</div>
{% endif %}
{% if absent_groups %}
<div class="row no-margin">
<div class="col {% if not print %}s12 m3{% else %}s3{% endif %}">
<div class="col">
<strong class="truncate">
{% trans "Absent groups" %}
</strong>
</div>
<div class="col {% if not print %}s12 m9{% else %}s9{% endif %} black-text-a">
<div class="col">
{% include "chronos/partials/groups.html" with groups=absent_groups no_collapsible=True %}
</div>
</div>
{% endif %}
{% if affected_teachers %}
<div class="row no-margin">
<div class="col {% if not print %}s12 m3{% else %}s3{% endif %}">
<div class="col">
<strong class="truncate">
{% trans "Affected teachers" %}
</strong>
</div>
<div class="col {% if not print %}s12 m9{% else %}s9{% endif %} black-text-a">
<div class="col">
{% include "chronos/partials/teachers.html" with teachers=affected_teachers %}
</div>
</div>
{% endif %}
{% if affected_groups %}
<div class="row no-margin">
<div class="col {% if not print %}s12 m3{% else %}s3{% endif %}">
<div class="col">
<strong class="truncate">
{% trans "Affected groups" %}
</strong>
</div>
<div class="col {% if not print %}s12 m9{% else %}s9{% endif %} black-text-a">
{% include "chronos/partials/groups.html" with groups=affected_groups no_collapsible=True %}
<div class="col">
{% include "chronos/partials/groups.html" with groups=affected_groups %}
</div>
</div>
{% endif %}
......
<span class="badge new blue center-align holiday-badge">{{ holiday.title }}</span>
{% load i18n %}
<div style="
{% with sub=lesson_period.get_substitution %}
{# Display background color only if lesson is not cancelled and it is not the old room #}
{% if not smart %}
{% include "chronos/partials/subject_colour.html" with subject=lesson_period.lesson.subject %}
{% elif not sub.cancelled and not lesson_period.get_substitution.cancelled_for_teachers and not lesson_period.replaced_by_event %}
{% if not type.value == "room" or lesson_period.room == lesson_period.get_room or lesson_period.get_room == el %}
{% if sub and sub.subject %}
{% include "chronos/partials/subject_colour.html" with subject=sub.subject %}
{% else %}
{% include "chronos/partials/subject_colour.html" with subject=lesson_period.lesson.subject %}
{% endif %}
{% endif %}
{% endif %}
{% endwith %}
"
{# Add CSS class for sub when it's a sub #}
class="{% if smart %}{% if lesson_period.get_substitution or lesson_period.replaced_by_event %}lesson-with-sub{% endif %}{% endif %}"
>
<p>
{% if lesson_period.replaced_by_event and smart %}
{% include "chronos/partials/groups.html" with groups=lesson_period.lesson.groups.all %}
{% include "chronos/partials/subject.html" with subject=lesson_period.lesson.subject %}
<br/>
<span class="badge new green">{% trans "Cancelled due to an event" %}</span>
{% elif lesson_period.get_substitution and smart %}
{% with sub=lesson_period.get_substitution %}
{# SUBSTITUTION #}
{% if type.value == "room" and lesson_period.room != lesson_period.get_room and lesson_period.get_room != el %}
{# When it's the old room, let it empty #}
{% elif sub.cancelled or sub.cancelled_for_teachers %}
{# When a badge (cancellation, etc.) exists, then display it with the teacher#}
{# Class or room > Display teacher #}
{% if type.value == "group" or type.value == "room" and lesson_period.lesson.teachers.all %}
{% include "chronos/partials/teachers.html" with teachers=lesson_period.lesson.teachers.all %}<br/>
{% endif %}
{# Badge #}
{% include "chronos/partials/subs/badge.html" with sub=sub %}
{% else %}
{# Display sub #}
{# Teacher or room > display classes #}
{% if type.value == "teacher" or type.value == "room" %}
{% include "chronos/partials/groups.html" with groups=lesson_period.lesson.groups.all %}
{% endif %}
{# Display teacher with tooltip #}
{% include "chronos/partials/subs/teachers.html" with type="substitution" el=sub %}
{# Display subject #}
{% include "chronos/partials/subs/subject.html" with type="substitution" el=sub %}
{# Teacher or class > display room #}
{% if type.value == "teacher" or type.value == "group" %}
{% include "chronos/partials/subs/room.html" with type="substitution" el=sub %}
{% endif %}
{% endif %}
{# Display the comment (e. g. work orders) #}
{% if sub.comment %}
<br>
<small>
<em>{{ sub.comment }}</em>
</small>
{% endif %}
{% endwith %}
{% else %}
{# Normal plan #}
{# Teacher or room > Display classes #}
{% if type.value == "teacher" or type.value == "room" %}
{# {{ element_container.element.classes }}#}
{% if lesson_period.lesson.groups %}
{% include "chronos/partials/groups.html" with groups=lesson_period.lesson.groups.all %}
{% endif %}
{% endif %}
{# Class or room > Display teacher #}
{% if type.value == "room" or type.value == "group" %}
{% include "chronos/partials/teachers.html" with teachers=lesson_period.lesson.teachers.all %}
{% endif %}
{# Display subject #}
{% include "chronos/partials/subject.html" with subject=lesson_period.lesson.subject %}
{# Teacher or class > Display room #}
{% if type.value == "teacher" or type.value == "group" %}
{% if lesson_period.room %}
{% include "chronos/partials/room.html" with room=lesson_period.room %}
{% endif %}
{% endif %}
{% endif %}
</p>
</div>
{% if holiday %}
<div class="row">
<div class="col s12">
<div class="card col s12 holiday-card">
<div class="card-content">
<p>
{% include "chronos/partials/holiday.html" with holiday=holiday %}<br/>
</p>
</div>
</div>
</div>
</div>
{% else %}
{% for row in timetable %}
<div class="row">
<div class="col s4">
{% if row.type == "period" %}
{% include "chronos/partials/period_time.html" with period=row.period periods=periods %}
{% endif %}
</div>
<div class="col s8">
{% if row.type == "period" %}
{% include "chronos/partials/elements.html" with elements=row.col %}
{% else %}
{% include "chronos/partials/supervision.html" with supervision=row.col %}
{% endif %}
</div>
</div>
{% endfor %}
{% endif %}
{% load data_helpers %}
<div class="card timetable-title-card">
<div class="card-content">
{# Lesson number #}
<span class="card-title left">
{{ period }}.
</span>
{# Time dimension of lesson #}
<div class="right timetable-time grey-text text-darken-2">
{% with period_obj=periods|get_dict:period %}
<span>{{ period_obj.0|time }}</span>
<br/>
<span>{{ period_obj.1|time }}</span>
{% endwith %}
</div>
</div>
</div>
{% if room %}
<span class="tooltipped" data-position="bottom" data-tooltip="{{ room.name }}">
<a href="{% url "timetable" "room" room.pk %}">
{{ room.short_name }}
</a>
</span>
{% endif %}
{% for room in rooms %}
{{ room.short_name|default:room.name }}{% if not forloop.last %},{% endif %}
{% endfor %}