diff --git a/aleksis/apps/chronos/migrations/0001_initial.py b/aleksis/apps/chronos/migrations/0001_initial.py index f7fe1b4949e026eeb5d51246b761d4b5c53d0a68..883b25bbf97f4bb0660e787d0a787b754b5efec4 100644 --- a/aleksis/apps/chronos/migrations/0001_initial.py +++ b/aleksis/apps/chronos/migrations/0001_initial.py @@ -305,8 +305,7 @@ class Migration(migrations.Migration): "verbose_name_plural": "Supervision substitutions", "ordering": ["date", "supervision"], }, - managers=[ - ], + managers=[], ), migrations.CreateModel( name="SupervisionArea", diff --git a/aleksis/apps/chronos/migrations/0007_unique_constraints.py b/aleksis/apps/chronos/migrations/0007_unique_constraints.py new file mode 100644 index 0000000000000000000000000000000000000000..754c3ae1c1c9c7a8a4e3ab6f616a459cf65d5298 --- /dev/null +++ b/aleksis/apps/chronos/migrations/0007_unique_constraints.py @@ -0,0 +1,85 @@ +# Generated by Django 3.2.3 on 2021-05-22 12:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('chronos', '0006_indexes'), + ] + + operations = [ + migrations.AlterModelOptions( + name='chronosglobalpermissions', + options={'managed': False, 'permissions': (('view_all_room_timetables', 'Can view all room timetables'), ('view_all_group_timetables', 'Can view all group timetables'), ('view_all_person_timetables', 'Can view all person timetables'), ('view_timetable_overview', 'Can view timetable overview'), ('view_lessons_day', 'Can view all lessons per day'))}, + ), + migrations.AlterField( + model_name='room', + name='short_name', + field=models.CharField(max_length=255, verbose_name='Short name'), + ), + migrations.AlterField( + model_name='subject', + name='name', + field=models.CharField(max_length=255, verbose_name='Long name'), + ), + migrations.AlterField( + model_name='subject', + name='short_name', + field=models.CharField(max_length=255, verbose_name='Short name'), + ), + migrations.AlterField( + model_name='timeperiod', + name='weekday', + field=models.PositiveSmallIntegerField(choices=[(0, 'Montag'), (1, 'Dienstag'), (2, 'Mittwoch'), (3, 'Donnerstag'), (4, 'Freitag'), (5, 'Samstag'), (6, 'Sonntag')], verbose_name='Week day'), + ), + migrations.AlterUniqueTogether( + name='lessonsubstitution', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='timeperiod', + unique_together=set(), + ), + migrations.AlterUniqueTogether( + name='validityrange', + unique_together=set(), + ), + migrations.AddConstraint( + model_name='absencereason', + constraint=models.UniqueConstraint(fields=('site_id', 'short_name'), name='unique_short_name_per_site_absence_reason'), + ), + migrations.AddConstraint( + model_name='break', + constraint=models.UniqueConstraint(fields=('site_id', 'short_name'), name='unique_short_name_per_site_break'), + ), + migrations.AddConstraint( + model_name='lessonsubstitution', + constraint=models.UniqueConstraint(fields=('lesson_period', 'week'), name='unique_period_per_week'), + ), + migrations.AddConstraint( + model_name='room', + constraint=models.UniqueConstraint(fields=('site_id', 'short_name'), name='unique_short_name_per_site_room'), + ), + migrations.AddConstraint( + model_name='subject', + constraint=models.UniqueConstraint(fields=('site_id', 'short_name'), name='unique_short_name_per_site_subject'), + ), + migrations.AddConstraint( + model_name='subject', + constraint=models.UniqueConstraint(fields=('site_id', 'name'), name='unique_name_per_site'), + ), + migrations.AddConstraint( + model_name='supervisionarea', + constraint=models.UniqueConstraint(fields=('site_id', 'short_name'), name='unique_short_name_per_site_supervision_area'), + ), + migrations.AddConstraint( + model_name='timeperiod', + constraint=models.UniqueConstraint(fields=('weekday', 'period', 'validity'), name='unique_period_per_range'), + ), + migrations.AddConstraint( + model_name='validityrange', + constraint=models.UniqueConstraint(fields=('school_term', 'date_start', 'date_end'), name='unique_dates_per_term'), + ), + ] diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index 9842363ca301fb7bf3ddcc9a3f7bd96a51f7f375..97f701ee1c3bc2fd591a7572dff96e3844c03654 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -116,7 +116,12 @@ class ValidityRange(ExtensibleModel): class Meta: verbose_name = _("Validity range") verbose_name_plural = _("Validity ranges") - unique_together = ["date_start", "date_end"] + constraints = [ + # Heads up: Uniqueness per term implies uniqueness per site + models.UniqueConstraint( + fields=["school_term", "date_start", "date_end"], name="unique_dates_per_term" + ), + ] indexes = [ models.Index(fields=["date_start", "date_end"], name="validity_date_start_date_end") ] @@ -297,7 +302,12 @@ class TimePeriod(ValidityRangeRelatedExtensibleModel): return period_choices class Meta: - unique_together = [["weekday", "period", "validity"]] + constraints = [ + # Heads up: Uniqueness per validity range implies validity per site + models.UniqueConstraint( + fields=["weekday", "period", "validity"], name="unique_period_per_range" + ), + ] ordering = ["weekday", "period"] indexes = [models.Index(fields=["time_start", "time_end"])] verbose_name = _("Time period") @@ -305,8 +315,8 @@ class TimePeriod(ValidityRangeRelatedExtensibleModel): class Subject(ExtensibleModel): - short_name = models.CharField(verbose_name=_("Short name"), max_length=255, unique=True) - name = models.CharField(verbose_name=_("Long name"), max_length=255, unique=True) + short_name = models.CharField(verbose_name=_("Short name"), max_length=255) + name = models.CharField(verbose_name=_("Long name"), max_length=255) colour_fg = ColorField(verbose_name=_("Foreground colour"), blank=True) colour_bg = ColorField(verbose_name=_("Background colour"), blank=True) @@ -318,10 +328,16 @@ class Subject(ExtensibleModel): ordering = ["name", "short_name"] verbose_name = _("Subject") verbose_name_plural = _("Subjects") + constraints = [ + models.UniqueConstraint( + fields=["site_id", "short_name"], name="unique_short_name_per_site_subject" + ), + models.UniqueConstraint(fields=["site_id", "name"], name="unique_name_per_site"), + ] class Room(ExtensibleModel): - short_name = models.CharField(verbose_name=_("Short name"), max_length=255, unique=True) + short_name = models.CharField(verbose_name=_("Short name"), max_length=255) name = models.CharField(verbose_name=_("Long name"), max_length=255) def __str__(self) -> str: @@ -335,6 +351,11 @@ class Room(ExtensibleModel): ordering = ["name", "short_name"] verbose_name = _("Room") verbose_name_plural = _("Rooms") + constraints = [ + models.UniqueConstraint( + fields=["site_id", "short_name"], name="unique_short_name_per_site_room" + ), + ] class Lesson(ValidityRangeRelatedExtensibleModel, GroupPropertiesMixin, TeacherPropertiesMixin): @@ -368,6 +389,7 @@ class Lesson(ValidityRangeRelatedExtensibleModel, GroupPropertiesMixin, TeacherP return f"{format_m2m(self.groups)}, {self.subject.short_name}, {format_m2m(self.teachers)}" class Meta: + # Heads up: Link to periods implies uniqueness per site ordering = ["validity__date_start", "subject"] verbose_name = _("Lesson") verbose_name_plural = _("Lessons") @@ -416,7 +438,6 @@ class LessonSubstitution(ExtensibleModel, TeacherPropertiesMixin, WeekRelatedMix return f"{self.lesson_period}, {date_format(self.date)}" class Meta: - unique_together = [["lesson_period", "week"]] ordering = [ "year", "week", @@ -427,7 +448,11 @@ class LessonSubstitution(ExtensibleModel, TeacherPropertiesMixin, WeekRelatedMix models.CheckConstraint( check=~Q(cancelled=True, subject__isnull=False), name="either_substituted_or_cancelled", - ) + ), + # Heads up: Link to period implies uniqueness per site + models.UniqueConstraint( + fields=["lesson_period", "week"], name="unique_period_per_week" + ), ] indexes = [ models.Index(fields=["week", "year"], name="substitution_week_year"), @@ -533,6 +558,7 @@ class LessonPeriod(WeekAnnotationMixin, TeacherPropertiesMixin, ExtensibleModel) return self._equal_lessons.next_lesson(self, -1) class Meta: + # Heads up: Link to period implies uniqueness per site ordering = [ "lesson__validity__date_start", "period__weekday", @@ -599,6 +625,11 @@ class AbsenceReason(ExtensibleModel): class Meta: verbose_name = _("Absence reason") verbose_name_plural = _("Absence reasons") + constraints = [ + models.UniqueConstraint( + fields=["site_id", "short_name"], name="unique_short_name_per_site_absence_reason" + ), + ] class Absence(SchoolTermRelatedExtensibleModel): @@ -667,6 +698,7 @@ class Absence(SchoolTermRelatedExtensibleModel): return _("Unknown absence") class Meta: + # Heads up: Link to period implies uniqueness per site ordering = ["date_start"] indexes = [models.Index(fields=["date_start", "date_end"])] verbose_name = _("Absence") @@ -698,6 +730,7 @@ class Exam(SchoolTermRelatedExtensibleModel): comment = models.TextField(verbose_name=_("Comment"), blank=True, null=True) class Meta: + # Heads up: Link to period implies uniqueness per site ordering = ["date"] indexes = [models.Index(fields=["date"])] verbose_name = _("Exam") @@ -765,6 +798,11 @@ class SupervisionArea(ExtensibleModel): ordering = ["name"] verbose_name = _("Supervision area") verbose_name_plural = _("Supervision areas") + constraints = [ + models.UniqueConstraint( + fields=["site_id", "short_name"], name="unique_short_name_per_site_supervision_area" + ), + ] class Break(ValidityRangeRelatedExtensibleModel): @@ -826,6 +864,11 @@ class Break(ValidityRangeRelatedExtensibleModel): indexes = [models.Index(fields=["after_period", "before_period"])] verbose_name = _("Break") verbose_name_plural = _("Breaks") + constraints = [ + models.UniqueConstraint( + fields=["site_id", "short_name"], name="unique_short_name_per_site_break" + ), + ] class Supervision(ValidityRangeRelatedExtensibleModel, WeekAnnotationMixin): @@ -1002,6 +1045,7 @@ class Event(SchoolTermRelatedExtensibleModel, GroupPropertiesMixin, TeacherPrope return self.teachers class Meta: + # Heads up: Link to period implies uniqueness per site ordering = ["date_start"] indexes = [ models.Index( @@ -1061,6 +1105,7 @@ class ExtraLesson( return self.subject class Meta: + # Heads up: Link to period implies uniqueness per site verbose_name = _("Extra lesson") verbose_name_plural = _("Extra lessons") indexes = [models.Index(fields=["week", "year"], name="extra_lesson_week_year")]