From 702dd6e5f9fff5bd71e399ac570ef95ecfba6999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Thu, 28 Oct 2021 18:41:17 +0200 Subject: [PATCH 1/4] Show semantic meaning for unlimited room capacity in help text --- AKModel/locale/de_DE/LC_MESSAGES/django.po | 6 +++--- AKModel/migrations/0047_room_capacity_help.py | 18 ++++++++++++++++++ AKModel/models.py | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 AKModel/migrations/0047_room_capacity_help.py diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po index 024239b8..2b85f9ca 100644 --- a/AKModel/locale/de_DE/LC_MESSAGES/django.po +++ b/AKModel/locale/de_DE/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-05-15 14:34+0000\n" +"POT-Creation-Date: 2021-10-28 16:39+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -519,8 +519,8 @@ msgid "Capacity" msgstr "Kapazität" #: AKModel/models.py:331 -msgid "Maximum number of people" -msgstr "Maximale Personenzahl" +msgid "Maximum number of people (-1 for unlimited)." +msgstr "Maximale Personenzahl (-1 wenn unbeschränkt)." #: AKModel/models.py:332 msgid "Properties" diff --git a/AKModel/migrations/0047_room_capacity_help.py b/AKModel/migrations/0047_room_capacity_help.py new file mode 100644 index 00000000..d47591d2 --- /dev/null +++ b/AKModel/migrations/0047_room_capacity_help.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.8 on 2021-10-28 16:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('AKModel', '0046_present_by_default'), + ] + + operations = [ + migrations.AlterField( + model_name='room', + name='capacity', + field=models.IntegerField(help_text='Maximum number of people (-1 for unlimited).', verbose_name='Capacity'), + ), + ] diff --git a/AKModel/models.py b/AKModel/models.py index d0d77784..de03c22d 100644 --- a/AKModel/models.py +++ b/AKModel/models.py @@ -328,7 +328,7 @@ class Room(models.Model): name = models.CharField(max_length=64, verbose_name=_('Name'), help_text=_('Name or number of the room')) location = models.CharField(max_length=256, blank=True, verbose_name=_('Location'), help_text=_('Name or number of the location')) - capacity = models.IntegerField(verbose_name=_('Capacity'), help_text=_('Maximum number of people')) + capacity = models.IntegerField(verbose_name=_('Capacity'), help_text=_('Maximum number of people (-1 for unlimited).')) properties = models.ManyToManyField(to=AKRequirement, blank=True, verbose_name=_('Properties'), help_text=_('AK requirements fulfilled by the room')) -- GitLab From bdc7b76f5560ffe1f20e9974aff4a5bfc907b172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Thu, 28 Oct 2021 22:52:02 +0200 Subject: [PATCH 2/4] Implement constraint violation check for room sizes for changed AKSlot Both violation and warning for capacity nearly exceeded Adapt model (better description for capacity exceeded violation type) Add comment to detail fields of constraint violation --- AKModel/locale/de_DE/LC_MESSAGES/django.po | 13 ++-- .../0048_constraint_violation_text.py | 18 ++++++ AKModel/models.py | 4 +- .../locale/de_DE/LC_MESSAGES/django.po | 32 ++++++++-- AKScheduling/models.py | 64 ++++++++++++++++++- 5 files changed, 116 insertions(+), 15 deletions(-) create mode 100644 AKModel/migrations/0048_constraint_violation_text.py diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po index 2b85f9ca..740582d5 100644 --- a/AKModel/locale/de_DE/LC_MESSAGES/django.po +++ b/AKModel/locale/de_DE/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-10-28 16:39+0000\n" +"POT-Creation-Date: 2021-10-28 20:45+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -652,10 +652,8 @@ msgid "Two AK Slots for the same AK scheduled at the same time" msgstr "Zwei AK Slots eines AKs wurden zur selben Zeit platziert" #: AKModel/models.py:466 -msgid "AK Slot is scheduled in a room with less space than interest" -msgstr "" -"AK Slot wurde in einem Raum mit weniger Plätzen als am AK Interessierten " -"platziert" +msgid "Room does not have enough space for interest in scheduled AK Slot" +msgstr "Room hat nicht genug Platz für das Interesse am geplanten AK-Slot" #: AKModel/models.py:467 msgid "AK Slot is scheduled outside the event's availabilities" @@ -1007,6 +1005,11 @@ msgstr "Resolutionsabsicht?" msgid "Category (for Wishes)" msgstr "Kategorie (für Wünsche)" +#~ msgid "AK Slot is scheduled in a room with less space than interest" +#~ msgstr "" +#~ "AK Slot wurde in einem Raum mit weniger Plätzen als am AK Interessierten " +#~ "platziert" + #~ msgid "Confirm" #~ msgstr "Bestätigen" diff --git a/AKModel/migrations/0048_constraint_violation_text.py b/AKModel/migrations/0048_constraint_violation_text.py new file mode 100644 index 00000000..71ff0893 --- /dev/null +++ b/AKModel/migrations/0048_constraint_violation_text.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.8 on 2021-10-28 20:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('AKModel', '0047_room_capacity_help'), + ] + + operations = [ + migrations.AlterField( + model_name='constraintviolation', + name='type', + field=models.CharField(choices=[('ots', 'Owner has two parallel slots'), ('soa', "AK Slot was scheduled outside the AK's availabilities"), ('rts', 'Room has two AK slots scheduled at the same time'), ('rng', 'Room does not satisfy the requirement of the scheduled AK'), ('acc', 'AK Slot is scheduled at the same time as an AK listed as a conflict'), ('abp', 'AK Slot is scheduled before an AK listed as a prerequisite'), ('aar', 'AK Slot for AK with intention to submit a resolution is scheduled after resolution deadline'), ('acm', 'AK Slot in a category is outside that categories availabilities'), ('asc', 'Two AK Slots for the same AK scheduled at the same time'), ('rce', 'Room does not have enough space for interest in scheduled AK Slot'), ('soe', "AK Slot is scheduled outside the event's availabilities")], help_text='Type of violation, i.e. what kind of constraint was violated', max_length=3, verbose_name='Type'), + ), + ] diff --git a/AKModel/models.py b/AKModel/models.py index de03c22d..98dad770 100644 --- a/AKModel/models.py +++ b/AKModel/models.py @@ -463,7 +463,7 @@ class ConstraintViolation(models.Model): 'AK Slot for AK with intention to submit a resolution is scheduled after resolution deadline') AK_CATEGORY_MISMATCH = 'acm', _('AK Slot in a category is outside that categories availabilities') AK_SLOT_COLLISION = 'asc', _('Two AK Slots for the same AK scheduled at the same time') - ROOM_CAPACITY_EXCEEDED = 'rce', _('AK Slot is scheduled in a room with less space than interest') + ROOM_CAPACITY_EXCEEDED = 'rce', _('Room does not have enough space for interest in scheduled AK Slot') SLOT_OUTSIDE_EVENT = 'soe', _('AK Slot is scheduled outside the event\'s availabilities') class ViolationLevel(models.IntegerChoices): @@ -499,7 +499,7 @@ class ConstraintViolation(models.Model): manually_resolved = models.BooleanField(verbose_name=_('Manually Resolved'), default=False, help_text=_('Mark this violation manually as resolved')) - fields = ['ak_owner', 'room', 'requirement', 'category'] + fields = ['ak_owner', 'room', 'requirement', 'category', 'comment'] fields_mm = ['_aks', '_ak_slots'] def __init__(self, *args, **kwargs): diff --git a/AKScheduling/locale/de_DE/LC_MESSAGES/django.po b/AKScheduling/locale/de_DE/LC_MESSAGES/django.po index 5ed003e8..dbf3a8a6 100644 --- a/AKScheduling/locale/de_DE/LC_MESSAGES/django.po +++ b/AKScheduling/locale/de_DE/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-05-09 18:23+0000\n" +"POT-Creation-Date: 2021-10-28 20:45+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,6 +17,24 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: AKScheduling/models.py:507 +#, python-format +msgid "" +"Not enough space for AK interest (Interest: %(interest)d, Capacity: " +"%(capacity)d)" +msgstr "" +"Nicht genug Platz für AK-Interesse (Interesse: %(interest)d, Kapazität: " +"%(capacity)d)" + +#: AKScheduling/models.py:519 +#, python-format +msgid "" +"Space is too close to AK interest (Interest: %(interest)d, Capacity: " +"%(capacity)d)" +msgstr "" +"Verfügbarer Platz zu dicht an Interesse (Interesse: %(interest)d, Kapazität: " +"%(capacity)d)" + #: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:11 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:11 #: AKScheduling/templates/admin/AKScheduling/scheduling.html:10 @@ -62,7 +80,7 @@ msgstr "Seit" #: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:134 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:243 -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:197 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:208 #: AKScheduling/templates/admin/AKScheduling/unscheduled.html:34 msgid "Event Status" msgstr "Event-Status" @@ -103,23 +121,23 @@ msgstr "AK-Track hinzufügen" msgid "AKs without track" msgstr "AKs ohne Track" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:87 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:91 msgid "Day (Horizontal)" msgstr "Tag (horizontal)" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:94 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:98 msgid "Day (Vertical)" msgstr "Tag (vertikal)" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:105 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:109 msgid "Event (Horizontal)" msgstr "Event (horizontal)" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:114 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:118 msgid "Event (Vertical)" msgstr "Event (vertikal)" -#: AKScheduling/templates/admin/AKScheduling/scheduling.html:141 +#: AKScheduling/templates/admin/AKScheduling/scheduling.html:146 msgid "Room" msgstr "Raum" diff --git a/AKScheduling/models.py b/AKScheduling/models.py index 24f544f1..0fb76831 100644 --- a/AKScheduling/models.py +++ b/AKScheduling/models.py @@ -1,5 +1,6 @@ from django.db.models.signals import post_save, m2m_changed from django.dispatch import receiver +from django.utils.translation import gettext_lazy as _ from AKModel.availability.models import Availability from AKModel.models import AK, AKSlot, Room, Event, AKOwner, ConstraintViolation @@ -57,6 +58,44 @@ def update_cv_reso_deadline_for_slot(slot): update_constraint_violations(new_violations, list(slot.constraintviolation_set.filter(type=violation_type))) +def check_capacity_for_slot(slot: AKSlot): + """ + Check whether this slot violates the capacity requirement + + :param slot: slot to check + :type slot: AKSlot + :return: Violation (if any) or None + :rtype: ConstraintViolation or None + """ + if slot.room: + if slot.room.capacity >= 0: + if slot.room.capacity < slot.ak.interest: + c = ConstraintViolation( + type=ConstraintViolation.ViolationType.ROOM_CAPACITY_EXCEEDED, + level=ConstraintViolation.ViolationLevel.VIOLATION, + event=slot.event, + room=slot.room, + comment=_("Not enough space for AK interest (Interest: %(interest)d, Capacity: %(capacity)d)") + % {'interest': slot.ak.interest, 'capacity': slot.room.capacity}, + ) + c.ak_slots_tmp.add(slot) + c.aks_tmp.add(slot.ak) + return c + elif slot.room.capacity < slot.ak.interest + 5 or slot.room.capacity < slot.ak.interest * 1.25: + c = ConstraintViolation( + type=ConstraintViolation.ViolationType.ROOM_CAPACITY_EXCEEDED, + level=ConstraintViolation.ViolationLevel.WARNING, + event=slot.event, + room=slot.room, + comment=_("Space is too close to AK interest (Interest: %(interest)d, Capacity: %(capacity)d)") + % {'interest': slot.ak.interest, 'capacity': slot.room.capacity} + ) + c.ak_slots_tmp.add(slot) + c.aks_tmp.add(slot.ak) + return c + return None + + @receiver(post_save, sender=AK) def ak_changed_handler(sender, instance: AK, **kwargs): # Changes might affect: Reso intention, Category, Interest @@ -64,7 +103,6 @@ def ak_changed_handler(sender, instance: AK, **kwargs): pass -# TODO adapt for Room's reauirements @receiver(m2m_changed, sender=AK.owners.through) def ak_owners_changed_handler(sender, instance: AK, action: str, **kwargs): """ @@ -492,6 +530,14 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): # print(existing_violations_to_check) update_constraint_violations(new_violations, existing_violations_to_check) + # == Check for room capacity == + cv = check_capacity_for_slot(instance) + new_violations = [cv] if cv is not None else [] + + # Compare to/update list of existing violations of this type for this slot + existing_violations_to_check = list(instance.constraintviolation_set.filter(type=ConstraintViolation.ViolationType.ROOM_CAPACITY_EXCEEDED)) + update_constraint_violations(new_violations, existing_violations_to_check) + @receiver(post_save, sender=Room) def room_changed_handler(sender, **kwargs): @@ -499,6 +545,22 @@ def room_changed_handler(sender, **kwargs): print(f"{sender} changed") +@receiver(m2m_changed, sender=Room.properties.through) +def room_requirements_changed_handler(sender, instance: Room, action: str, **kwargs): + """ + Requirements of room changed + """ + # Only signal after change (post_add, post_delete, post_clear) are relevant + if not action.startswith("post"): + return + + # print(f"{instance} changed") + + event = instance.event + + # TODO React to changes + + @receiver(post_save, sender=Availability) def availability_changed_handler(sender, instance: Availability, **kwargs): # Changes might affect: category availability, AK availability, Room availability -- GitLab From f58af4236ad470620225211378e78744c433009b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Thu, 28 Oct 2021 23:29:22 +0200 Subject: [PATCH 3/4] Implement room capacity constraint checks for changed AKs and rooms --- AKScheduling/models.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/AKScheduling/models.py b/AKScheduling/models.py index 0fb76831..deb7f795 100644 --- a/AKScheduling/models.py +++ b/AKScheduling/models.py @@ -100,7 +100,17 @@ def check_capacity_for_slot(slot: AKSlot): def ak_changed_handler(sender, instance: AK, **kwargs): # Changes might affect: Reso intention, Category, Interest # TODO Reso intention changes - pass + + # Check room capacities + violation_type = ConstraintViolation.ViolationType.ROOM_CAPACITY_EXCEEDED + new_violations = [] + for slot in instance.akslot_set.all(): + cv = check_capacity_for_slot(slot) + if cv is not None: + new_violations.append(cv) + + existing_violations_to_check = list(instance.constraintviolation_set.filter(type=violation_type)) + update_constraint_violations(new_violations, existing_violations_to_check) @receiver(m2m_changed, sender=AK.owners.through) @@ -540,9 +550,19 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): @receiver(post_save, sender=Room) -def room_changed_handler(sender, **kwargs): +def room_changed_handler(sender, instance: Room, **kwargs): # Changes might affect: Room size - print(f"{sender} changed") + + # Check room capacities + violation_type = ConstraintViolation.ViolationType.ROOM_CAPACITY_EXCEEDED + new_violations = [] + for slot in instance.akslot_set.all(): + cv = check_capacity_for_slot(slot) + if cv is not None: + new_violations.append(cv) + + existing_violations_to_check = list(instance.constraintviolation_set.filter(type=violation_type)) + update_constraint_violations(new_violations, existing_violations_to_check) @receiver(m2m_changed, sender=Room.properties.through) -- GitLab From b963f1a121da100ab3655d33c6e4468ca8da7093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Fri, 29 Oct 2021 11:39:04 +0200 Subject: [PATCH 4/4] Make sure corresponding CVs are deleted if reso_deadline is nulled --- AKScheduling/models.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/AKScheduling/models.py b/AKScheduling/models.py index deb7f795..bfdb1e16 100644 --- a/AKScheduling/models.py +++ b/AKScheduling/models.py @@ -44,6 +44,8 @@ def update_cv_reso_deadline_for_slot(slot): """ event = slot.event if slot.ak.reso and slot.event.reso_deadline and slot.start: + # Update only if reso_deadline exists + # if event was changed and reso_deadline is removed, CVs will be deleted by event changed handler violation_type = ConstraintViolation.ViolationType.AK_AFTER_RESODEADLINE new_violations = [] if slot.end > event.reso_deadline: @@ -612,7 +614,7 @@ def availability_changed_handler(sender, instance: Availability, **kwargs): c.ak_slots_tmp.add(slot) new_violations.append(c) - print(f"{instance.ak} has the following slots putside availabilities: {new_violations}") + print(f"{instance.ak} has the following slots outside availabilities: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -622,8 +624,13 @@ def availability_changed_handler(sender, instance: Availability, **kwargs): @receiver(post_save, sender=Event) -def event_changed_handler(sender, instance, **kwargs): +def event_changed_handler(sender, instance: Event, **kwargs): # == Check for reso ak after reso deadline (which might have changed) == if instance.reso_deadline: for slot in instance.akslot_set.filter(start__isnull=False, ak__reso=True): update_cv_reso_deadline_for_slot(slot) + else: + # No reso deadline, delete all violations + violation_type = ConstraintViolation.ViolationType.AK_AFTER_RESODEADLINE + existing_violations_to_check = list(instance.constraintviolation_set.filter(type=violation_type)) + update_constraint_violations([], existing_violations_to_check) -- GitLab