From f9991cec416c7d16bfe3c47397a9c7c1eba7f03d Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Sun, 10 Jan 2021 16:56:33 +0100
Subject: [PATCH] Update LessonDocumentation and PersonalNote to link also to
 events and extra lessons

---
 .../migrations/0008_events_extra_lessons.py   | 91 +++++++++++++++++++
 aleksis/apps/alsijil/models.py                | 67 ++++++++++++--
 2 files changed, 150 insertions(+), 8 deletions(-)
 create mode 100644 aleksis/apps/alsijil/migrations/0008_events_extra_lessons.py

diff --git a/aleksis/apps/alsijil/migrations/0008_events_extra_lessons.py b/aleksis/apps/alsijil/migrations/0008_events_extra_lessons.py
new file mode 100644
index 000000000..606a92161
--- /dev/null
+++ b/aleksis/apps/alsijil/migrations/0008_events_extra_lessons.py
@@ -0,0 +1,91 @@
+# Generated by Django 3.1.5 on 2021-01-10 15:48
+
+import aleksis.apps.chronos.util.date
+import django.contrib.sites.managers
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('chronos', '0004_substitution_extra_lesson_year'),
+        ('alsijil', '0007_personal_note_lesson_documentation_year'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='lessondocumentation',
+            options={'ordering': ['year', 'week', 'lesson_period__period__weekday', 'lesson_period__period__period'], 'verbose_name': 'Lesson documentation', 'verbose_name_plural': 'Lesson documentations'},
+        ),
+        migrations.AlterModelOptions(
+            name='personalnote',
+            options={'ordering': ['year', 'week', 'lesson_period__period__weekday', 'lesson_period__period__period', 'person__last_name', 'person__first_name'], 'verbose_name': 'Personal note', 'verbose_name_plural': 'Personal notes'},
+        ),
+        migrations.AddField(
+            model_name='lessondocumentation',
+            name='event',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='documentations', to='chronos.event'),
+        ),
+        migrations.AddField(
+            model_name='lessondocumentation',
+            name='extra_lesson',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='documentations', to='chronos.extralesson'),
+        ),
+        migrations.AddField(
+            model_name='personalnote',
+            name='event',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='personal_notes', to='chronos.event'),
+        ),
+        migrations.AddField(
+            model_name='personalnote',
+            name='extra_lesson',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='personal_notes', to='chronos.extralesson'),
+        ),
+        migrations.AlterField(
+            model_name='lessondocumentation',
+            name='lesson_period',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='documentations', to='chronos.lessonperiod'),
+        ),
+        migrations.AlterField(
+            model_name='lessondocumentation',
+            name='week',
+            field=models.IntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='lessondocumentation',
+            name='year',
+            field=models.IntegerField(blank=True, default=aleksis.apps.chronos.util.date.get_current_year, null=True, verbose_name='Year'),
+        ),
+        migrations.AlterField(
+            model_name='personalnote',
+            name='lesson_period',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='personal_notes', to='chronos.lessonperiod'),
+        ),
+        migrations.AlterField(
+            model_name='personalnote',
+            name='week',
+            field=models.IntegerField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='personalnote',
+            name='year',
+            field=models.IntegerField(blank=True, default=aleksis.apps.chronos.util.date.get_current_year, null=True, verbose_name='Year'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='lessondocumentation',
+            unique_together=set(),
+        ),
+        migrations.AlterUniqueTogether(
+            name='personalnote',
+            unique_together=set(),
+        ),
+        migrations.AddConstraint(
+            model_name='lessondocumentation',
+            constraint=models.CheckConstraint(check=models.Q(models.Q(('event__isnull', True), ('extra_lesson__isnull', True), ('lesson_period__isnull', False), ('week__isnull', False), ('year__isnull', False)), models.Q(('event__isnull', False), ('extra_lesson__isnull', True), ('lesson_period__isnull', True), ('week__isnull', True), ('year__isnull', True)), models.Q(('event__isnull', True), ('extra_lesson__isnull', False), ('lesson_period__isnull', True), ('week__isnull', True), ('year__isnull', True)), _connector='OR'), name='one_relation_only_lesson_documentation'),
+        ),
+        migrations.AddConstraint(
+            model_name='personalnote',
+            constraint=models.CheckConstraint(check=models.Q(models.Q(('event__isnull', True), ('extra_lesson__isnull', True), ('lesson_period__isnull', False), ('week__isnull', False), ('year__isnull', False)), models.Q(('event__isnull', False), ('extra_lesson__isnull', True), ('lesson_period__isnull', True), ('week__isnull', True), ('year__isnull', True)), models.Q(('event__isnull', True), ('extra_lesson__isnull', False), ('lesson_period__isnull', True), ('week__isnull', True), ('year__isnull', True)), _connector='OR'), name='one_relation_only_personal_note'),
+        ),
+    ]
diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py
index dc92c009f..38526f78b 100644
--- a/aleksis/apps/alsijil/models.py
+++ b/aleksis/apps/alsijil/models.py
@@ -1,4 +1,6 @@
 from django.db import models
