diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index c2037dc4ae2be3bbcb17275ab3eaa84c20b6d296..b0c487c952d68a8fec352a579a9bcd664da6d7c7 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -428,19 +428,7 @@ class Room(ExtensibleModel): ordering = ["name", "short_name"] -class Lesson(ExtensibleModel): - subject = models.ForeignKey("Subject", on_delete=models.CASCADE, related_name="lessons") - teachers = models.ManyToManyField("core.Person", related_name="lessons_as_teacher") - periods = models.ManyToManyField("TimePeriod", related_name="lessons", through="LessonPeriod") - groups = models.ManyToManyField("core.Group", related_name="lessons") - - date_start = models.DateField(verbose_name=_("Effective start date of lesson"), null=True) - date_end = models.DateField(verbose_name=_("Effective end date of lesson"), null=True) - - @property - def teacher_names(self, sep: Optional[str] = ", ") -> str: - return sep.join([teacher.full_name for teacher in self.teachers.all()]) - +class GroupPropertiesMixin: @property def group_names(self, sep: Optional[str] = ", ") -> str: return sep.join([group.short_name for group in self.groups.all()]) @@ -457,6 +445,22 @@ class Lesson(ExtensibleModel): def groups_to_show_names(self, sep: Optional[str] = ", ") -> str: return sep.join([group.short_name for group in self.groups_to_show]) + +class TeacherPropertiesMixin: + @property + def teacher_names(self, sep: Optional[str] = ", ") -> str: + return sep.join([teacher.full_name for teacher in self.teachers.all()]) + + +class Lesson(ExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin): + subject = models.ForeignKey("Subject", on_delete=models.CASCADE, related_name="lessons") + teachers = models.ManyToManyField("core.Person", related_name="lessons_as_teacher") + periods = models.ManyToManyField("TimePeriod", related_name="lessons", through="LessonPeriod") + groups = models.ManyToManyField("core.Group", related_name="lessons") + + date_start = models.DateField(verbose_name=_("Effective start date of lesson"), null=True) + date_end = models.DateField(verbose_name=_("Effective end date of lesson"), null=True) + def get_calendar_week(self, week: int): year = self.date_start.year if week < int(self.date_start.strftime("%V")): @@ -1016,10 +1020,13 @@ class TimetableQuerySet(models.QuerySet): class EventQuerySet(DateRangeQuerySet, TimetableQuerySet): - pass + def annotate_day(self, day: date): + """ Annotate all events in the QuerySet with the provided date. """ + return self.annotate(_date=models.Value(day, models.DateField())) -class Event(ExtensibleModel): + +class Event(ExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin): label_ = "event" objects = models.Manager.from_queryset(EventQuerySet)() @@ -1042,6 +1049,22 @@ class Event(ExtensibleModel): else: return _("Event {}".format(self.pk)) + @property + def period_from_on_day(self) -> int: + day = getattr(self, "_date", timezone.now().date()) + if day != self.date_start: + return TimePeriod.period_min + else: + return self.period_from.period + + @property + def period_to_on_day(self) -> int: + day = getattr(self, "_date", timezone.now().date()) + if day != self.date_end: + return TimePeriod.period_max + else: + return self.period_to.period + class Meta: ordering = ["date_start"] indexes = [models.Index(fields=["period_from", "period_to", "date_start", "date_end"])] @@ -1064,10 +1087,10 @@ class ExtraLessonQuerySet(TimetableQuerySet): ) def on_day(self, day:date): - self.within_dates(day, day) + return self.within_dates(day, day) -class ExtraLesson(ExtensibleModel): +class ExtraLesson(ExtensibleModel, GroupPropertiesMixin): label_ = "extra_lesson" objects = models.Manager.from_queryset(ExtraLessonQuerySet)() diff --git a/aleksis/apps/chronos/templates/chronos/partials/event.html b/aleksis/apps/chronos/templates/chronos/partials/event.html index b45ce48abc0d9414ba29220d935d25dc43a611ea..3ca850407977fd5a75a5e1b824086002c26943fc 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/event.html +++ b/aleksis/apps/chronos/templates/chronos/partials/event.html @@ -13,7 +13,7 @@ {# Teacher or class > Display rooms #} {% if type == "teacher" or type == "group" %} {% for room in event.rooms.all %} - {% include "chronos/partials/room.html" with room=room %} + {% include "chronos/partials/room.html" with room=room %}{% if not forloop.last %},{% endif %} {% endfor %} {% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/room.html b/aleksis/apps/chronos/templates/chronos/partials/room.html index d8985a8580219f78c693680fcc237d0e99391551..c96a7fd01975a86ac1bb66241a82872333d084bc 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/room.html +++ b/aleksis/apps/chronos/templates/chronos/partials/room.html @@ -1,5 +1,7 @@ -<span class="tooltipped" data-position="bottom" data-tooltip="{{ room.name }}"> - <a href="{% url "timetable" "room" room.pk %}"> - {{ room.short_name }} - </a> -</span> +{% 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/subs/colour.html b/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html index 61979467194ad62262a9c639d414db0dadefe260..833a24b2080d1ecb555ba94c8159fd8550ede7ce 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/colour.html @@ -6,4 +6,6 @@ {% endif %} {% elif item.type == "supervision_substitution" %} blue-text +{% elif item.type == "event" %} + purple-text {% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/comment.html b/aleksis/apps/chronos/templates/chronos/partials/subs/comment.html new file mode 100644 index 0000000000000000000000000000000000000000..694a0ce3a430b25f295c2ed75120ef2e0ed9db84 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/comment.html @@ -0,0 +1,5 @@ +{% if el.title %} + <em>{{ el.title }}</em> +{% elif el.comment %} + <em>{{ el.comment }}</em> +{% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html b/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html new file mode 100644 index 0000000000000000000000000000000000000000..d1a4da990b388458a9de5614acd17d988c4aac09 --- /dev/null +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/groups.html @@ -0,0 +1,5 @@ +{% if type == "substitution" %} + {% include "chronos/partials/groups.html" with groups=el.lesson_period.lesson.groups.all %} +{% elif type == "extra_lesson" or type == "event" %} + {% include "chronos/partials/groups.html" with groups=el.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 8e8d31bd2c48e14cc28330cc948e3d43882d6f2f..7d09cbebe5a320e98892ff637b99ae224e6f94f9 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/period.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/period.html @@ -1,6 +1,14 @@ <strong> {% if type == "substitution" %} {{ el.lesson_period.period.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 }}. diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html index 7461e2dce94dae3200588ab54bb6033513fa5301..837aaa9c86b9cc695851b0116257f7edd48db00f 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/room.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/room.html @@ -30,4 +30,10 @@ {{ 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/subject.html b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html index e6c9e86d016237276a2d5c894addba10c8a5942f..1210528a33f030c85fad581d47419ab41067e069 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/subject.html @@ -15,14 +15,14 @@ <strong>{{ el.subject.abbrev }}</strong> </span> {% elif el.subject and not el.lesson_period.lesson.subject %} - <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.subject.name }}"> - <strong>{{ el.subject.abbrev }}</strong> - </span> + {% include "chronos/partials/subject.html" with subject=el.subject %} {% else %} - <span data-position="bottom" class="tooltipped" data-tooltip="{{ el.lesson_period.lesson.subject.name }}"> - <strong>{{ el.lesson_period.lesson.subject.abbrev }}</strong> - </span> + {% include "chronos/partials/subject.html" with subject=el.lesson_period.lesson.subject %} {% endif %} {% elif type == "supervision_substitution" %} {% trans "Supervision" %} +{% elif type == "extra_lesson" %} + {% include "chronos/partials/subject.html" with subject=el.subject %} +{% elif type == "event" %} + {% trans "Event" %} {% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html b/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html index 4fbd63fea66282e17f77870ea3d16ec47fb31da4..4fa80d8fc7f82d8cae15010083ad4b93222bba41 100644 --- a/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html +++ b/aleksis/apps/chronos/templates/chronos/partials/subs/teachers.html @@ -22,4 +22,6 @@ <strong> {% include "chronos/partials/teachers.html" with teachers=el.teachers %} </strong> +{% elif type == "extra_lesson" or type == "event" %} + {% include "chronos/partials/teachers.html" with teachers=el.teachers.all %} {% endif %} diff --git a/aleksis/apps/chronos/templates/chronos/substitutions.html b/aleksis/apps/chronos/templates/chronos/substitutions.html index eff5da58288939ad8906126ae80666b903f75074..3f5d70b3c8f29bbebb68fa7593b73a5f2291c7c0 100644 --- a/aleksis/apps/chronos/templates/chronos/substitutions.html +++ b/aleksis/apps/chronos/templates/chronos/substitutions.html @@ -61,9 +61,7 @@ <tr class="{% include "chronos/partials/subs/colour.html" with item=item %}"> {# TODO: Extend support for purple (events) #} <td> - {% if item.type == "substitution" %} - {% include "chronos/partials/groups.html" with groups=item.el.lesson_period.lesson.groups.all %} - {% endif %} + {% include "chronos/partials/subs/groups.html" with type=item.type el=item.el %} </td> <td> {% include "chronos/partials/subs/period.html" with type=item.type el=item.el %} @@ -81,7 +79,7 @@ <span class="hide-on-med-and-up"> {% include "chronos/partials/subs/badge.html" with sub=item.el %} </span> - <em>{{ sub.comment|default:"" }}</em> + {% include "chronos/partials/subs/comment.html" with el=item.el %} </td> <td class="hide-on-small-and-down"> {% include "chronos/partials/subs/badge.html" with sub=item.el %} diff --git a/aleksis/apps/chronos/templates/chronos/substitutions_print.html b/aleksis/apps/chronos/templates/chronos/substitutions_print.html index f52f4d58ae177721e23554daf673f031c472d323..56dcf953109aac69b4fcd2316c853e3fe1a61cb2 100644 --- a/aleksis/apps/chronos/templates/chronos/substitutions_print.html +++ b/aleksis/apps/chronos/templates/chronos/substitutions_print.html @@ -54,9 +54,7 @@ <tr class="{% include "chronos/partials/subs/colour.html" with item=item %}"> <td> - {% if item.type == "substitution" %} - {% include "chronos/partials/groups.html" with groups=item.el.lesson_period.lesson.groups.all %} - {% endif %} + {% include "chronos/partials/subs/groups.html" with type=item.type el=item.el %} </td> <td> {% include "chronos/partials/subs/period.html" with type=item.type el=item.el %} @@ -72,7 +70,7 @@ </td> <td> {% include "chronos/partials/subs/badge.html" with sub=item.el %} - <em>{{ sub.comment|default:"" }}</em> + {% include "chronos/partials/subs/comment.html" with el=item.el %} </td> </tr> {% endfor %} diff --git a/aleksis/apps/chronos/util/build.py b/aleksis/apps/chronos/util/build.py index 9e7754bb7c258d24a5bcbddfc89371a3904bb639..5a8ebfe8e38dc55345b84f209a6f664aa4e5da57 100644 --- a/aleksis/apps/chronos/util/build.py +++ b/aleksis/apps/chronos/util/build.py @@ -299,6 +299,35 @@ def build_substitutions_list(wanted_day: date) -> List[dict]: } 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": "{}".format(extra_lesson.group_names), + "sort_b": "{}".format(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: + if event.groups.all(): + sort_a = event.group_names + else: + sort_a = "Z.".format(event.teacher_names) + + row = { + "type": "event", + "sort_a": sort_a, + "sort_b": "{}".format(event.period_from_on_day), + "el": event, + } + rows.append(row) + # Sort all items def sorter(row: dict): return row["sort_a"] + row["sort_b"]