diff --git a/aleksis/apps/chronos/frontend/index.js b/aleksis/apps/chronos/frontend/index.js index 0414c125ea5c304433084070a4eb627bf9bad62e..4749ac593afcd244d9d4f06b992ef6dea99c0225 100644 --- a/aleksis/apps/chronos/frontend/index.js +++ b/aleksis/apps/chronos/frontend/index.js @@ -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, + }, + }, ], }; diff --git a/aleksis/apps/chronos/managers.py b/aleksis/apps/chronos/managers.py index 45aecf55b7553f0bd61c74cc4fee19e65f05b8d9..6b2f4c3cc7a6ec190624e8db5c69c3edd3c818d6 100644 --- a/aleksis/apps/chronos/managers.py +++ b/aleksis/apps/chronos/managers.py @@ -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 diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index 35f4e0119151d0892c6bdc9433fd7686346d9ae7..3189999febab65467e7cd76ad6e273f438be089b 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -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": diff --git a/aleksis/apps/chronos/preferences.py b/aleksis/apps/chronos/preferences.py index d26653216f30acefac397ead20f97193546b0f0a..b3dc8362091693b313bda2d5f9493d8afa7e7701 100644 --- a/aleksis/apps/chronos/preferences.py +++ b/aleksis/apps/chronos/preferences.py @@ -1,13 +1,16 @@ 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 diff --git a/aleksis/apps/chronos/rules.py b/aleksis/apps/chronos/rules.py index 85a0d1c4a6b7e4bdef21edcaf1a294aa22d473c3..34d58360ce18353231f56acccf1a00818f479fbb 100644 --- a/aleksis/apps/chronos/rules.py +++ b/aleksis/apps/chronos/rules.py @@ -1,13 +1,11 @@ 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 diff --git a/aleksis/apps/chronos/schema/__init__.py b/aleksis/apps/chronos/schema/__init__.py index 253f56f183a9391bebf1cc2c4b1322864a4de48b..279003489d17818eda4487867de799fb1eeef213 100644 --- a/aleksis/apps/chronos/schema/__init__.py +++ b/aleksis/apps/chronos/schema/__init__.py @@ -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",) diff --git a/aleksis/apps/chronos/templates/chronos/partials/datepicker.html b/aleksis/apps/chronos/templates/chronos/partials/datepicker.html deleted file mode 100644 index f7d49f34e44569009b9af35aec1b0f26571773f4..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/datepicker.html +++ /dev/null @@ -1,30 +0,0 @@ -{% 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> diff --git a/aleksis/apps/chronos/templates/chronos/partials/elements.html b/aleksis/apps/chronos/templates/chronos/partials/elements.html deleted file mode 100644 index a7df614d20a2bc7fb2c203d90b0473756294da0a..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/elements.html +++ /dev/null @@ -1,13 +0,0 @@ -<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> diff --git a/aleksis/apps/chronos/templates/chronos/partials/event.html b/aleksis/apps/chronos/templates/chronos/partials/event.html deleted file mode 100644 index 3aa6b797639598c0e24051053f040fcd3f1aead4..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/event.html +++ /dev/null @@ -1,33 +0,0 @@ -<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> diff --git a/aleksis/apps/chronos/templates/chronos/partials/extra_lesson.html b/aleksis/apps/chronos/templates/chronos/partials/extra_lesson.html deleted file mode 100644 index 1113f011f0164be5a92f1dbe7ef3b32e8db07ed5..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/extra_lesson.html +++ /dev/null @@ -1,28 +0,0 @@ -<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> diff --git a/aleksis/apps/chronos/templates/chronos/partials/group.html b/aleksis/apps/chronos/templates/chronos/partials/group.html index d2e345d53df2bec9020b9de38139726744d1effe..3105150c7630d7d6fd550536366900d3ba3a3fa1 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/group.html +++ b/aleksis/apps/chronos/templates/chronos/partials/group.html @@ -1,3 +1 @@ -<a href="{% url "timetable" "group" item.pk %}"> - {{ item.short_name }}{% if not forloop.last %},{% endif %} -</a> +{{ item.short_name }}{% if not forloop.last %},{% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/groups.html b/aleksis/apps/chronos/templates/chronos/partials/groups.html index fb85c6d4dce53da5aea4ecef805fb52ddab4eed9..61bf6927b350f89cb2f191acc593dcb2758fb055 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/groups.html +++ b/aleksis/apps/chronos/templates/chronos/partials/groups.html @@ -1,5 +1,5 @@ {% 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 %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/groups_part.html b/aleksis/apps/chronos/templates/chronos/partials/groups_part.html index edafcb1977d04a18fb2a53651cf4ad6def3f0b19..d4c8f9f2a71c066f61b4c0a1c71d44674b190f0a 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/groups_part.html +++ b/aleksis/apps/chronos/templates/chronos/partials/groups_part.html @@ -1,7 +1,3 @@ -{% 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 %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/headerbox.html b/aleksis/apps/chronos/templates/chronos/partials/headerbox.html index 1ed1e7de07cdcf64b2afea35f93f40b74aca9bd2..c3862168a7f71127935987b99767fbed20d9ab79 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/headerbox.html +++ b/aleksis/apps/chronos/templates/chronos/partials/headerbox.html @@ -1,53 +1,53 @@ {% 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 %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/holiday.html b/aleksis/apps/chronos/templates/chronos/partials/holiday.html deleted file mode 100644 index 85fc42d4ffd50621da8fe04802b3c2d18a509b71..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/holiday.html +++ /dev/null @@ -1 +0,0 @@ -<span class="badge new blue center-align holiday-badge">{{ holiday.title }}</span> diff --git a/aleksis/apps/chronos/templates/chronos/partials/lesson.html b/aleksis/apps/chronos/templates/chronos/partials/lesson.html deleted file mode 100644 index 3fa1f75788ae6433bc1bf2ef9983a06df8fb59ff..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/lesson.html +++ /dev/null @@ -1,103 +0,0 @@ -{% 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> diff --git a/aleksis/apps/chronos/templates/chronos/partials/lessons_col.html b/aleksis/apps/chronos/templates/chronos/partials/lessons_col.html deleted file mode 100644 index 25ddedc47f422916b2e44f5419151e1a36ce8332..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/lessons_col.html +++ /dev/null @@ -1,30 +0,0 @@ -{% 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 %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/period_time.html b/aleksis/apps/chronos/templates/chronos/partials/period_time.html deleted file mode 100644 index ebd76dab6393c83332e07c1b194248480846323f..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/period_time.html +++ /dev/null @@ -1,20 +0,0 @@ -{% 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> diff --git a/aleksis/apps/chronos/templates/chronos/partials/room.html b/aleksis/apps/chronos/templates/chronos/partials/room.html deleted file mode 100644 index c96a7fd01975a86ac1bb66241a82872333d084bc..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/room.html +++ /dev/null @@ -1,7 +0,0 @@ -{% 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 %} 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 0000000000000000000000000000000000000000..e1f895c7d4aea8c03e02f71f0752e2a495e85c4a --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/rooms.html @@ -0,0 +1,3 @@ +{% for room in rooms %} + {{ room.short_name|default:room.name }}{% if not forloop.last %},{% endif %} +{% endfor %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/subject.html b/aleksis/apps/chronos/templates/chronos/partials/subject.html index e326c75a2419b28880578a32a1ffea4002194847..efed11727cb1b824f238d0e33d08cd2036f60837 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subject.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subject.html @@ -1,3 +1 @@ -<strong> - <span data-position="bottom" class="tooltipped" data-tooltip="{{ subject.name }}">{{ subject.short_name }}</span> -</strong> +{{ subject.short_name|default:subject.name }} diff --git a/aleksis/apps/chronos/templates/chronos/partials/subject_colour.html b/aleksis/apps/chronos/templates/chronos/partials/subject_colour.html deleted file mode 100644 index 4cead55119b2c4b799c13018dc175c2fe414f059..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/subject_colour.html +++ /dev/null @@ -1,6 +0,0 @@ -{% if subject.colour_fg %} - color: {{ subject.colour_fg }}; -{% endif %} -{% if subject.colour_bg and subject.colour_bg != subject.colour_fg %} - background-color: {{ subject.colour_bg }}; -{% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/badge.html b/aleksis/apps/chronos/templates/chronos/partials/subs/badge.html index c04c9dd331eeb4f271ae5e7c212b43a776c71a51..f0210546a37921a76c78a2e2d100f6b01092f1a9 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/badge.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/badge.html @@ -1,7 +1,5 @@ {% load i18n %} -{% if sub.cancelled %} +{% if el.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 833a24b2080d1ecb555ba94c8159fd8550ede7ce..06ccab2698661fd484ce14c9a06adb57551f225c 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 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 d1a4da990b388458a9de5614acd17d988c4aac09..447054384f19929af4872de104df01401254be2a 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html @@ -1,5 +1,11 @@ -{% 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 ef1d2838373b1537444aa6f2248ddc4c0e644267..9aa39762a4a0b88b58351b33ab0b02cc759b63b1 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/period.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/period.html @@ -1,19 +1,14 @@ +{% load i18n %} <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 }}. + {% if el.REFERENCE_OBJECT.slot_number_start and el.REFERENCE_OBJECT.slot_number_end %} + {% if el.REFERENCE_OBJECT.slot_number_start == el.REFERENCE_OBJECT.slot_number_end %} + {{ el.REFERENCE_OBJECT.slot_number_start }}. {% else %} - {{ el.period_from_on_day }}.–{{ el.period_to_on_day }}. + {{ el.REFERENCE_OBJECT.slot_number_start }}.–{{ el.REFERENCE_OBJECT.slot_number_end }}. {% endif %} - {% elif type == "supervision_substitution" %} - {% with break=el.supervision.break_item %} - {{ break.after_period_number }}./{{ break.before_period_number }}. - {% endwith %} + {% elif el.DTSTART.dt.time %} + {{ el.DTSTART.dt.time }}–{{ el.DTEND.dt.time }} + {% else %} + {% trans "all day" %} {% endif %} </strong> diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html deleted file mode 100644 index 94f2d3574992d23dffe3d6f9b03ffdd3e75cb042..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html +++ /dev/null @@ -1,39 +0,0 @@ -{% 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 %} -{% 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 0000000000000000000000000000000000000000..6a1ad0dedf556584323a97c9d4168497af81af35 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/rooms.html @@ -0,0 +1,11 @@ +{% 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 1b7a3c5ce4e0ba6f286778adfb846d5006b9e2cb..033d7b745a36aa3d859ad95b79e705b9ce4cfb87 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html @@ -1,28 +1,21 @@ {% 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 %} - {% endif %} -{% elif type == "supervision_substitution" %} +{% if el.name == "supervision" %} {% trans "Supervision" %} -{% elif type == "extra_lesson" %} +{% elif not el.amends.subject and not el.subject %} + {% if el.amends.title %} + <s>{{ el.amends.title }}</s> + {% endif %} + {% if el.title %} + <s>{{ el.title }}</s> + {% endif %} +{% elif el.cancelled %} + <s>{% include "chronos/partials/subject.html" with subject=el.amends.subject %}</s> +{% elif el.subject and el.amends.subject %} + <s>{% include "chronos/partials/subject.html" with subject=el.amends.subject %}</s> + → + <strong>{% include "chronos/partials/subject.html" with subject=el.subject %}</strong> +{% 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 4fa80d8fc7f82d8cae15010083ad4b93222bba41..9e08eeb5aca391263f0112010d58f58766af5f6a 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/partials/supervision.html b/aleksis/apps/chronos/templates/chronos/partials/supervision.html deleted file mode 100644 index 0fff8f5cfef6c5fa1e897c0705210ce3d1a3ca54..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/supervision.html +++ /dev/null @@ -1,29 +0,0 @@ -{% load i18n %} - -<div class="card lesson-card supervision-card {% if active_day and week_day == active_day %} z-depth-5 active {% endif %}"> - <div class="card-content"> - {% if supervision %} - <div style=" - {% if supervision.area.colour_fg %} - color: {{ supervision.area.colour_fg }}; - {% endif %} - {% if supervision.area.colour_bg and supervision.area.colour_bg != supervision.area.colour_fg %} - background-color: {{ supervision.area.colour_bg }}; - {% endif %} - " class="{% if supervision.get_substitution and smart %}lesson-with-sub{% endif %}"> - <p> - <strong>{% trans "Supervision" %}</strong> - <span data-position="bottom" class="tooltipped" - data-tooltip="{{ supervision.area.name }}" title="{{ supervision.area.name }}"> - {{ supervision.area.short_name }} - </span> - {% if supervision.get_substitution and smart %} - {% include "chronos/partials/subs/teachers.html" with type="supervision_substitution" el=supervision.get_substitution %} - {% elif type == "supervision_area" %} - {% include "chronos/partials/teachers.html" with teachers=supervision.teachers %} - {% endif %} - </p> - </div> - {% endif %} - </div> -</div> diff --git a/aleksis/apps/chronos/templates/chronos/partials/teachers.html b/aleksis/apps/chronos/templates/chronos/partials/teachers.html index d7ec97197f1309c651d0a8c07b0d1e387ecaade8..f506b4b14287cb35d8f4127cd21aafdd223f3287 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/teachers.html +++ b/aleksis/apps/chronos/templates/chronos/partials/teachers.html @@ -1,8 +1,3 @@ {% for teacher in teachers %} - <span data-position="bottom" class="tooltipped" - data-tooltip="{{ teacher }}"> - <a href="{% url "timetable" "teacher" teacher.pk %}"> - {{ teacher.short_name }}{% if not forloop.last %},{% endif %} - </a> - </span> + {{ teacher.short_name|default:teacher.full_name }}{% if not forloop.last %},{% endif %} {% endfor %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/today.html b/aleksis/apps/chronos/templates/chronos/partials/today.html deleted file mode 100644 index 8f3897c26b5de1d4c69d825d828e8df6630cf682..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/today.html +++ /dev/null @@ -1,2 +0,0 @@ -{% load i18n %} -<span class="badge new orange center-align holiday-badge">{% trans "Today" %}</span> diff --git a/aleksis/apps/chronos/templates/chronos/partials/week_select.html b/aleksis/apps/chronos/templates/chronos/partials/week_select.html deleted file mode 100644 index e9b57b84ceb1ce5443676cb9e6711640bb7bebd9..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/week_select.html +++ /dev/null @@ -1,47 +0,0 @@ -{% load i18n %} - -<div class="col s12 m6 right"> - <div class="col s2 no-print"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium right" href="{{ url_prev }}"> - <i class="material-icons iconify center" data-icon="mdi:chevron-left"></i> - </a> - </div> - - <div class="input-field col s8 no-margin hide-on-med-and-up"> - <select id="calendar-week-1"> - {% for week in weeks %} - <option value="{{ week.week }}" {% if week == wanted_week %} - selected {% endif %}>{% trans "CW" %} {{ week.week }} - ({{ week.0|date:"SHORT_DATE_FORMAT" }}–{{ week.6|date:"SHORT_DATE_FORMAT" }}) - </option> - {% endfor %} - </select> - </div> - - <div class="input-field col s8 no-margin hide-on-med-and-down"> - <select id="calendar-week-2"> - {% for week in weeks %} - <option value="{{ week.week }}" {% if week == wanted_week %} - selected {% endif %}>{% trans "CW" %} {{ week.week }} - ({{ week.0|date:"j.n." }}–{{ week.6|date:"SHORT_DATE_FORMAT" }}) - </option> - {% endfor %} - </select> - </div> - - <div class="input-field col s8 no-margin hide-on-small-and-down hide-on-large-only"> - <select id="calendar-week-3"> - {% for week in weeks %} - <option value="{{ week.week }}" {% if week == wanted_week %} - selected {% endif %}>{% trans "CW" %} {{ week.week }} ({{ week.0|date:"j.n." }}–{{ week.6|date:"j.n." }}) - </option> - {% endfor %} - </select> - </div> - - <div class="col s2 no-print"> - <a class="waves-effect waves-teal btn-flat btn-flat-medium left" href="{{ url_next }}"> - <i class="material-icons iconify center" data-icon="mdi:chevron-right"></i> - </a> - </div> -</div> diff --git a/aleksis/apps/chronos/templates/chronos/partials/week_timetable.html b/aleksis/apps/chronos/templates/chronos/partials/week_timetable.html deleted file mode 100644 index 22030da954fd3f96ccf324e4ef7b50ab76862835..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/partials/week_timetable.html +++ /dev/null @@ -1,71 +0,0 @@ -{# Week days #} -<div class="row"> - <div class="col {% if active_day %}s1{% else %}s2{% endif %}"> - - </div> - {# Show short weekdays on tablets #} - {% for weekday in weekdays_short %} - <div class="col s2 hide-on-large-only"> - <div class="card timetable-title-card {% if active_day and weekday.date == active_day %} z-depth-5 {% endif %}"> - <div class="card-content"> - <span class="card-title"> - {{ weekday.name }} - </span> - {% if smart %} - {{ weekday.date }} - {% if weekday.holiday %} - <br/>{% include "chronos/partials/holiday.html" with holiday=weekday.holiday %} - {% endif %} - {% if weekday.date == today %} - <br/> {% include "chronos/partials/today.html" %} - {% endif %} - {% endif %} - </div> - </div> - </div> - {% endfor %} - - {# Show long weekdays elsewere #} - {% for weekday in weekdays %} - <div class="col {% if weekday.date == active_day %} s3 {% else %} s2 {% endif %} hide-on-med-only"> - <div class="card timetable-title-card {% if active_day and weekday.date == active_day %} z-depth-5 {% endif %}"> - <div class="card-content"> - <span class="card-title"> - {{ weekday.name }} - </span> - {% if smart %} - {{ weekday.date }} - {% if weekday.holiday %} - <br/>{% include "chronos/partials/holiday.html" with holiday=weekday.holiday %} - {% endif %} - {% if weekday.date == today %} - <br/> {% include "chronos/partials/today.html" %} - {% endif %} - {% endif %} - </div> - </div> - </div> - {% endfor %} -</div> - -{# Lessons #} -{% for row in timetable %} -<div class="row"> - <div class="col {% if active_day %}s1{% else %}s2{% endif %}"> - {% if row.type == "period" %} - {% include "chronos/partials/period_time.html" with period=row.period periods=periods %} - {% endif %} - </div> - - {% for col in row.cols %} - {# A lesson #} - <div class="col {% if forloop.counter0 == active_day.weekday %} s3 {% else %} s2 {% endif %}"> - {% if row.type == "period" %} - {% include "chronos/partials/elements.html" with elements=col week_day=forloop.counter0 active_day=active_day.weekday %} - {% else %} - {% include "chronos/partials/supervision.html" with supervision=col %} - {% endif %} - </div> - {% endfor %} -</div> -{% endfor %} \ No newline at end of file diff --git a/aleksis/apps/chronos/templates/chronos/substitutions_print.html b/aleksis/apps/chronos/templates/chronos/substitutions_print.html index 743a2234e990bbafff8343e3ec7108e89995991c..04dc4cf24a5a171fa0638a9074693d025b1ccd02 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 affected_teachers=c.affected_teachers affected_groups=c.affected_groups %} <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,30 +47,30 @@ <tbody> {% for item in c.substitutions %} - {% ifchanged item.el.lesson_period.lesson.groups_to_show_names %} + {% ifchanged item.el.REFERENCE_OBJECT.group_names %} </tbody> <tbody class="{% cycle "striped" "not-striped" %}"> {% endifchanged %} - <tr class="{% include "chronos/partials/subs/colour.html" with item=item %}"> + <tr class="{% include "chronos/partials/subs/colour.html" with el=item.el.REFERENCE_OBJECT %}"> <td> - {% include "chronos/partials/subs/groups.html" with type=item.type el=item.el %} + {% include "chronos/partials/subs/groups.html" with el=item.el.REFERENCE_OBJECT %} </td> <td> - {% include "chronos/partials/subs/period.html" with type=item.type el=item.el item=item %} + {% include "chronos/partials/subs/period.html" with el=item.el %} </td> <td> - {% include "chronos/partials/subs/teachers.html" with type=item.type el=item.el %} + {% include "chronos/partials/subs/teachers.html" with el=item.el.REFERENCE_OBJECT %} </td> <td> - {% include "chronos/partials/subs/subject.html" with type=item.type el=item.el %} + {% include "chronos/partials/subs/subject.html" with el=item.el.REFERENCE_OBJECT %} </td> <td> - {% include "chronos/partials/subs/room.html" with type=item.type el=item.el %} + {% include "chronos/partials/subs/rooms.html" with el=item.el.REFERENCE_OBJECT %} </td> <td> - {% include "chronos/partials/subs/badge.html" with sub=item.el %} - {% include "chronos/partials/subs/comment.html" with el=item.el %} + {% include "chronos/partials/subs/badge.html" with el=item.el.REFERENCE_OBJECT %} + {% include "chronos/partials/subs/comment.html" with el=item.el.REFERENCE_OBJECT %} </td> </tr> {% endfor %} diff --git a/aleksis/apps/chronos/templates/chronos/timetable_print.html b/aleksis/apps/chronos/templates/chronos/timetable_print.html deleted file mode 100644 index 4769673610019ad98538d11db579a9cbe6c4601c..0000000000000000000000000000000000000000 --- a/aleksis/apps/chronos/templates/chronos/timetable_print.html +++ /dev/null @@ -1,55 +0,0 @@ -{% extends 'core/base_print.html' %} - -{% load data_helpers static i18n %} - -{% block extra_head %} - <link rel="stylesheet" href="{% static 'css/chronos/timetable.css' %}"> - <link rel="stylesheet" href="{% static 'css/chronos/timetable_print.css' %}"> -{% endblock %} - -{% block page_title %} - {% trans "Timetable" %} <i>{{ el.short_name }}</i> -{% endblock %} -{% block content %} - <div class="timetable-plan"> - {# Week days #} - <div class="row"> - <div class="col s2"> - - </div> - {% for weekday in weekdays_short %} - <div class="col s2"> - <div class="card timetable-title-card"> - <div class="card-content"> - <span class="card-title"> - {{ weekday.name }} - </span> - </div> - </div> - </div> - {% endfor %} - </div> - - {% for row in timetable %} - <div class="row"> - <div class="col s2"> - {% if row.type == "period" %} - {% include "chronos/partials/period_time.html" with period=row.period periods=periods %} - {% endif %} - </div> - - {% for col in row.cols %} - {# A lesson #} - <div class="col s2"> - {% if row.type == "period" %} - {% include "chronos/partials/elements.html" with elements=col %} - {% else %} - {% include "chronos/partials/supervision.html" with supervision=col %} - {% endif %} - </div> - {% endfor %} - </div> - {% endfor %} - </div> - -{% endblock %} diff --git a/aleksis/apps/chronos/urls.py b/aleksis/apps/chronos/urls.py index 7d55323042b9a23438975c589bd23967c3cbb396..0e3a3e7f10bf87613a2614fbd299ced106be0d96 100644 --- a/aleksis/apps/chronos/urls.py +++ b/aleksis/apps/chronos/urls.py @@ -8,4 +8,9 @@ urlpatterns = [ views.substitutions_print, name="substitutions_print", ), + path( + "substitutions/print/<str:day>/", + views.substitutions_print, + name="substitutions_print", + ), ] diff --git a/aleksis/apps/chronos/util/build.py b/aleksis/apps/chronos/util/build.py index 625998db7a20e293f0f8edc3e4f04f4109c3c9db..dd24a0452b93b3e34785e71f5ccdd5b58b908baa 100644 --- a/aleksis/apps/chronos/util/build.py +++ b/aleksis/apps/chronos/util/build.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from datetime import date +from datetime import date, datetime, time from typing import Union from django.apps import apps @@ -7,9 +7,11 @@ from django.apps import apps from calendarweek import CalendarWeek from aleksis.apps.chronos.managers import TimetableType +from aleksis.apps.chronos.models import SupervisionEvent 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") @@ -380,96 +382,57 @@ def build_timetable( return rows -def build_substitutions_list(wanted_day: date) -> list[dict]: +def build_substitutions_list(wanted_day: date) -> tuple[list[dict], set[Person], set[Group]]: rows = [] - - subs = LessonSubstitution.objects.on_day(wanted_day).order_by( - "lesson_period__lesson__groups", "lesson_period__period" + affected_teachers = set() + affected_groups = set() + + lesson_events = LessonEvent.get_single_events( + datetime.combine(wanted_day, time.min), + datetime.combine(wanted_day, time.max), + params={"amending": True}, + with_reference_object=True, ) - 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 + for lesson_event in lesson_events: + affected_teachers.update(lesson_event["REFERENCE_OBJECT"].teachers.all()) + affected_teachers.update(lesson_event["REFERENCE_OBJECT"].amends.teachers.all()) + affected_groups.update(lesson_event["REFERENCE_OBJECT"].groups.all()) + affected_groups.update(lesson_event["REFERENCE_OBJECT"].amends.groups.all()) row = { "type": "substitution", - "sort_a": sort_a, - "sort_b": str(sub.lesson_period.period.period), - "el": sub, - "start_period": start_period if start_period else sub.lesson_period.period.period, - "end_period": sub.lesson_period.period.period, + "sort_a": lesson_event["REFERENCE_OBJECT"].group_names, + "sort_b": str(lesson_event["DTSTART"]), + "el": lesson_event, } - if start_period: - start_period = None - rows.append(row) - # Get supervision substitutions - super_subs = SupervisionSubstitution.objects.filter(date=wanted_day) + supervision_events = SupervisionEvent.get_single_events( + datetime.combine(wanted_day, time.min), + datetime.combine(wanted_day, time.max), + params={"amending": True}, + with_reference_object=True, + ) + print(supervision_events) + + for supervision_event in supervision_events: + affected_teachers.update(supervision_event["REFERENCE_OBJECT"].teachers.all()) + affected_teachers.update(supervision_event["REFERENCE_OBJECT"].amends.teachers.all()) - 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, + "sort_a": "Z", + "sort_b": str(supervision_event["DTSTART"]), + "el": supervision_event, } - 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) + rows.sort(key=lambda row: row["sort_a"] + row["sort_b"]) - 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 - def sorter(row: dict): - return row["sort_a"] + row["sort_b"] - - rows.sort(key=sorter) - - return rows + return rows, affected_teachers, affected_groups def build_weekdays( diff --git a/aleksis/apps/chronos/util/change_tracker.py b/aleksis/apps/chronos/util/change_tracker.py index c9b8afb1b94c9bd5630a50b36d36e08ba58d31fd..669f5c4ee3f0cd6077189e019f58954ad3adf0f1 100644 --- a/aleksis/apps/chronos/util/change_tracker.py +++ b/aleksis/apps/chronos/util/change_tracker.py @@ -1,9 +1,4 @@ -from typing import Any, Optional - from django.contrib.contenttypes.models import ContentType -from django.db import transaction -from django.db.models import Model -from django.db.models.signals import m2m_changed, post_save, pre_delete from django.dispatch import Signal, receiver from celery import shared_task @@ -11,126 +6,9 @@ from reversion.models import Revision def _get_substitution_models(): - from aleksis.apps.chronos.models import ( - Event, - ExtraLesson, - LessonSubstitution, - SupervisionSubstitution, - ) - - return [LessonSubstitution, Event, ExtraLesson, SupervisionSubstitution] - - -class TimetableChange: - """A change to timetable models.""" - - def __init__( - self, - instance: Model, - changed_fields: Optional[dict[str, Any]] = None, - created: bool = False, - deleted: bool = False, - ): - self.instance = instance - self.changed_fields = changed_fields or {} - self.created = created - self.deleted = deleted - - -class TimetableDataChangeTracker: - """Helper class for tracking changes in timetable models by using signals.""" - - @classmethod - def get_models(cls) -> list[type[Model]]: - """Return all models that should be tracked.""" - from aleksis.apps.chronos.models import ( - Event, - ExtraLesson, - LessonSubstitution, - SupervisionSubstitution, - ) - - return [LessonSubstitution, Event, ExtraLesson, SupervisionSubstitution] - - def __init__(self): - self.changes = {} - self.m2m_fields = {} - - if transaction.get_autocommit(): - raise RuntimeError("Cannot use change tracker outside of transaction") - - for model in self.get_models(): - post_save.connect(self._handle_save, sender=model, weak=False) - pre_delete.connect(self._handle_delete, sender=model, weak=False) - - # Register signals for all relevant m2m fields - m2m_fields = { - getattr(model, f.name).through: f - for f in model._meta.get_fields() - if f.many_to_many - } - self.m2m_fields.update(m2m_fields) - for through_model, _field in m2m_fields.items(): - m2m_changed.connect(self._handle_m2m_changed, sender=through_model, weak=False) - - transaction.on_commit(self.close) - - def get_instance_key(self, instance: Model) -> str: - """Get unique string key for an instance.""" - return f"{instance._meta.model_name}_{instance.id}" - - def _add_change(self, change: TimetableChange): - """Add a change to the list of changes and update, if necessary.""" - key = self.get_instance_key(change.instance) - if key not in self.changes or change.deleted or change.created: - self.changes[key] = change - else: - self.changes[key].changed_fields.update(change.changed_fields) - - def _handle_save(self, sender: type[Model], instance: Model, created: bool, **kwargs): - """Handle the save signal.""" - change = TimetableChange(instance, created=created) - if not created: - change.changed_fields = instance.tracker.changed() - self._add_change(change) - - def _handle_delete(self, sender: type[Model], instance: Model, **kwargs): - """Handle the delete signal.""" - change = TimetableChange(instance, deleted=True) - self._add_change(change) - - def _handle_m2m_changed( - self, - sender: type[Model], - instance: Model, - action: str, - model: type[Model], - pk_set: set, - **kwargs, - ): - """Handle the m2m_changed signal.""" - if action in ["pre_add", "pre_remove", "pre_clear"]: - field_name = self.m2m_fields[sender].name - current_value = list(getattr(instance, field_name).all()) - if self.get_instance_key(instance) in self.changes: - change = self.changes[self.get_instance_key(instance)] - if field_name in change.changed_fields: - current_value = None - - if current_value is not None: - change = TimetableChange(instance, changed_fields={field_name: current_value}) - self._add_change(change) - - def close(self): - """Disconnect signals and send change signal.""" - for model in self.get_models(): - post_save.disconnect(self._handle_save, sender=model) - pre_delete.disconnect(self._handle_delete, sender=model) - - for through_model, _field in self.m2m_fields.items(): - m2m_changed.disconnect(self._handle_m2m_changed, sender=through_model) + from aleksis.apps.chronos.models import LessonEvent, SupervisionEvent - timetable_data_changed.send(sender=self, changes=self.changes) + return [LessonEvent, SupervisionEvent] chronos_revision_created = Signal() diff --git a/aleksis/apps/chronos/util/chronos_helpers.py b/aleksis/apps/chronos/util/chronos_helpers.py index 8e1640150383c7c00cfb145c74c9b8750c7a0c29..8e70ceae4b383dfcd72eb3aad80afd12d2f08175 100644 --- a/aleksis/apps/chronos/util/chronos_helpers.py +++ b/aleksis/apps/chronos/util/chronos_helpers.py @@ -1,11 +1,9 @@ -from datetime import datetime, timedelta +from datetime import date, datetime, timedelta from typing import TYPE_CHECKING, Optional from django.db.models import Count, Q from django.http import HttpRequest, HttpResponseNotFound from django.shortcuts import get_object_or_404 -from django.urls import reverse -from django.utils import timezone from guardian.core import ObjectPermissionChecker @@ -15,15 +13,12 @@ from aleksis.core.util.predicates import check_global_permission from ..managers import TimetableType from ..models import ( - Absence, LessonPeriod, LessonSubstitution, Supervision, SupervisionSubstitution, - TimePeriod, ) from .build import build_substitutions_list -from .js import date_unix if TYPE_CHECKING: from django.contrib.auth import get_user_model @@ -160,23 +155,13 @@ def get_rooms(user: "User"): def get_substitutions_context_data( - request: Optional[HttpRequest] = None, - year: Optional[int] = None, - month: Optional[int] = None, - day: Optional[int] = None, - is_print: bool = False, + wanted_day: date, number_of_days: Optional[int] = None, show_header_box: Optional[bool] = None, ): """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"] ) @@ -187,50 +172,41 @@ def get_substitutions_context_data( ) day_contexts = {} - if is_print: - 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)) - else: - day_contexts = {wanted_day: {"day": wanted_day}} + day = get_next_relevant_day(wanted_day) + for _i in range(day_number): + day_contexts[day] = {"day": day} - for day in day_contexts: - subs = build_substitutions_list(day) + subs, affected_teachers, affected_groups = build_substitutions_list(day) day_contexts[day]["substitutions"] = subs - day_contexts[day]["announcements"] = Announcement.for_timetables().on_date(day) + day_contexts[day]["announcements"] = Announcement.objects.on_date(day) if show_header_box: - subs = LessonSubstitution.objects.on_day(day).order_by( - "lesson_period__lesson__groups", "lesson_period__period" + day_contexts[day]["affected_teachers"] = sorted( + affected_teachers, key=lambda t: t.short_name or t.full_name ) - 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] - context["datepicker"] = { - "date": date_unix(wanted_day), - "dest": reverse("substitutions"), - } + day = get_next_relevant_day(day + timedelta(days=1)) - context["url_prev"], context["url_next"] = TimePeriod.get_prev_next_by_day( - wanted_day, "substitutions_by_date" - ) + context["days"] = day_contexts + + return context + + +def get_next_relevant_day(current: datetime | date) -> date: + """Get next relevant day for substitution plans.""" + relevant_days = get_site_preferences()["chronos__substitutions_relevant_days"] + change_time = get_site_preferences()["chronos__substitutions_day_change_time"] + if isinstance(current, datetime): + current_day = current.date() + if current.time() > change_time: + current_day += timedelta(days=1) else: - context["days"] = day_contexts + current_day = current - return context + while str(current_day.weekday()) not in relevant_days: + current_day += timedelta(days=1) + + return current_day diff --git a/aleksis/apps/chronos/views.py b/aleksis/apps/chronos/views.py index a45997ca580db9f8fa442be52d88054055e2661d..84d093728e50825f90358084909d828f48cc7d71 100644 --- a/aleksis/apps/chronos/views.py +++ b/aleksis/apps/chronos/views.py @@ -1,10 +1,10 @@ +from datetime import date, datetime from typing import Optional from django.http import HttpRequest, HttpResponse from rules.contrib.views import permission_required -from aleksis.core.decorators import pwa_cache from aleksis.core.util.pdf import render_pdf from .util.chronos_helpers import ( @@ -12,14 +12,12 @@ from .util.chronos_helpers import ( ) -@pwa_cache @permission_required("chronos.view_substitutions_rule") def substitutions_print( request: HttpRequest, - year: Optional[int] = None, - month: Optional[int] = None, - day: Optional[int] = None, + day: Optional[str] = None, ) -> HttpResponse: """View all substitutions on a specified day.""" - context = get_substitutions_context_data(request, year, month, day, is_print=True) + day = datetime.strptime(day, "%Y-%m-%d").date() if day else date.today() + context = get_substitutions_context_data(day) return render_pdf(request, "chronos/substitutions_print.html", context)