+from django.db.models.constraints import CheckConstraint
+from django.db.models.query_utils import Q
 from django.urls import reverse
 from django.utils.formats import date_format
 from django.utils.translation import gettext_lazy as _
@@ -46,6 +48,31 @@ class ExcuseType(ExtensibleModel):
         verbose_name_plural = _("Excuse types")
 
 
+lesson_related_constraint_q = (
+    Q(
+        lesson_period__isnull=False,
+        event__isnull=True,
+        extra_lesson__isnull=True,
+        week__isnull=False,
+        year__isnull=False,
+    )
+    | Q(
+        lesson_period__isnull=True,
+        event__isnull=False,
+        extra_lesson__isnull=True,
+        week__isnull=True,
+        year__isnull=True,
+    )
+    | Q(
+        lesson_period__isnull=True,
+        event__isnull=True,
+        extra_lesson__isnull=False,
+        week__isnull=True,
+        year__isnull=True,
+    )
+)
+
+
 class PersonalNote(ExtensibleModel, WeekRelatedMixin):
     """A personal note about a single person.
 
@@ -65,11 +92,19 @@ class PersonalNote(ExtensibleModel, WeekRelatedMixin):
     person = models.ForeignKey("core.Person", models.CASCADE, related_name="personal_notes")
     groups_of_person = models.ManyToManyField("core.Group", related_name="+")
 
-    week = models.IntegerField()
-    year = models.IntegerField(verbose_name=_("Year"), default=get_current_year)
+    week = models.IntegerField(blank=True, null=True)
+    year = models.IntegerField(
+        verbose_name=_("Year"), default=get_current_year, blank=True, null=True
+    )
 
     lesson_period = models.ForeignKey(
-        "chronos.LessonPeriod", models.CASCADE, related_name="personal_notes"
+        "chronos.LessonPeriod", models.CASCADE, related_name="personal_notes", blank=True, null=True
+    )
+    event = models.ForeignKey(
+        "chronos.Event", models.CASCADE, related_name="personal_notes", blank=True, null=True
+    )
+    extra_lesson = models.ForeignKey(
+        "chronos.ExtraLesson", models.CASCADE, related_name="personal_notes", blank=True, null=True
     )
 
     absent = models.BooleanField(default=False)
@@ -123,7 +158,6 @@ class PersonalNote(ExtensibleModel, WeekRelatedMixin):
     class Meta:
         verbose_name = _("Personal note")
         verbose_name_plural = _("Personal notes")
-        unique_together = [["lesson_period", "week", "person"]]
         ordering = [
             "year",
             "week",
@@ -132,6 +166,11 @@ class PersonalNote(ExtensibleModel, WeekRelatedMixin):
             "person__last_name",
             "person__first_name",
         ]
+        constraints = [
+            CheckConstraint(
+                check=lesson_related_constraint_q, name="one_relation_only_personal_note"
+            )
+        ]
 
 
 class LessonDocumentation(ExtensibleModel, WeekRelatedMixin):
@@ -142,11 +181,19 @@ class LessonDocumentation(ExtensibleModel, WeekRelatedMixin):
 
     data_checks = [LessonDocumentationOnHolidaysDataCheck]
 
-    week = models.IntegerField()
-    year = models.IntegerField(verbose_name=_("Year"), default=get_current_year)
+    week = models.IntegerField(blank=True, null=True)
+    year = models.IntegerField(
+        verbose_name=_("Year"), default=get_current_year, blank=True, null=True
+    )
 
     lesson_period = models.ForeignKey(
-        "chronos.LessonPeriod", models.CASCADE, related_name="documentations"
+        "chronos.LessonPeriod", models.CASCADE, related_name="documentations", blank=True, null=True
+    )
+    event = models.ForeignKey(
+        "chronos.Event", models.CASCADE, related_name="documentations", blank=True, null=True
+    )
+    extra_lesson = models.ForeignKey(
+        "chronos.ExtraLesson", models.CASCADE, related_name="documentations", blank=True, null=True
     )
 
     topic = models.CharField(verbose_name=_("Lesson topic"), max_length=200, blank=True)
@@ -203,13 +250,17 @@ class LessonDocumentation(ExtensibleModel, WeekRelatedMixin):
     class Meta:
         verbose_name = _("Lesson documentation")
         verbose_name_plural = _("Lesson documentations")
-        unique_together = [["lesson_period", "week"]]
         ordering = [
             "year",
             "week",
             "lesson_period__period__weekday",
             "lesson_period__period__period",
         ]
+        constraints = [
+            CheckConstraint(
+                check=lesson_related_constraint_q, name="one_relation_only_lesson_documentation",
+            )
+        ]
 
 
 class ExtraMark(ExtensibleModel):
-- 
GitLab