diff --git a/aleksis/apps/chronos/migrations/0007_advanced_models_from_untis.py b/aleksis/apps/chronos/migrations/0007_advanced_models_from_untis.py new file mode 100644 index 0000000000000000000000000000000000000000..87b517a8beb8d3ee4dd097981ed53f95cc2b4ad4 --- /dev/null +++ b/aleksis/apps/chronos/migrations/0007_advanced_models_from_untis.py @@ -0,0 +1,280 @@ +# Generated by Django 3.0.3 on 2020-03-08 20:16 + +import colorfield.fields +import django.contrib.postgres.fields.jsonb +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('chronos', '0006_extended_data'), + ] + + operations = [ + migrations.CreateModel( + name='Absence', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('date_start', models.DateField(null=True, verbose_name='Effective start date of absence')), + ('date_end', models.DateField(null=True, verbose_name='Effective end date of absence')), + ('comment', models.TextField(verbose_name='Comment', null=True, blank=True)), + ], + options={ + 'verbose_name': 'Absence', + 'verbose_name_plural': 'Absences', + 'ordering': ['date_start'], + }, + ), + migrations.CreateModel( + name='AbsenceReason', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('title', models.CharField(max_length=50, verbose_name='Title')), + ('description', models.TextField(verbose_name='Description', null=True, blank=True)), + ], + options={ + 'verbose_name': 'Absence reason', + 'verbose_name_plural': 'Absence reasons', + }, + ), + migrations.CreateModel( + name='Event', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('title', models.CharField(max_length=50, verbose_name='Title')), + ('date_start', models.DateField(null=True, verbose_name='Effective start date of event')), + ('date_end', models.DateField(null=True, verbose_name='Effective end date of event')), + ], + options={ + 'verbose_name': 'Events', + 'verbose_name_plural': 'Events', + 'ordering': ['date_start'], + }, + ), + migrations.CreateModel( + name='Exam', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('date', models.DateField(null=True, verbose_name='Date of exam')), + ('title', models.CharField(max_length=50, verbose_name='Title')), + ('comment', models.TextField(verbose_name='Comment', null=True, blank=True)), + ], + options={ + 'verbose_name': 'Exam', + 'verbose_name_plural': 'Exams', + 'ordering': ['date'], + }, + ), + migrations.CreateModel( + name='Holiday', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('title', models.CharField(max_length=50, verbose_name='Title of the holidays')), + ('date_start', models.DateField(null=True, verbose_name='Effective start date of holidays')), + ('date_end', models.DateField(null=True, verbose_name='Effective end date of holidays')), + ('comments', models.TextField(verbose_name='Comments', null=True, blank=True)), + ], + options={ + 'verbose_name': 'Holiday', + 'verbose_name_plural': 'Holidays', + 'ordering': ['date_start'], + }, + ), + migrations.CreateModel( + name='SupervisionArea', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('short_name', models.CharField(max_length=10, verbose_name='Short name')), + ('name', models.CharField(max_length=50, verbose_name='Long name')), + ('colour_fg', colorfield.fields.ColorField(default='#000000', max_length=18)), + ('colour_bg', colorfield.fields.ColorField(default='#FFFFFF', max_length=18)), + ], + options={ + 'verbose_name': 'Supervision areas', + 'verbose_name_plural': 'Supervision areas', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='Break', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('short_name', models.CharField(max_length=10, verbose_name='Short name')), + ('name', models.CharField(max_length=50, verbose_name='Long name')), + ('weekday', models.PositiveSmallIntegerField( + choices=[(0, 'Montag'), (1, 'Dienstag'), (2, 'Mittwoch'), (3, 'Donnerstag'), (4, 'Freitag'), + (5, 'Samstag'), (6, 'Sonntag')], verbose_name='Week day')), + ('time_start', models.TimeField(verbose_name='Start time')), + ('time_end', models.TimeField(verbose_name='End time')), + ], + options={ + 'verbose_name': 'Break', + 'verbose_name_plural': 'Breaks', + 'ordering': ['weekday', 'time_start'], + }, + ), + migrations.CreateModel( + name='Supervision', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ], + options={ + 'verbose_name': 'Supervision', + 'verbose_name_plural': 'Supervisions', + 'ordering': ['area', 'break_item'], + }, + ), + migrations.CreateModel( + name='SupervisionSubstitution', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('extended_data', django.contrib.postgres.fields.jsonb.JSONField(default=dict, editable=False)), + ('date', models.DateField(verbose_name='Date')), + ], + options={ + 'verbose_name': 'Supervision substitution', + 'verbose_name_plural': 'Supervision substitutions', + 'ordering': ['date', 'supervision'], + }, + ), + migrations.AddIndex( + model_name='holiday', + index=models.Index(fields=['date_start', 'date_end'], name='chronos_hol_date_st_a47004_idx'), + ), + migrations.AddField( + model_name='exam', + name='lesson', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='exams', to='chronos.Lesson'), + ), + migrations.AddField( + model_name='exam', + name='period_from', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='chronos.TimePeriod', verbose_name='Effective start period of exam'), + ), + migrations.AddField( + model_name='exam', + name='period_to', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='chronos.TimePeriod', verbose_name='Effective end period of exam'), + ), + migrations.AddField( + model_name='event', + name='absence_reason', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='absence_reason', to='chronos.AbsenceReason', verbose_name='Absence reason'), + ), + migrations.AddField( + model_name='event', + name='teachers', + field=models.ManyToManyField(related_name='events', to='core.Person', verbose_name='Teachers'), + ), + migrations.AddField( + model_name='event', + name='period_from', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='chronos.TimePeriod', verbose_name='Effective start period of event'), + ), + migrations.AddField( + model_name='event', + name='period_to', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='chronos.TimePeriod', verbose_name='Effective end period of event'), + ), + migrations.AddField( + model_name='event', + name='rooms', + field=models.ManyToManyField(related_name='events', to='chronos.Room', verbose_name='Rooms'), + ), + migrations.AddField( + model_name='absence', + name='period_from', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='chronos.TimePeriod', verbose_name='Effective start period of absence'), + ), + migrations.AddField( + model_name='absence', + name='period_to', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='chronos.TimePeriod', verbose_name='Effective end period of absence'), + ), + migrations.AddField( + model_name='absence', + name='person', + field=models.ManyToManyField(related_name='absences', to='core.Person'), + ), + migrations.AddField( + model_name='absence', + name='reason', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='absences', to='chronos.AbsenceReason'), + ), + migrations.AddIndex( + model_name='exam', + index=models.Index(fields=['date'], name='chronos_exa_date_5ba442_idx'), + ), + migrations.AddIndex( + model_name='event', + index=models.Index(fields=['period_from', 'period_to', 'date_start', 'date_end'], name='chronos_eve_periodf_56eb18_idx'), + ), + migrations.AddIndex( + model_name='absence', + index=models.Index(fields=['date_start', 'date_end'], name='chronos_abs_date_st_337ff5_idx'), + ), + migrations.AddField( + model_name='lessonsubstitution', + name='cancelled_for_teachers', + field=models.BooleanField(default=False, verbose_name='Cancelled for teachers?'), + ), + migrations.AddField( + model_name='lessonsubstitution', + name='comment', + field=models.TextField(blank=True, null=True, verbose_name='Comment'), + ), + migrations.AlterField( + model_name='lessonsubstitution', + name='cancelled', + field=models.BooleanField(default=False, verbose_name='Cancelled?'), + ), + migrations.AddField( + model_name='event', + name='groups', + field=models.ManyToManyField(related_name='events', to='core.Group', verbose_name='Groups'), + ), + migrations.AddField( + model_name='supervisionsubstitution', + name='supervision', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='substitutions', + to='chronos.Supervision', verbose_name='Supervision'), + ), + migrations.AddField( + model_name='supervisionsubstitution', + name='teacher', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='substituted_supervisions', to='core.Person', verbose_name='Teacher'), + ), + migrations.AddField( + model_name='supervision', + name='area', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supervisions', + to='chronos.SupervisionArea', verbose_name='Supervision area'), + ), + migrations.AddField( + model_name='supervision', + name='break_item', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supervisions', + to='chronos.Break', verbose_name='Break'), + ), + migrations.AddField( + model_name='supervision', + name='teacher', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supervisions', + to='core.Person', verbose_name='Teacher'), + ), + migrations.AddIndex( + model_name='break', + index=models.Index(fields=['weekday', 'time_start', 'time_end'], name='chronos_bre_weekday_165338_idx'), + ), + ] diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py index 94fd844c2c578ec29257ad2cfe0280af9f1ea6b9..78d9b425b26d7c182f9d4abc3b65c63a5949ee4d 100644 --- a/aleksis/apps/chronos/models.py +++ b/aleksis/apps/chronos/models.py @@ -18,6 +18,7 @@ from django.utils.decorators import classproperty from django.utils.translation import ugettext_lazy as _ from calendarweek.django import CalendarWeek, i18n_day_names_lazy, i18n_day_abbrs_lazy +from colorfield.fields import ColorField from django_global_request.middleware import get_request from aleksis.core.mixins import ExtensibleModel @@ -466,7 +467,10 @@ class LessonSubstitution(models.Model): ) room = models.ForeignKey("Room", models.CASCADE, null=True, blank=True, verbose_name=_("Room")) - cancelled = models.BooleanField(default=False) + cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled?")) + cancelled_for_teachers = models.BooleanField(default=False, verbose_name=_("Cancelled for teachers?")) + + comment = models.TextField(verbose_name=_("Comment"), blank=True, null=True) def clean(self) -> None: if self.subject and self.cancelled: @@ -587,3 +591,125 @@ class TimetableWidget(DashboardWidget): class Meta: proxy = True verbose_name = _("Timetable widget") + + +class AbsenceReason(ExtensibleModel): + title = models.CharField(verbose_name=_("Title"), max_length=50) + description = models.TextField(verbose_name=_("Description"), blank=True, null=True) + + class Meta: + verbose_name = _("Absence reason") + verbose_name_plural = _("Absence reasons") + +class Absence(ExtensibleModel): + reason = models.ForeignKey("AbsenceReason", on_delete=models.CASCADE, related_name="absences") + person = models.ManyToManyField("core.Person", related_name="absences") + + date_start = models.DateField(verbose_name=_("Effective start date of absence"), null=True) + date_end = models.DateField(verbose_name=_("Effective end date of absence"), null=True) + period_from = models.ForeignKey("TimePeriod", on_delete=models.CASCADE, verbose_name=_("Effective start period of absence"), null=True, related_name="+") + period_to = models.ForeignKey("TimePeriod", on_delete=models.CASCADE, verbose_name=_("Effective end period of absence"), null=True, related_name="+") + comment = models.TextField(verbose_name=_("Comment"), blank=True, null=True) + + class Meta: + ordering = ["date_start"] + indexes = [models.Index(fields=["date_start", "date_end"])] + verbose_name = _("Absence") + verbose_name_plural = _("Absences") + + +class Exam(ExtensibleModel): + lesson = models.ForeignKey("Lesson", on_delete=models.CASCADE, related_name="exams") + + date = models.DateField(verbose_name=_("Date of exam"), null=True) + period_from = models.ForeignKey("TimePeriod", on_delete=models.CASCADE, verbose_name=_("Effective start period of exam"), null=True, related_name="+") + period_to = models.ForeignKey("TimePeriod", on_delete=models.CASCADE, verbose_name=_("Effective end period of exam"), null=True, related_name="+") + + title = models.CharField(verbose_name=_("Title"), max_length=50) + comment = models.TextField(verbose_name=_("Comment"), blank=True, null=True) + + class Meta: + ordering = ["date"] + indexes = [models.Index(fields=["date"])] + verbose_name = _("Exam") + verbose_name_plural = _("Exams") + + +class Holiday(ExtensibleModel): + title = models.CharField(verbose_name=_("Title of the holidays"), max_length=50) + date_start = models.DateField(verbose_name=_("Effective start date of holidays"), null=True) + date_end = models.DateField(verbose_name=_("Effective end date of holidays"), null=True) + comments = models.TextField(verbose_name=_("Comments"), null=True, blank=True) + + class Meta: + ordering = ["date_start"] + indexes = [models.Index(fields=["date_start", "date_end"])] + verbose_name = _("Holiday") + verbose_name_plural = _("Holidays") + + +class SupervisionArea(ExtensibleModel): + short_name = models.CharField(verbose_name=_("Short name"), max_length=10) + name = models.CharField(verbose_name=_("Long name"), max_length=50) + colour_fg = ColorField(default="#000000") + colour_bg = ColorField() + + class Meta: + ordering = ["name"] + verbose_name = _("Supervision area") + verbose_name_plural = _("Supervision areas") + + +class Break(ExtensibleModel): + short_name = models.CharField(verbose_name=_("Short name"), max_length=10) + name = models.CharField(verbose_name=_("Long name"), max_length=50) + weekday = models.PositiveSmallIntegerField(verbose_name=_("Week day"), choices=TimePeriod.WEEKDAY_CHOICES) + time_start = models.TimeField(verbose_name=_("Start time")) + time_end = models.TimeField(verbose_name=_("End time")) + + class Meta: + ordering = ["weekday", "time_start"] + indexes = [models.Index(fields=["weekday", "time_start", "time_end"])] + verbose_name = _("Break") + verbose_name_plural = _("Breaks") + + +class Supervision(ExtensibleModel): + area = models.ForeignKey(SupervisionArea, models.CASCADE, verbose_name=_("Supervision area"), related_name="supervisions") + break_item = models.ForeignKey(Break, models.CASCADE, verbose_name=_("Break"), related_name="supervisions") + teacher = models.ForeignKey("core.Person", models.CASCADE, related_name="supervisions", verbose_name=_("Teacher")) + + class Meta: + ordering = ["area", "break_item"] + verbose_name= _("Supervision") + verbose_name_plural = _("Supervisions") + + +class SupervisionSubstitution(ExtensibleModel): + date = models.DateField(verbose_name=_("Date")) + supervision = models.ForeignKey(Supervision, models.CASCADE, verbose_name=_("Supervision"), related_name="substitutions") + teacher = models.ForeignKey("core.Person", models.CASCADE, related_name="substituted_supervisions", verbose_name=_("Teacher")) + + class Meta: + ordering = ["date", "supervision"] + verbose_name = _("Supervision substitution") + verbose_name_plural = _("Supervision substitutions") + + +class Event(ExtensibleModel): + title = models.CharField(verbose_name=_("Title"), max_length=50) + date_start = models.DateField(verbose_name=_("Effective start date of event"), null=True) + date_end = models.DateField(verbose_name=_("Effective end date of event"), null=True) + absence_reason = models.ForeignKey("AbsenceReason", on_delete=models.CASCADE, related_name="absence_reason", verbose_name=_("Absence reason")) + period_from = models.ForeignKey("TimePeriod", on_delete=models.CASCADE, verbose_name=_("Effective start period of event"), related_name="+") + period_to = models.ForeignKey("TimePeriod", on_delete=models.CASCADE, verbose_name=_("Effective end period of event"), related_name="+") + + groups = models.ManyToManyField("core.Group", related_name="events", verbose_name=_("Groups")) + rooms = models.ManyToManyField("Room", related_name="events", verbose_name=_("Rooms")) + teachers = models.ManyToManyField("core.Person", related_name="events", verbose_name=_("Teachers")) + + class Meta: + ordering = ["date_start"] + indexes = [models.Index(fields=["period_from", "period_to", "date_start", "date_end"])] + verbose_name = _("Event") + verbose_name_plural = _("Events")