Skip to content
Snippets Groups Projects
Commit 87511023 authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Merge branch '6-drop-substitution-stuff' into 'master'

Resolve "Drop substitution stuff"

Closes #6

See merge request !27
parents 0f8acffd 63dcaaa2
No related branches found
No related tags found
1 merge request!27Resolve "Drop substitution stuff"
Pipeline #172870 failed
......@@ -26,13 +26,11 @@ class DefaultConfig(AppConfig):
# Configure change tracking for models to sync changes with LessonEvent in Chronos
from .models import (
Lesson,
Substitution,
Supervision,
SupervisionSubstitution,
ValidityRange,
)
models = [Lesson, Supervision, Substitution, SupervisionSubstitution]
models = [Lesson, Supervision]
for model in models:
signals.post_save.connect(
......@@ -53,10 +51,6 @@ class DefaultConfig(AppConfig):
m2m_changed_handler,
sender=Supervision.rooms.through,
)
signals.m2m_changed.connect(
m2m_changed_handler,
sender=Substitution.rooms.through,
)
signals.post_save.connect(create_time_grid_for_new_validity_range, sender=ValidityRange)
......
# Generated by Django 4.2.10 on 2024-02-28 18:58
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('lesrooster', '0015_drop_site'),
]
operations = [
migrations.RemoveField(
model_name='supervisionsubstitution',
name='subject',
),
migrations.RemoveField(
model_name='supervisionsubstitution',
name='supervision',
),
migrations.RemoveField(
model_name='supervisionsubstitution',
name='supervision_event',
),
migrations.RemoveField(
model_name='supervisionsubstitution',
name='teachers',
),
migrations.DeleteModel(
name='Substitution',
),
migrations.DeleteModel(
name='SupervisionSubstitution',
),
]
......@@ -469,203 +469,6 @@ class Supervision(TeacherPropertiesMixin, RoomPropertiesMixin, ExtensibleModel):
return supervision_event
class Substitution(RoomPropertiesMixin, TeacherPropertiesMixin, ExtensibleModel):
"""A substitution is a lesson that is changed."""
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 get_teachers(self) -> QuerySet[Person]:
return self.teachers.all()
def get_rooms(self) -> QuerySet[Room]:
return self.rooms.all()
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) -> LessonEvent:
"""Sync the lesson with its lesson event."""
week = CalendarWeek.from_date(self.date)
if not self.lesson.lesson_event:
return None
lesson_event = self.lesson_event if self.lesson_event else LessonEvent()
lesson_event.amends = self.lesson.lesson_event
lesson_event.course = self.lesson.course
lesson_event.subject = self.subject
lesson_event.datetime_start = self.lesson.slot_start.get_datetime_start(week)
lesson_event.datetime_end = self.lesson.slot_end.get_datetime_end(week)
lesson_event.cancelled = self.cancelled
lesson_event.comment = self.comment
lesson_event.save()
lesson_event.groups.set(self.lesson.course.groups.all())
lesson_event.teachers.set(self.teachers.all())
lesson_event.rooms.set(self.rooms.all())
if self.lesson_event != lesson_event:
self.lesson_event = lesson_event
self.save()
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",
),
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(TeacherPropertiesMixin, ExtensibleModel):
"""A supervision substitution is a supervision that is changed."""
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,
)
subject = models.ForeignKey(
Subject,
on_delete=models.CASCADE,
verbose_name=_("Subject"),
related_name="lr_supervision_substitutions",
blank=True,
null=True,
)
cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled?"))
comment = models.TextField(verbose_name=_("Comment"), blank=True)
def get_teachers(self) -> QuerySet[Person]:
return self.teachers.all()
@property
def time_range(self) -> (timezone.datetime, timezone.datetime):
"""Get the time range of this supervision."""
return timezone.datetime.combine(
self.date, self.supervision.break_slot.period.time_start
), timezone.datetime.combine(self.date, self.supervision.break_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 = self.supervision_event if self.supervision_event else SupervisionEvent()
supervision_event.amends = self.supervision.supervision_event
supervision_event.datetime_start = self.supervision.break_slot.get_datetime_start(week)
supervision_event.datetime_end = self.supervision.break_slot.get_datetime_end(week)
supervision_event.cancelled = self.cancelled
supervision_event.comment = self.comment
supervision_event.subject = self.subject
supervision_event.save()
supervision_event.teachers.set(self.teachers.all())
if self.supervision_event != supervision_event:
self.supervision_event = supervision_event
self.save()
return supervision_event
class Meta:
ordering = [
"date",
"supervision__break_slot__weekday",
"supervision__break_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")
class TimeboundCourseConfig(ExtensibleModel):
"""A timebound course config is the specific configuration of a course.
......
......@@ -12,7 +12,6 @@ from .models import (
Lesson,
Slot,
Supervision,
SupervisionSubstitution,
TimeboundCourseConfig,
TimeGrid,
ValidityRange,
......@@ -162,40 +161,6 @@ delete_supervision_predicate = view_supervision_predicate & (
add_perm("lesrooster.delete_supervision_rule", delete_supervision_predicate)
# Supervision substitutions
view_supervision_substitutions_predicate = has_person & (
has_global_perm("lesrooster.view_supervisionsubstitution")
| has_any_object("lesrooster.view_supervisionsubstitution", SupervisionSubstitution)
)
add_perm("lesrooster.view_supervisionsubstitutions_rule", view_supervision_substitutions_predicate)
view_supervision_substitution_predicate = has_person & (
has_global_perm("lesrooster.view_supervisionsubstitution")
| has_object_perm("lesrooster.view_supervisionsubstitution")
)
add_perm("lesrooster.view_supervisionsubstitution_rule", view_supervision_substitution_predicate)
create_supervision_substitution_predicate = has_person & has_global_perm(
"lesrooster.add_supervisionsubstitution"
)
add_perm(
"lesrooster.create_supervisionsubstitution_rule", create_supervision_substitution_predicate
)
edit_supervision_substitution_predicate = view_supervision_substitution_predicate & (
has_global_perm("lesrooster.change_supervisionsubstitution")
| has_object_perm("lesrooster.change_supervisionsubstitution")
)
add_perm("lesrooster.edit_supervisionsubstitution_rule", edit_supervision_substitution_predicate)
delete_supervision_substitution_predicate = view_supervision_substitution_predicate & (
has_global_perm("lesrooster.delete_supervisionsubstitution")
| has_object_perm("lesrooster.delete_supervisionsubstitution")
)
add_perm(
"lesrooster.delete_supervisionsubstitution_rule", delete_supervision_substitution_predicate
)
# Timebound course configs
view_timebound_course_configs_predicate = has_person & (
......
......@@ -38,19 +38,12 @@ def create_time_grid_for_new_validity_range(sender, instance, created, **kwargs)
def publish_validity_range(sender, instance, created, **kwargs):
from ..models import Lesson, Substitution, Supervision, SupervisionSubstitution
from ..models import Lesson, Supervision
# FIXME Move this to a background job
objs_to_update = (
list(Lesson.objects.filter(slot_start__time_grid__validity_range=instance))
+ list(Supervision.objects.filter(break_slot__time_grid__validity_range=instance))
+ list(Substitution.objects.filter(lesson__slot_start__time_grid__validity_range=instance))
+ list(
SupervisionSubstitution.objects.filter(
supervision__break_slot__time_grid__validity_range=instance
)
)
)
objs_to_update = list(
Lesson.objects.filter(slot_start__time_grid__validity_range=instance)
) + list(Supervision.objects.filter(break_slot__time_grid__validity_range=instance))
for obj in objs_to_update:
logging.info(f"Syncing object {obj} ({type(obj)}, {obj.pk})")
obj.sync()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment