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-Lesrooster
  • elumKtrolte/AlekSIS-App-Lesrooster
  • 3caifricarha/AlekSIS-App-Lesrooster
  • 0randpenFtiabo/AlekSIS-App-Lesrooster
  • disclaMcremze/AlekSIS-App-Lesrooster
  • mogamuboun/AlekSIS-App-Lesrooster
  • 1lidisPtheoto/AlekSIS-App-Lesrooster
  • 8exitQconsko/AlekSIS-App-Lesrooster
  • 9scelasOyghe/AlekSIS-App-Lesrooster
  • misraMcaryo/AlekSIS-App-Lesrooster
  • 3mirummocgu/AlekSIS-App-Lesrooster
11 results
Show changes
Commits on Source (10)
......@@ -5,7 +5,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0050_alter_custommenuitem_icon_alter_notification_icon"),
("core", "0048_delete_personalicalurl"),
("lesrooster", "0001_initial"),
]
......
from copy import deepcopy
from datetime import date, datetime
from typing import Optional, Union
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Q
from django.utils import timezone
from django.utils.formats import date_format, time_format
from django.utils.functional import classproperty
......@@ -12,10 +14,11 @@ from calendarweek import CalendarWeek
from calendarweek.django import i18n_day_abbr_choices_lazy, i18n_day_name_choices_lazy
from recurrence.fields import RecurrenceField
from aleksis.apps.chronos.models import LessonEvent, SupervisionEvent
from aleksis.apps.cursus.models import Course, Subject
from aleksis.core.managers import CurrentSiteManagerWithoutMigrations
from aleksis.core.mixins import ExtensibleModel, ExtensiblePolymorphicModel
from aleksis.core.models import Person, Room, SchoolTerm
from aleksis.core.models import Holiday, Person, Room, SchoolTerm
from .managers import ValidityRangeQuerySet
......@@ -132,7 +135,7 @@ class Slot(ExtensiblePolymorphicModel):
day = date_ref
else:
day = self.get_date(date_ref)
return datetime.combine(day, self.time_start)
return timezone.make_aware(datetime.combine(day, self.time_start))
def get_datetime_end(self, date_ref: Union[CalendarWeek, int, date]) -> datetime:
"""Get datetime of lesson end in a specific week or on a specific day."""
......@@ -140,7 +143,7 @@ class Slot(ExtensiblePolymorphicModel):
day = date_ref
else:
day = self.get_date(date_ref)
return datetime.combine(day, self.time_end)
return timezone.make_aware(datetime.combine(day, self.time_end))
class Meta:
constraints = [
......@@ -158,6 +161,15 @@ class Slot(ExtensiblePolymorphicModel):
class Lesson(ExtensibleModel):
"""A lesson represents a single teaching event."""
lesson_event = models.OneToOneField(
LessonEvent,
on_delete=models.SET_NULL,
related_name="lesson",
verbose_name=_("Linked lesson event"),
blank=True,
null=True,
)
# A Course is the base of each lesson, it is its planing base
course = models.ForeignKey(
......@@ -216,6 +228,37 @@ class Lesson(ExtensibleModel):
if self.slot_start.validity != self.slot_end.validity:
raise ValidationError(_("The slots must be in the same validity range."))
def sync(self):
"""Sync the lesson with its lesson event."""
week_start = CalendarWeek.from_date(self.slot_start.validity_range.date_start)
week_end = CalendarWeek.from_date(self.slot_start.validity_range.date_end)
datetime_start = self.slot_start.get_datetime_start(week_start)
datetime_end = self.slot_end.get_datetime_end(week_start)
datetime_end_series = self.slot_end.get_datetime_end(week_end)
lesson_event, __ = LessonEvent.objects.update_or_create(
lesson=self,
defaults={
"course": self.course,
"subject": self.subject,
"datetime_start": datetime_start,
"datetime_end": datetime_end,
},
)
lesson_event.recurrences = deepcopy(self.recurrence)
lesson_event.recurrences.exdates += Holiday.get_ex_dates(
datetime_start, datetime_end_series, self.recurrence
)
lesson_event.save()
self.lesson_event = lesson_event
self.save()
lesson_event.groups.set(self.course.groups.all())
lesson_event.teachers.set(self.teachers.all())
lesson_event.rooms.set(self.rooms.all())
return lesson_event
class Meta:
# Heads up: Link to slot implies uniqueness per site
ordering = [
......@@ -229,6 +272,10 @@ class Lesson(ExtensibleModel):
class Break(Slot):
period_after = models.IntegerField(
verbose_name=_("Period after"),
)
class Meta:
verbose_name = _("Break")
verbose_name_plural = _("Breaks")
......@@ -237,6 +284,15 @@ class Break(Slot):
class Supervision(ExtensibleModel):
"""A supervision is a time period in which a teacher supervises a room."""
supervision_event = models.OneToOneField(
SupervisionEvent,
on_delete=models.SET_NULL,
related_name="supervision",
verbose_name=_("Linked supervision event"),
blank=True,
null=True,
)
rooms = models.ManyToManyField(
Room,
verbose_name=_("Rooms"),
......@@ -254,6 +310,216 @@ class Supervision(ExtensibleModel):
related_name="lr_supervisions",
)
# Recurrence rules allow to define a series of supervisions
# Common examples are weekly or every second week
recurrence = RecurrenceField(
verbose_name=_("Recurrence"),
blank=True,
null=True,
help_text=_("Leave empty for a single supervision."),
)
class Meta:
verbose_name = _("Supervision")
verbose_name_plural = _("Supervisions")
def sync(self):
"""Sync the supervision with its supervision event."""
week_start = CalendarWeek.from_date(self.slot.validity_range.date_start)
week_end = CalendarWeek.from_date(self.slot.validity_range.date_end)
datetime_start = self.slot.get_datetime_start(week_start)
datetime_end = self.slot.get_datetime_end(week_start)
datetime_end_series = self.slot.get_datetime_end(week_end)
supervision_event, __ = SupervisionEvent.objects.update_or_create(
supervision=self,
defaults={
"datetime_start": datetime_start,
"datetime_end": datetime_end,
},
)
supervision_event.recurrences = deepcopy(self.recurrence)
supervision_event.recurrences.exdates += Holiday.get_ex_dates(
datetime_start, datetime_end_series, self.recurrence
)
supervision_event.save()
self.supervision_event = supervision_event
self.save()
supervision_event.teachers.set(self.teachers.all())
supervision_event.rooms.set(self.rooms.all())
return supervision_event
class Substitution(ExtensibleModel):
lesson_event = models.OneToOneField(
LessonEvent,
on_delete=models.SET_NULL,
related_name="substitution",
verbose_name=_("Linked lesson event"),
blank=True,
null=True,
)
lesson = models.ForeignKey(Lesson, models.CASCADE, "substitutions", verbose_name=_("Lesson"))
date = models.DateField(verbose_name=_("Date"))
rooms = models.ManyToManyField(
Room,
verbose_name=_("Rooms"),
related_name="lr_substitutions",
blank=True,
)
teachers = models.ManyToManyField(
Person,
verbose_name=_("Teachers"),
related_name="lr_substitutions",
blank=True,
)
subject = models.ForeignKey(
Subject,
on_delete=models.CASCADE,
verbose_name=_("Subject"),
related_name="substitutions",
blank=True,
null=True,
)
cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled?"))
comment = models.TextField(verbose_name=_("Comment"), blank=True)
def clean(self) -> None:
if self.subject and self.cancelled:
raise ValidationError(_("Lessons can only be either substituted or cancelled."))
@property
def time_range(self) -> (timezone.datetime, timezone.datetime):
"""Get the time range of this substitution."""
return timezone.datetime.combine(
self.date, self.lesson_period.period.time_start
), timezone.datetime.combine(self.date, self.lesson_period.period.time_end)
def __str__(self):
return f"{self.lesson}, {date_format(self.date)}"
def sync(self):
"""Sync the lesson with its lesson event."""
week = CalendarWeek.from_date(self.date)
print(self.lesson.__dict__, self.lesson.lesson_event)
if not self.lesson.lesson_event:
return None
lesson_event, __ = LessonEvent.objects.update_or_create(
substitution=self,
defaults={
"amends": self.lesson.lesson_event,
"course": self.lesson.course,
"subject": self.subject,
"datetime_start": self.lesson.slot_start.get_datetime_start(week),
"datetime_end": self.lesson.slot_end.get_datetime_end(week),
"cancelled": self.cancelled,
"comment": self.comment,
},
)
self.lesson_event = lesson_event
self.save()
lesson_event.groups.set(self.lesson.course.groups.all())
lesson_event.teachers.set(self.teachers.all())
lesson_event.rooms.set(self.rooms.all())
print(lesson_event)
return lesson_event
class Meta:
ordering = [
"date",
"lesson__slot_start__weekday",
"lesson__slot_start__period",
]
constraints = [
models.CheckConstraint(
check=~Q(cancelled=True, subject__isnull=False),
name="lr_either_substituted_or_cancelled",
),
# Heads up: Link to period implies uniqueness per site
models.UniqueConstraint(fields=["lesson", "date"], name="unique_lesson_per_date"),
]
indexes = [
models.Index(fields=["date"], name="substitution_date"),
models.Index(fields=["lesson"], name="substitution_lesson"),
]
verbose_name = _("Substitution")
verbose_name_plural = _("Substitutions")
class SupervisionSubstitution(ExtensibleModel):
supervision_event = models.OneToOneField(
SupervisionEvent,
on_delete=models.SET_NULL,
related_name="supervision_substitution",
verbose_name=_("Linked supervision event"),
blank=True,
null=True,
)
supervision = models.ForeignKey(
Supervision, models.CASCADE, "substitutions", verbose_name=_("Supervision")
)
date = models.DateField(verbose_name=_("Date"))
teachers = models.ManyToManyField(
Person,
verbose_name=_("Teachers"),
related_name="lr_supervision_substitutions",
blank=True,
)
cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled?"))
comment = models.TextField(verbose_name=_("Comment"), blank=True)
@property
def time_range(self) -> (timezone.datetime, timezone.datetime):
"""Get the time range of this supervision."""
return timezone.datetime.combine(
self.date, self.supervision.slot.period.time_start
), timezone.datetime.combine(self.date, self.supervision.slot.period.time_end)
def __str__(self):
return f"{self.supervision}, {date_format(self.date)}"
def sync(self):
"""Sync the supervision with its supervision event."""
week = CalendarWeek.from_date(self.date)
if not self.supervision.supervision_event:
return None
supervision_event, __ = SupervisionEvent.objects.update_or_create(
supervision_substitution=self,
defaults={
"amends": self.supervision.supervision_event,
"datetime_start": self.supervision.slot.get_datetime_start(week),
"datetime_end": self.supervision.slot.get_datetime_end(week),
"cancelled": self.cancelled,
"comment": self.comment,
},
)
self.supervision_event = supervision_event
self.save()
supervision_event.teachers.set(self.teachers.all())
return supervision_event
class Meta:
ordering = [
"date",
"supervision__slot__weekday",
"supervision__slot__period",
]
indexes = [
models.Index(fields=["date"], name="super_substitution_date"),
models.Index(fields=["supervision"], name="super_substitution_supervision"),
]
verbose_name = _("Supervision Substitution")
verbose_name_plural = _("Supervision Substitutions")