diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po index 0b7afd2a2a67ec35f361cb7c263b825600052831..30f6450705afb096ce15c50b156fc88a4b255615 100644 --- a/AKModel/locale/de_DE/LC_MESSAGES/django.po +++ b/AKModel/locale/de_DE/LC_MESSAGES/django.po @@ -466,6 +466,38 @@ msgstr "" msgid "Events" msgstr "Events" +#: AKModel/models.py:430 +#, python-brace-format +msgid "AK {ak_name} is not assigned any timeslot by the solver" +msgstr "Dem AK {ak_name} wurde vom Solver kein Zeitslot zugewiesen" + +#: AKModel/models.py:440 +#, python-brace-format +msgid "" +"Duration of AK {ak_name} assigned by solver ({solver_duration} hours) is " +"less than the duration required by the slot ({slot_duration} hours)" +msgstr "" +"Die dem AK {ak_name} vom Solver zugewiesene Dauer ({solver_duration} Stunden) ist " +"kürzer als die aktuell vorgesehene Dauer des Slots ({slot_duration} Stunden)" + +#: AKModel/models.py:454 +#, python-brace-format +msgid "" +"Fixed AK {ak_name} assigned by solver to room {solver_room} is fixed to room " +"{slot_room}" +msgstr "" +"Dem fix geplanten AK {ak_name} wurde vom Solver Raum {solver_room} zugewiesen, " +"dabei ist der AK bereits fix in Raum {slot_room} eingeplant." + +#: AKModel/models.py:465 +#, python-brace-format +msgid "" +"Fixed AK {ak_name} assigned by solver to start at {solver_start} is fixed to " +"start at {slot_start}" +msgstr "" +"Dem fix geplanten AK {ak_name} wurde vom Solver die Startzeit {solver_start} zugewiesen, " +"dabei ist der AK bereits für {slot_start} eingeplant." + #: AKModel/models.py:180 msgid "Nickname" msgstr "Spitzname" @@ -981,7 +1013,7 @@ msgstr "Primäre Kategorien" msgid "Categories that should be assigned to this slot primarily" msgstr "Kategorieren, die diesem Slot primär zugewiesen werden sollen" -#: AKModel/site.py:14 +#: AKModel/site.py:13 AKModel/site.py:14 msgid "Administration" msgstr "Verwaltung" @@ -1326,6 +1358,15 @@ msgstr "" msgid "AK Schedule JSON Import" msgstr "AK-Plan JSON-Import" +#: AKModel/views/manage.py:265 +#, python-brace-format +msgid "Successfully imported {n} slot(s)" +msgstr "Erfolgreich {n} Slot(s) importiert" + +#: AKModel/views/manage.py:271 +msgid "Importing an AK schedule failed! Reason: " +msgstr "AK-Plan importieren fehlgeschlagen! Grund: " + #: AKModel/views/room.py:37 #, python-format msgid "Created Room '%(room)s'" diff --git a/AKModel/models.py b/AKModel/models.py index 35bc22352bea2cd4e409e8eddf14b29f2cdb2d87..25c6b9c587152e8a3ca057b6daf014f4fbb01a9a 100644 --- a/AKModel/models.py +++ b/AKModel/models.py @@ -8,7 +8,7 @@ from typing import Iterable, Generator from django.core.validators import RegexValidator from django.apps import apps -from django.db import models +from django.db import models, transaction from django.db.models import Count from django.urls import reverse_lazy from django.utils import timezone @@ -413,7 +413,8 @@ class Event(models.Model): else: yield from self.uniform_time_slots(slots_in_an_hour=slots_in_an_hour) - def schedule_from_json(self, schedule: str) -> None: + @transaction.atomic + def schedule_from_json(self, schedule: str) -> int: """Load AK schedule from a json string. :param schedule: A string that can be decoded to json, describing @@ -431,18 +432,63 @@ class Event(models.Model): for timeslot in block } + slots_updated = 0 for scheduled_slot in schedule["scheduled_aks"]: + scheduled_slot["timeslot_ids"] = list(map(int, scheduled_slot["timeslot_ids"])) slot = AKSlot.objects.get(id=int(scheduled_slot["ak_id"])) - slot.room = Room.objects.get(id=int(scheduled_slot["room_id"])) - scheduled_slot["timeslot_ids"] = list(map(int, scheduled_slot["timeslot_ids"])) + if not scheduled_slot["timeslot_ids"]: + raise ValueError( + _("AK {ak_name} is not assigned any timeslot by the solver").format(ak_name=slot.ak.name) + ) start_timeslot = timeslot_dict[min(scheduled_slot["timeslot_ids"])].avail end_timeslot = timeslot_dict[max(scheduled_slot["timeslot_ids"])].avail + solver_duration = (end_timeslot.end - start_timeslot.start).total_seconds() / 3600.0 + + if solver_duration + 2e-4 < slot.duration: + raise ValueError( + _( + "Duration of AK {ak_name} assigned by solver ({solver_duration} hours) " + "is less than the duration required by the slot ({slot_duration} hours)" + ).format( + ak_name=slot.ak.name, + solver_duration=solver_duration, + slot_duration=slot.duration, + ) + ) + + if slot.fixed: + solver_room = Room.objects.get(id=int(scheduled_slot["room_id"])) + if slot.room != solver_room: + raise ValueError( + _( + "Fixed AK {ak_name} assigned by solver to room {solver_room} " + "is fixed to room {slot_room}" + ).format( + ak_name=slot.ak.name, + solver_room=solver_room.name, + slot_room=slot.room.name, + ) + ) + if slot.start != start_timeslot.start: + raise ValueError( + _( + "Fixed AK {ak_name} assigned by solver to start at {solver_start} " + "is fixed to start at {slot_start}" + ).format( + ak_name=slot.ak.name, + solver_start=start_timeslot.start, + slot_start=slot.start, + ) + ) + else: + slot.room = Room.objects.get(id=int(scheduled_slot["room_id"])) + slot.start = start_timeslot.start + slot.save() + slots_updated += 1 - slot.start = start_timeslot.start - slot.duration = (end_timeslot.end - start_timeslot.start).total_seconds() / 3600.0 - slot.save() + return slots_updated class AKOwner(models.Model): """ An AKOwner describes the person organizing/holding an AK. diff --git a/AKModel/views/manage.py b/AKModel/views/manage.py index 1bad9534efd6fe81fa70ebf30fbdf84aad476967..3acb05fd29a6e91cd17f45e9ed43d889a67da22c 100644 --- a/AKModel/views/manage.py +++ b/AKModel/views/manage.py @@ -257,6 +257,18 @@ class AKScheduleJSONImportView(EventSlugMixin, IntermediateAdminView): title = _("AK Schedule JSON Import") def form_valid(self, form): - self.event.schedule_from_json(form.data["json_data"]) + try: + number_of_slots_changed = self.event.schedule_from_json(form.data["json_data"]) + messages.add_message( + self.request, + messages.SUCCESS, + _("Successfully imported {n} slot(s)").format(n=number_of_slots_changed) + ) + except ValueError as ex: + messages.add_message( + self.request, + messages.ERROR, + _("Importing an AK schedule failed! Reason: ") + str(ex), + ) return redirect("admin:event_status", self.event.slug) diff --git a/AKScheduling/locale/de_DE/LC_MESSAGES/django.po b/AKScheduling/locale/de_DE/LC_MESSAGES/django.po index e66d8d55bcda42f3271b2400d6b98cf84f64ee1d..1e391c1fa08573165d8d02636ffd40027437b583 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: 2024-04-25 00:24+0200\n" +"POT-Creation-Date: 2025-01-22 19:00+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" @@ -27,7 +27,7 @@ msgstr "Ende" #: AKScheduling/forms.py:26 msgid "Duration" -msgstr "" +msgstr "Dauer" #: AKScheduling/forms.py:27 #: AKScheduling/templates/admin/AKScheduling/scheduling.html:171 @@ -107,6 +107,7 @@ msgid "Event Status" msgstr "Event-Status" #: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:113 +#: AKScheduling/views.py:48 msgid "Scheduling" msgstr "Scheduling" @@ -239,6 +240,7 @@ msgstr[1] "" " " #: AKScheduling/templates/admin/AKScheduling/unscheduled.html:7 +#: AKScheduling/views.py:25 msgid "Unscheduled AK Slots" msgstr "Noch nicht geschedulte AK-Slots" @@ -246,10 +248,22 @@ msgstr "Noch nicht geschedulte AK-Slots" msgid "Count" msgstr "Anzahl" +#: AKScheduling/views.py:89 +msgid "Constraint violations for" +msgstr "Constraintverletzungen für" + +#: AKScheduling/views.py:104 +msgid "AKs requiring special attention for" +msgstr "AKs die besondere Aufmerksamkeit erfordern für" + #: AKScheduling/views.py:150 msgid "Interest updated" msgstr "Interesse aktualisiert" +#: AKScheduling/views.py:157 +msgid "Enter interest" +msgstr "Interesse eingeben" + #: AKScheduling/views.py:201 msgid "Wishes" msgstr "Wünsche" diff --git a/AKSubmission/locale/de_DE/LC_MESSAGES/django.po b/AKSubmission/locale/de_DE/LC_MESSAGES/django.po index d395dfbc7a63f2af496babc33056210d375cff4d..1d21dff1bde7a9e89ec7fb877a30c7cd311a10e4 100644 --- a/AKSubmission/locale/de_DE/LC_MESSAGES/django.po +++ b/AKSubmission/locale/de_DE/LC_MESSAGES/django.po @@ -420,7 +420,11 @@ msgstr "" msgid "Currently planned AKs" msgstr "Aktuell geplante AKs" -#: AKSubmission/views.py:305 +#: AKSubmission/views.py:231 +msgid "AKs with Track" +msgstr "AK mit Track" + +#: AKSubmission/views.py:300 msgid "Event inactive. Cannot create or update." msgstr "Event inaktiv. Hinzufügen/Bearbeiten nicht möglich."