From 1133ded42c0dde439b616179ea3e972cb461987b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Mon, 26 Sep 2022 22:10:16 +0200 Subject: [PATCH 1/7] Improve slot handling for wishes This implements #144 Don't show slots, slot creation button, and availabilities for wishes on detail page Don't create slots for wishes automatically (and don't ask for durations) Automatically create one slot when wish changes to AK Automatically remove all unscheduled slots when AK changes to wish --- AKSubmission/forms.py | 2 +- .../templates/AKSubmission/ak_detail.html | 149 +++++++++--------- AKSubmission/views.py | 22 ++- 3 files changed, 94 insertions(+), 79 deletions(-) diff --git a/AKSubmission/forms.py b/AKSubmission/forms.py index e24e0a41..55959be4 100644 --- a/AKSubmission/forms.py +++ b/AKSubmission/forms.py @@ -151,7 +151,7 @@ class AKEditForm(AKForm): self.fields["tags_raw"].initial = "; ".join(str(tag) for tag in self.instance.tags.all()) -class AKWishForm(AKSubmissionForm): +class AKWishForm(AKForm): class Meta(AKForm.Meta): exclude = ['owners', 'link', 'protocol_link'] diff --git a/AKSubmission/templates/AKSubmission/ak_detail.html b/AKSubmission/templates/AKSubmission/ak_detail.html index 69324b63..e5052faf 100644 --- a/AKSubmission/templates/AKSubmission/ak_detail.html +++ b/AKSubmission/templates/AKSubmission/ak_detail.html @@ -235,95 +235,96 @@ <p style="margin-top: 30px;margin-bottom: 30px;">{{ ak.description|linebreaks }}</p> - - <table class="table"> - <thead> - <tr> - {% if not ak.event.plan_hidden or user.is_staff %} - <th>{% trans "When?" %}</th> - {% endif %} - <th>{% trans "Duration" %}</th> - {% if not ak.event.plan_hidden or user.is_staff %} - <th>{% trans "Room" %}</th> - {% endif %} - <th></th> - </tr> - </thead> - <tbody> - {% for slot in ak.akslot_set.all %} + {% if not ak.wish %} + <table class="table"> + <thead> <tr> {% if not ak.event.plan_hidden or user.is_staff %} - <td>{{ slot.time_simplified }}</td> + <th>{% trans "When?" %}</th> {% endif %} - <td>{{ slot.duration_simplified }}</td> + <th>{% trans "Duration" %}</th> {% if not ak.event.plan_hidden or user.is_staff %} - <td> - {% if slot.room %} - {% if "AKPlan"|check_app_installed %} - <a href="{% url 'plan:plan_room' event_slug=ak.event.slug pk=slot.room.pk %}">{{ slot.room }}</a> + <th>{% trans "Room" %}</th> + {% endif %} + <th></th> + </tr> + </thead> + <tbody> + {% for slot in ak.akslot_set.all %} + <tr> + {% if not ak.event.plan_hidden or user.is_staff %} + <td>{{ slot.time_simplified }}</td> + {% endif %} + <td>{{ slot.duration_simplified }}</td> + {% if not ak.event.plan_hidden or user.is_staff %} + <td> + {% if slot.room %} + {% if "AKPlan"|check_app_installed %} + <a href="{% url 'plan:plan_room' event_slug=ak.event.slug pk=slot.room.pk %}">{{ slot.room }}</a> + {% else %} + {{ slot.room }} + {% endif %} {% else %} - {{ slot.room }} + - {% endif %} + </td> + {% endif %} + <td> + {% if not slot.start %} + <a href="{% url 'submit:akslot_edit' event_slug=ak.event.slug pk=slot.pk %}" + data-toggle="tooltip" title="{% trans 'Edit' %}" + class="btn btn-success">{% fa5_icon 'pencil-alt' 'fas' %}</a> + <a href="{% url 'submit:akslot_delete' event_slug=ak.event.slug pk=slot.pk %}" + data-toggle="tooltip" title="{% trans 'Delete' %}" + class="btn btn-danger">{% fa5_icon 'times' 'fas' %}</a> {% else %} - - + {% if "AKOnline"|check_app_installed and slot.room and slot.room.virtualroom and slot.room.virtualroom.url != '' %} + <a class="btn btn-success" href="{{ slot.room.virtualroom.url }}"> + {% fa5_icon 'external-link-alt' 'fas' %} {% trans "Go to virtual room" %} + </a> + {% endif %} {% endif %} - </td> - {% endif %} - <td> - {% if not slot.start %} - <a href="{% url 'submit:akslot_edit' event_slug=ak.event.slug pk=slot.pk %}" - data-toggle="tooltip" title="{% trans 'Edit' %}" - class="btn btn-success">{% fa5_icon 'pencil-alt' 'fas' %}</a> - <a href="{% url 'submit:akslot_delete' event_slug=ak.event.slug pk=slot.pk %}" - data-toggle="tooltip" title="{% trans 'Delete' %}" - class="btn btn-danger">{% fa5_icon 'times' 'fas' %}</a> - {% else %} - {% if "AKOnline"|check_app_installed and slot.room and slot.room.virtualroom and slot.room.virtualroom.url != '' %} - <a class="btn btn-success" href="{{ slot.room.virtualroom.url }}"> - {% fa5_icon 'external-link-alt' 'fas' %} {% trans "Go to virtual room" %} - </a> + {% if user.is_staff %} + <a href="{% url 'admin:AKModel_akslot_change' slot.pk %}" + data-toggle="tooltip" title="{% trans 'Schedule' %}" + class="btn btn-outline-success">{% fa5_icon 'stream' 'fas' %}</a> {% endif %} - {% endif %} - {% if user.is_staff %} - <a href="{% url 'admin:AKModel_akslot_change' slot.pk %}" - data-toggle="tooltip" title="{% trans 'Schedule' %}" - class="btn btn-outline-success">{% fa5_icon 'stream' 'fas' %}</a> - {% endif %} - </td> - </tr> - {% endfor %} - </tbody> - </table> + </td> + </tr> + {% endfor %} + </tbody> + </table> - {% if ak.event.active %} - <div class=""> - <a href="{% url 'submit:akslot_add' event_slug=ak.event.slug pk=ak.pk %}" - class="btn btn-success">{% fa5_icon 'plus' 'fas' %} {% trans "Add another slot" %}</a> - </div> - {% endif %} + {% if ak.event.active %} + <div class=""> + <a href="{% url 'submit:akslot_add' event_slug=ak.event.slug pk=ak.pk %}" + class="btn btn-success">{% fa5_icon 'plus' 'fas' %} {% trans "Add another slot" %}</a> + </div> + {% endif %} - {% if 'AKPlan'|check_app_installed %} - <div id='akSlotCalendar' style="margin-top: 50px;margin-bottom: 50px;"></div> - {% endif %} + {% if 'AKPlan'|check_app_installed %} + <div id='akSlotCalendar' style="margin-top: 50px;margin-bottom: 50px;"></div> + {% endif %} - <h4 style="margin-top: 30px;">{% trans "Possible Times" %}</h4> - <table class="table"> - <thead> - <tr> - <th>{% trans "Start" %}</th> - <th>{% trans "End" %}</th> - </tr> - </thead> - <tbody> - {% for a in availabilities %} + <h4 style="margin-top: 30px;">{% trans "Possible Times" %}</h4> + <table class="table"> + <thead> <tr> - <td>{{ a.start | timezone:event.timezone | date:"l H:i" }}</td> - <td>{{ a.end | timezone:event.timezone | date:"l H:i" }}</td> + <th>{% trans "Start" %}</th> + <th>{% trans "End" %}</th> </tr> - {% endfor %} - </tbody> - </table> + </thead> + <tbody> + {% for a in availabilities %} + <tr> + <td>{{ a.start | timezone:event.timezone | date:"l H:i" }}</td> + <td>{{ a.end | timezone:event.timezone | date:"l H:i" }}</td> + </tr> + {% endfor %} + </tbody> + </table> + {% endif %} {% endblock %} diff --git a/AKSubmission/views.py b/AKSubmission/views.py index 28e6b6d1..15d0811d 100644 --- a/AKSubmission/views.py +++ b/AKSubmission/views.py @@ -223,10 +223,11 @@ class AKAndAKWishSubmissionView(EventSlugMixin, EventInactiveRedirectMixin, Crea tag, was_created = AKTag.objects.get_or_create(name=tag_name) self.object.tags.add(tag) - # Generate slot(s) - for duration in form.cleaned_data["durations"]: - new_slot = AKSlot(ak=self.object, duration=duration, event=self.object.event) - new_slot.save() + # Generate slot(s) (but not for wishes) + if "durations" in form.cleaned_data: + for duration in form.cleaned_data["durations"]: + new_slot = AKSlot(ak=self.object, duration=duration, event=self.object.event) + new_slot.save() return super_form_valid @@ -269,6 +270,8 @@ class AKEditView(EventSlugMixin, EventInactiveRedirectMixin, UpdateView): return redirect(reverse_lazy('submit:submission_overview', kwargs={'event_slug': form.cleaned_data["event"].slug})) + previous_owner_count = self.object.owners.count() + super_form_valid = super().form_valid(form) # Detach existing tags @@ -279,6 +282,17 @@ class AKEditView(EventSlugMixin, EventInactiveRedirectMixin, UpdateView): tag, was_created = AKTag.objects.get_or_create(name=tag_name) self.object.tags.add(tag) + # Did this AK change from wish to AK or vice versa? + new_owner_count = self.object.owners.count() + # Now AK: + if previous_owner_count == 0 and new_owner_count > 0 and self.object.akslot_set.count() == 0: + # Create one slot with default length + AKSlot.objects.create(ak=self.object, duration=self.object.event.default_slot, event=self.object.event) + # Now wish: + elif previous_owner_count > 0 and new_owner_count == 0: + # Delete all unscheduled slots + self.object.akslot_set.filter(start__isnull=True).delete() + return super_form_valid -- GitLab From 2f9789a89c619d8a820344a247e33a973cbfaefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Tue, 27 Sep 2022 13:24:08 +0200 Subject: [PATCH 2/7] Introduce generic admin action view with form & adapt orga message deletion Introduce a new generic view that can be used for all admin actions that require a preview/confirmation or a form to specify parameters Change AK Orga Message deletion view to use this generic view instead of a manually adapted delete view --- AKModel/forms.py | 4 +++ .../admin/AKModel/action_intermediate.html | 30 ++++++++++++++++ .../admin/AKModel/message_delete.html | 17 ++------- AKModel/templates/admin/AKModel/status.html | 2 +- AKModel/urls.py | 2 +- AKModel/views.py | 36 +++++++++++++++---- 6 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 AKModel/templates/admin/AKModel/action_intermediate.html diff --git a/AKModel/forms.py b/AKModel/forms.py index 43dcfa96..1f5c07ee 100644 --- a/AKModel/forms.py +++ b/AKModel/forms.py @@ -68,3 +68,7 @@ class NewEventWizardActivateForm(forms.ModelForm): class Meta: fields = ["active"] model = Event + + +class AdminIntermediateForm(forms.Form): + pass diff --git a/AKModel/templates/admin/AKModel/action_intermediate.html b/AKModel/templates/admin/AKModel/action_intermediate.html new file mode 100644 index 00000000..0c211ecb --- /dev/null +++ b/AKModel/templates/admin/AKModel/action_intermediate.html @@ -0,0 +1,30 @@ +{% extends "admin/base_site.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load bootstrap4 %} +{% load fontawesome_5 %} + + +{% block title %}{{event}}: {{ title }}{% endblock %} + +{% block content %} + {% block action_preview %} + <p> + {{ preview|linebreaksbr }} + </p> + {% endblock %} + + <form method="post">{% csrf_token %} + {% bootstrap_form form %} + + <div class="float-right"> + <button type="submit" class="save btn btn-success" value="Submit"> + {% fa5_icon "check" 'fas' %} {% trans "Confirm" %} + </button> + </div> + <a href="javascript:history.back()" class="btn btn-info"> + {% fa5_icon "times" 'fas' %} {% trans "Cancel" %} + </a> + </form> +{% endblock %} diff --git a/AKModel/templates/admin/AKModel/message_delete.html b/AKModel/templates/admin/AKModel/message_delete.html index 03b5899b..1bdbf0a5 100644 --- a/AKModel/templates/admin/AKModel/message_delete.html +++ b/AKModel/templates/admin/AKModel/message_delete.html @@ -1,24 +1,11 @@ -{% extends "admin/base_site.html" %} +{% extends "admin/AKModel/action_intermediate.html" %} {% load tags_AKModel %} {% load i18n %} {% load fontawesome_5 %} -{% block title %}{{event}}: {% trans "Delete Orga-Messages" %}{% endblock %} - -{% block content %} - <h2>{% trans "Delete AK Orga Messages" %}</h2> +{% block action_preview %} <p>{% blocktrans with message_count=ak_messages.count %}Are you sure you want to delete all orga messages for {{ event }}? This will permanently delete {{ message_count }} message(s):{% endblocktrans %}</p> {% include "admin/AKModel/render_ak_messages.html" %} - - <form method="post">{% csrf_token %} - <button type="submit" class="save btn btn-danger float-right" value="Confirm"> - {% fa5_icon "check" 'fas' %} {% trans "Delete" %} - </button> - - <a href="{% url 'admin:event_status' slug=event.slug %}" class="btn btn-info"> - {% fa5_icon "times" 'fas' %} {% trans "Cancel" %} - </a> - </form> {% endblock %} diff --git a/AKModel/templates/admin/AKModel/status.html b/AKModel/templates/admin/AKModel/status.html index f64a5a0c..362c7240 100644 --- a/AKModel/templates/admin/AKModel/status.html +++ b/AKModel/templates/admin/AKModel/status.html @@ -115,7 +115,7 @@ <div class="col-md-4"> <h3 class="block-header">{% trans "Messages" %}</h3> {% include "admin/AKModel/render_ak_messages.html" %} - <a class="btn btn-danger" href="{% url 'admin:ak_delete_orga_messages' slug=event.slug %}">{% trans "Delete all messages" %}</a> + <a class="btn btn-danger" href="{% url 'admin:ak_delete_orga_messages' event_slug=event.slug %}">{% trans "Delete all messages" %}</a> </div> </div> {% endtimezone %} diff --git a/AKModel/urls.py b/AKModel/urls.py index 2071acc9..ca9cfe67 100644 --- a/AKModel/urls.py +++ b/AKModel/urls.py @@ -79,7 +79,7 @@ def get_admin_urls_event(admin_site): name="ak_csv_export"), path('<slug:slug>/ak-wiki-export/', admin_site.admin_view(AKWikiExportView.as_view()), name="ak_wiki_export"), - path('<slug:slug>/delete-orga-messages/', admin_site.admin_view(AKMessageDeleteView.as_view()), + path('<slug:event_slug>/delete-orga-messages/', admin_site.admin_view(AKMessageDeleteView.as_view()), name="ak_delete_orga_messages"), path('<slug:event_slug>/ak-slide-export/', export_slides, name="ak_slide_export"), diff --git a/AKModel/views.py b/AKModel/views.py index 2729bdb6..08f054f1 100644 --- a/AKModel/views.py +++ b/AKModel/views.py @@ -1,3 +1,4 @@ +from abc import ABC, abstractmethod from itertools import zip_longest from django.contrib import admin, messages @@ -11,7 +12,7 @@ from django_tex.shortcuts import render_to_pdf from rest_framework import viewsets, permissions, mixins from AKModel.forms import NewEventWizardStartForm, NewEventWizardSettingsForm, NewEventWizardPrepareImportForm, \ - NewEventWizardImportForm, NewEventWizardActivateForm + NewEventWizardImportForm, NewEventWizardActivateForm, AdminIntermediateForm from AKModel.models import Event, AK, AKSlot, Room, AKTrack, AKCategory, AKOwner, AKOrgaMessage, AKRequirement from AKModel.serializers import AKSerializer, AKSlotSerializer, RoomSerializer, AKTrackSerializer, AKCategorySerializer, \ AKOwnerSerializer @@ -194,22 +195,43 @@ class AKWikiExportView(AdminViewMixin, DetailView): return context -class AKMessageDeleteView(AdminViewMixin, DeleteView): - model = Event +class IntermediateAdminView(AdminViewMixin, FormView, ABC): + template_name = "admin/AKModel/action_intermediate.html" + form_class = AdminIntermediateForm + + @abstractmethod + def get_preview(self): + pass + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["title"] = self.title + context["preview"] = self.get_preview() + return context + + +class AKMessageDeleteView(EventSlugMixin, IntermediateAdminView): template_name = "admin/AKModel/message_delete.html" + title = _("Delete AK Orga Messages") def get_orga_messages_for_event(self, event): return AKOrgaMessage.objects.filter(ak__event=event) + def get_preview(self): + return None + + def get_success_url(self): + return reverse_lazy('admin:event_status', kwargs={'slug': self.event.slug}) + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["ak_messages"] = self.get_orga_messages_for_event(self.get_object()) + context["ak_messages"] = self.get_orga_messages_for_event(self.event) return context - def post(self, request, *args, **kwargs): - self.get_orga_messages_for_event(self.get_object()).delete() + def form_valid(self, form): + self.get_orga_messages_for_event(self.event).delete() messages.add_message(self.request, messages.SUCCESS, _("AK Orga Messages successfully deleted")) - return HttpResponseRedirect(reverse_lazy('admin:event_status', kwargs={'slug': self.get_object().slug})) + return super().form_valid(form) class WizardViewMixin: -- GitLab From 563b72347a1cad3f748a0bade199830706fcb4b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Tue, 27 Sep 2022 14:18:54 +0200 Subject: [PATCH 3/7] Introduce admin action/view to delete unscheduled slots of wishes This implements first part of #145 --- AKModel/locale/de_DE/LC_MESSAGES/django.po | 413 +++++++++--------- AKModel/models.py | 11 + .../locale/de_DE/LC_MESSAGES/django.po | 33 +- .../admin/AKScheduling/special_attention.html | 1 + AKScheduling/urls.py | 4 +- AKScheduling/views.py | 22 +- 6 files changed, 270 insertions(+), 214 deletions(-) diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po index 6e3bc74d..3aefe1d4 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: 2022-08-17 22:41+0200\n" +"POT-Creation-Date: 2022-09-27 14:14+0200\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" @@ -65,17 +65,17 @@ msgstr "Die eingegebene Verfügbarkeit enthält ein ungültiges Datum." msgid "Please fill in your availabilities!" msgstr "Bitte Verfügbarkeiten eintragen!" -#: .\AKModel\availability\models.py:38 .\AKModel\models.py:53 -#: .\AKModel\models.py:117 .\AKModel\models.py:172 .\AKModel\models.py:191 -#: .\AKModel\models.py:223 .\AKModel\models.py:277 .\AKModel\models.py:343 -#: .\AKModel\models.py:376 .\AKModel\models.py:447 .\AKModel\models.py:488 +#: .\AKModel\availability\models.py:38 .\AKModel\models.py:54 +#: .\AKModel\models.py:120 .\AKModel\models.py:175 .\AKModel\models.py:194 +#: .\AKModel\models.py:226 .\AKModel\models.py:280 .\AKModel\models.py:354 +#: .\AKModel\models.py:387 .\AKModel\models.py:458 .\AKModel\models.py:499 msgid "Event" msgstr "Event" -#: .\AKModel\availability\models.py:39 .\AKModel\models.py:118 -#: .\AKModel\models.py:173 .\AKModel\models.py:192 .\AKModel\models.py:224 -#: .\AKModel\models.py:278 .\AKModel\models.py:344 .\AKModel\models.py:377 -#: .\AKModel\models.py:448 .\AKModel\models.py:489 +#: .\AKModel\availability\models.py:39 .\AKModel\models.py:121 +#: .\AKModel\models.py:176 .\AKModel\models.py:195 .\AKModel\models.py:227 +#: .\AKModel\models.py:281 .\AKModel\models.py:355 .\AKModel\models.py:388 +#: .\AKModel\models.py:459 .\AKModel\models.py:500 msgid "Associated event" msgstr "Zugehöriges Event" @@ -87,8 +87,8 @@ msgstr "Person" msgid "Person whose availability this is" msgstr "Person deren Verfügbarkeit hier abgebildet wird" -#: .\AKModel\availability\models.py:56 .\AKModel\models.py:347 -#: .\AKModel\models.py:366 .\AKModel\models.py:497 +#: .\AKModel\availability\models.py:56 .\AKModel\models.py:358 +#: .\AKModel\models.py:377 .\AKModel\models.py:508 msgid "Room" msgstr "Raum" @@ -96,8 +96,8 @@ msgstr "Raum" msgid "Room whose availability this is" msgstr "Raum dessen Verfügbarkeit hier abgebildet wird" -#: .\AKModel\availability\models.py:65 .\AKModel\models.py:283 -#: .\AKModel\models.py:365 .\AKModel\models.py:442 +#: .\AKModel\availability\models.py:65 .\AKModel\models.py:286 +#: .\AKModel\models.py:376 .\AKModel\models.py:453 msgid "AK" msgstr "AK" @@ -105,8 +105,8 @@ msgstr "AK" msgid "AK whose availability this is" msgstr "Verfügbarkeiten" -#: .\AKModel\availability\models.py:74 .\AKModel\models.py:176 -#: .\AKModel\models.py:503 +#: .\AKModel\availability\models.py:74 .\AKModel\models.py:179 +#: .\AKModel\models.py:514 msgid "AK Category" msgstr "AK-Kategorie" @@ -135,131 +135,131 @@ msgstr "AK-Kategorien kopieren" msgid "Copy ak requirements" msgstr "AK-Anforderungen kopieren" -#: .\AKModel\models.py:17 .\AKModel\models.py:164 .\AKModel\models.py:188 -#: .\AKModel\models.py:207 .\AKModel\models.py:221 .\AKModel\models.py:239 -#: .\AKModel\models.py:335 +#: .\AKModel\models.py:18 .\AKModel\models.py:167 .\AKModel\models.py:191 +#: .\AKModel\models.py:210 .\AKModel\models.py:224 .\AKModel\models.py:242 +#: .\AKModel\models.py:346 msgid "Name" msgstr "Name" -#: .\AKModel\models.py:18 +#: .\AKModel\models.py:19 msgid "Name or iteration of the event" msgstr "Name oder Iteration des Events" -#: .\AKModel\models.py:19 +#: .\AKModel\models.py:20 msgid "Short Form" msgstr "Kurzer Name" -#: .\AKModel\models.py:20 +#: .\AKModel\models.py:21 msgid "Short name of letters/numbers/dots/dashes/underscores used in URLs." msgstr "" "Kurzname bestehend aus Buchstaben, Nummern, Punkten und Unterstrichen zur " "Nutzung in URLs" -#: .\AKModel\models.py:22 +#: .\AKModel\models.py:23 msgid "Place" msgstr "Ort" -#: .\AKModel\models.py:23 +#: .\AKModel\models.py:24 msgid "City etc. the event takes place in" msgstr "Stadt o.ä. in der das Event stattfindet" -#: .\AKModel\models.py:25 +#: .\AKModel\models.py:26 msgid "Time Zone" msgstr "Zeitzone" -#: .\AKModel\models.py:25 +#: .\AKModel\models.py:26 msgid "Time Zone where this event takes place in" msgstr "Zeitzone in der das Event stattfindet" -#: .\AKModel\models.py:26 .\AKModel\views.py:220 +#: .\AKModel\models.py:27 .\AKModel\views.py:242 msgid "Start" msgstr "Start" -#: .\AKModel\models.py:26 +#: .\AKModel\models.py:27 msgid "Time the event begins" msgstr "Zeit zu der das Event beginnt" -#: .\AKModel\models.py:27 +#: .\AKModel\models.py:28 msgid "End" msgstr "Ende" -#: .\AKModel\models.py:27 +#: .\AKModel\models.py:28 msgid "Time the event ends" msgstr "Zeit zu der das Event endet" -#: .\AKModel\models.py:28 +#: .\AKModel\models.py:29 msgid "Resolution Deadline" msgstr "Resolutionsdeadline" -#: .\AKModel\models.py:29 +#: .\AKModel\models.py:30 msgid "When should AKs with intention to submit a resolution be done?" msgstr "Wann sollen AKs mit Resolutionsabsicht stattgefunden haben?" -#: .\AKModel\models.py:31 +#: .\AKModel\models.py:32 msgid "Interest Window Start" msgstr "Beginn Interessensbekundung" -#: .\AKModel\models.py:32 +#: .\AKModel\models.py:33 msgid "Opening time for expression of interest." msgstr "Öffnungszeitpunkt für die Angabe von Interesse an AKs." -#: .\AKModel\models.py:33 +#: .\AKModel\models.py:34 msgid "Interest Window End" msgstr "Ende Interessensbekundung" -#: .\AKModel\models.py:34 +#: .\AKModel\models.py:35 msgid "Closing time for expression of interest." msgstr "Öffnungszeitpunkt für die Angabe von Interesse an AKs." -#: .\AKModel\models.py:36 +#: .\AKModel\models.py:37 msgid "Public event" msgstr "Öffentliches Event" -#: .\AKModel\models.py:37 +#: .\AKModel\models.py:38 msgid "Show this event on overview page." msgstr "Zeige dieses Event auf der Übersichtseite an" -#: .\AKModel\models.py:39 +#: .\AKModel\models.py:40 msgid "Active State" msgstr "Aktiver Status" -#: .\AKModel\models.py:39 +#: .\AKModel\models.py:40 msgid "Marks currently active events" msgstr "Markiert aktuell aktive Events" -#: .\AKModel\models.py:40 +#: .\AKModel\models.py:41 msgid "Plan Hidden" msgstr "Plan verborgen" -#: .\AKModel\models.py:40 +#: .\AKModel\models.py:41 msgid "Hides plan for non-staff users" msgstr "Verbirgt den Plan für Nutzer*innen ohne erweiterte Rechte" -#: .\AKModel\models.py:43 +#: .\AKModel\models.py:44 msgid "Base URL" msgstr "URL-Prefix" -#: .\AKModel\models.py:43 +#: .\AKModel\models.py:44 msgid "Prefix for wiki link construction" msgstr "Prefix für die automatische Generierung von Wiki-Links" -#: .\AKModel\models.py:44 +#: .\AKModel\models.py:45 msgid "Wiki Export Template Name" msgstr "Wiki-Export Templatename" -#: .\AKModel\models.py:45 +#: .\AKModel\models.py:46 msgid "Default Slot Length" msgstr "Standardslotlänge" -#: .\AKModel\models.py:46 +#: .\AKModel\models.py:47 msgid "Default length in hours that is assumed for AKs in this event." msgstr "Standardlänge von Slots (in Stunden) für dieses Event" -#: .\AKModel\models.py:48 +#: .\AKModel\models.py:49 msgid "Contact email address" msgstr "E-Mail Kontaktadresse" -#: .\AKModel\models.py:50 +#: .\AKModel\models.py:51 msgid "" "An email address that is displayed on every page and can be used for all " "kinds of questions" @@ -267,75 +267,75 @@ msgstr "" "Eine Mailadresse die auf jeder Seite angezeigt wird und für alle Arten von " "Fragen genutzt werden kann" -#: .\AKModel\models.py:54 +#: .\AKModel\models.py:55 msgid "Events" msgstr "Events" -#: .\AKModel\models.py:112 +#: .\AKModel\models.py:115 msgid "Nickname" msgstr "Spitzname" -#: .\AKModel\models.py:112 +#: .\AKModel\models.py:115 msgid "Name to identify an AK owner by" msgstr "Name, durch den eine AK-Leitung identifiziert wird" -#: .\AKModel\models.py:113 +#: .\AKModel\models.py:116 msgid "Slug" msgstr "Slug" -#: .\AKModel\models.py:113 +#: .\AKModel\models.py:116 msgid "Slug for URL generation" msgstr "Slug für URL-Generierung" -#: .\AKModel\models.py:114 +#: .\AKModel\models.py:117 msgid "Institution" msgstr "Instutution" -#: .\AKModel\models.py:114 +#: .\AKModel\models.py:117 msgid "Uni etc." msgstr "Universität o.ä." -#: .\AKModel\models.py:115 .\AKModel\models.py:248 +#: .\AKModel\models.py:118 .\AKModel\models.py:251 msgid "Web Link" msgstr "Internet Link" -#: .\AKModel\models.py:115 +#: .\AKModel\models.py:118 msgid "Link to Homepage" msgstr "Link zu Homepage oder Webseite" -#: .\AKModel\models.py:121 .\AKModel\models.py:496 +#: .\AKModel\models.py:124 .\AKModel\models.py:507 msgid "AK Owner" msgstr "AK-Leitung" -#: .\AKModel\models.py:122 +#: .\AKModel\models.py:125 msgid "AK Owners" msgstr "AK-Leitungen" -#: .\AKModel\models.py:164 +#: .\AKModel\models.py:167 msgid "Name of the AK Category" msgstr "Name der AK-Kategorie" -#: .\AKModel\models.py:165 .\AKModel\models.py:189 +#: .\AKModel\models.py:168 .\AKModel\models.py:192 msgid "Color" msgstr "Farbe" -#: .\AKModel\models.py:165 .\AKModel\models.py:189 +#: .\AKModel\models.py:168 .\AKModel\models.py:192 msgid "Color for displaying" msgstr "Farbe für die Anzeige" -#: .\AKModel\models.py:166 .\AKModel\models.py:242 +#: .\AKModel\models.py:169 .\AKModel\models.py:245 msgid "Description" msgstr "Beschreibung" -#: .\AKModel\models.py:167 +#: .\AKModel\models.py:170 msgid "Short description of this AK Category" msgstr "Beschreibung der AK-Kategorie" -#: .\AKModel\models.py:168 +#: .\AKModel\models.py:171 msgid "Present by default" msgstr "Defaultmäßig präsentieren" -#: .\AKModel\models.py:170 +#: .\AKModel\models.py:173 msgid "" "Present AKs of this category by default if AK owner did not specify whether " "this AK should be presented?" @@ -343,152 +343,152 @@ msgstr "" "AKs dieser Kategorie standardmäßig vorstellen, wenn die Leitungen das für " "ihren AK nicht explizit spezifiziert haben?" -#: .\AKModel\models.py:177 +#: .\AKModel\models.py:180 msgid "AK Categories" msgstr "AK-Kategorien" -#: .\AKModel\models.py:188 +#: .\AKModel\models.py:191 msgid "Name of the AK Track" msgstr "Name des AK-Tracks" -#: .\AKModel\models.py:195 +#: .\AKModel\models.py:198 msgid "AK Track" msgstr "AK-Track" -#: .\AKModel\models.py:196 +#: .\AKModel\models.py:199 msgid "AK Tracks" msgstr "AK-Tracks" -#: .\AKModel\models.py:207 +#: .\AKModel\models.py:210 msgid "Name of the AK Tag" msgstr "Name das AK-Tags" -#: .\AKModel\models.py:210 +#: .\AKModel\models.py:213 msgid "AK Tag" msgstr "AK-Tag" -#: .\AKModel\models.py:211 +#: .\AKModel\models.py:214 msgid "AK Tags" msgstr "AK-Tags" -#: .\AKModel\models.py:221 +#: .\AKModel\models.py:224 msgid "Name of the Requirement" msgstr "Name der Anforderung" -#: .\AKModel\models.py:227 .\AKModel\models.py:500 +#: .\AKModel\models.py:230 .\AKModel\models.py:511 msgid "AK Requirement" msgstr "AK-Anforderung" -#: .\AKModel\models.py:228 +#: .\AKModel\models.py:231 msgid "AK Requirements" msgstr "AK-Anforderungen" -#: .\AKModel\models.py:239 +#: .\AKModel\models.py:242 msgid "Name of the AK" msgstr "Name des AKs" -#: .\AKModel\models.py:240 +#: .\AKModel\models.py:243 msgid "Short Name" msgstr "Kurzer Name" -#: .\AKModel\models.py:241 +#: .\AKModel\models.py:244 msgid "Name displayed in the schedule" msgstr "Name zur Anzeige im AK-Plan" -#: .\AKModel\models.py:242 +#: .\AKModel\models.py:245 msgid "Description of the AK" msgstr "Beschreibung des AKs" -#: .\AKModel\models.py:244 +#: .\AKModel\models.py:247 msgid "Owners" msgstr "Leitungen" -#: .\AKModel\models.py:245 +#: .\AKModel\models.py:248 msgid "Those organizing the AK" msgstr "Menschen, die den AK organisieren und halten" -#: .\AKModel\models.py:248 +#: .\AKModel\models.py:251 msgid "Link to wiki page" msgstr "Link zur Wiki Seite" -#: .\AKModel\models.py:249 +#: .\AKModel\models.py:252 msgid "Protocol Link" msgstr "Protokolllink" -#: .\AKModel\models.py:249 +#: .\AKModel\models.py:252 msgid "Link to protocol" msgstr "Link zum Protokoll" -#: .\AKModel\models.py:251 +#: .\AKModel\models.py:254 msgid "Category" msgstr "Kategorie" -#: .\AKModel\models.py:252 +#: .\AKModel\models.py:255 msgid "Category of the AK" msgstr "Kategorie des AKs" -#: .\AKModel\models.py:253 +#: .\AKModel\models.py:256 msgid "Tags" msgstr "Tags" -#: .\AKModel\models.py:253 +#: .\AKModel\models.py:256 msgid "Tags provided by owners" msgstr "Tags, die durch die AK-Leitung vergeben wurden" -#: .\AKModel\models.py:254 +#: .\AKModel\models.py:257 msgid "Track" msgstr "Track" -#: .\AKModel\models.py:255 +#: .\AKModel\models.py:258 msgid "Track the AK belongs to" msgstr "Track zu dem der AK gehört" -#: .\AKModel\models.py:257 +#: .\AKModel\models.py:260 msgid "Resolution Intention" msgstr "Resolutionsabsicht" -#: .\AKModel\models.py:258 +#: .\AKModel\models.py:261 msgid "Intends to submit a resolution" msgstr "Beabsichtigt eine Resolution einzureichen" -#: .\AKModel\models.py:259 +#: .\AKModel\models.py:262 msgid "Present this AK" msgstr "AK präsentieren" -#: .\AKModel\models.py:260 +#: .\AKModel\models.py:263 msgid "Present results of this AK" msgstr "Die Ergebnisse dieses AKs vorstellen" -#: .\AKModel\models.py:262 .\AKModel\templates\admin\AKModel\status.html:97 +#: .\AKModel\models.py:265 .\AKModel\templates\admin\AKModel\status.html:97 msgid "Requirements" msgstr "Anforderungen" -#: .\AKModel\models.py:263 +#: .\AKModel\models.py:266 msgid "AK's Requirements" msgstr "Anforderungen des AKs" -#: .\AKModel\models.py:265 +#: .\AKModel\models.py:268 msgid "Conflicting AKs" msgstr "AK-Konflikte" -#: .\AKModel\models.py:266 +#: .\AKModel\models.py:269 msgid "AKs that conflict and thus must not take place at the same time" msgstr "" "AKs, die Konflikte haben und deshalb nicht gleichzeitig stattfinden dürfen" -#: .\AKModel\models.py:267 +#: .\AKModel\models.py:270 msgid "Prerequisite AKs" msgstr "Vorausgesetzte AKs" -#: .\AKModel\models.py:268 +#: .\AKModel\models.py:271 msgid "AKs that should precede this AK in the schedule" msgstr "AKs die im AK-Plan vor diesem AK stattfinden müssen" -#: .\AKModel\models.py:270 +#: .\AKModel\models.py:273 msgid "Organizational Notes" msgstr "Notizen zur Organisation" -#: .\AKModel\models.py:271 +#: .\AKModel\models.py:274 #, fuzzy #| msgid "" #| "Notes to organizers. These are public. For private notes, please send an " @@ -502,258 +502,258 @@ msgstr "" "Anmerkungen bitte den Button für Direktnachrichten verwenden (nach dem " "Anlegen/Bearbeiten)." -#: .\AKModel\models.py:273 +#: .\AKModel\models.py:276 msgid "Interest" msgstr "Interesse" -#: .\AKModel\models.py:273 +#: .\AKModel\models.py:276 msgid "Expected number of people" msgstr "Erwartete Personenzahl" -#: .\AKModel\models.py:274 +#: .\AKModel\models.py:277 msgid "Interest Counter" msgstr "Interessenszähler" -#: .\AKModel\models.py:275 +#: .\AKModel\models.py:278 msgid "People who have indicated interest online" msgstr "Anzahl Personen, die online Interesse bekundet haben" -#: .\AKModel\models.py:284 .\AKModel\models.py:491 +#: .\AKModel\models.py:287 .\AKModel\models.py:502 #: .\AKModel\templates\admin\AKModel\status.html:49 -#: .\AKModel\templates\admin\AKModel\status.html:56 .\AKModel\views.py:337 +#: .\AKModel\templates\admin\AKModel\status.html:56 .\AKModel\views.py:359 msgid "AKs" msgstr "AKs" -#: .\AKModel\models.py:335 +#: .\AKModel\models.py:346 msgid "Name or number of the room" msgstr "Name oder Nummer des Raums" -#: .\AKModel\models.py:336 +#: .\AKModel\models.py:347 msgid "Location" msgstr "Ort" -#: .\AKModel\models.py:337 +#: .\AKModel\models.py:348 msgid "Name or number of the location" msgstr "Name oder Nummer des Ortes" -#: .\AKModel\models.py:338 +#: .\AKModel\models.py:349 msgid "Capacity" msgstr "Kapazität" -#: .\AKModel\models.py:339 +#: .\AKModel\models.py:350 msgid "Maximum number of people (-1 for unlimited)." msgstr "Maximale Personenzahl (-1 wenn unbeschränkt)." -#: .\AKModel\models.py:340 +#: .\AKModel\models.py:351 msgid "Properties" msgstr "Eigenschaften" -#: .\AKModel\models.py:341 +#: .\AKModel\models.py:352 msgid "AK requirements fulfilled by the room" msgstr "AK-Anforderungen, die dieser Raum erfüllt" -#: .\AKModel\models.py:348 .\AKModel\templates\admin\AKModel\status.html:33 +#: .\AKModel\models.py:359 .\AKModel\templates\admin\AKModel\status.html:33 msgid "Rooms" msgstr "Räume" -#: .\AKModel\models.py:365 +#: .\AKModel\models.py:376 msgid "AK being mapped" msgstr "AK, der zugeordnet wird" -#: .\AKModel\models.py:367 +#: .\AKModel\models.py:378 msgid "Room the AK will take place in" msgstr "Raum in dem der AK stattfindet" -#: .\AKModel\models.py:368 +#: .\AKModel\models.py:379 msgid "Slot Begin" msgstr "Beginn des Slots" -#: .\AKModel\models.py:368 +#: .\AKModel\models.py:379 msgid "Time and date the slot begins" msgstr "Zeit und Datum zu der der AK beginnt" -#: .\AKModel\models.py:370 +#: .\AKModel\models.py:381 msgid "Duration" msgstr "Dauer" -#: .\AKModel\models.py:371 +#: .\AKModel\models.py:382 msgid "Length in hours" msgstr "Länge in Stunden" -#: .\AKModel\models.py:373 +#: .\AKModel\models.py:384 msgid "Scheduling fixed" msgstr "Planung fix" -#: .\AKModel\models.py:374 +#: .\AKModel\models.py:385 msgid "Length and time of this AK should not be changed" msgstr "Dauer und Zeit dieses AKs sollten nicht verändert werden" -#: .\AKModel\models.py:379 +#: .\AKModel\models.py:390 msgid "Last update" msgstr "Letzte Aktualisierung" -#: .\AKModel\models.py:382 +#: .\AKModel\models.py:393 msgid "AK Slot" msgstr "AK-Slot" -#: .\AKModel\models.py:383 .\AKModel\models.py:493 +#: .\AKModel\models.py:394 .\AKModel\models.py:504 msgid "AK Slots" msgstr "AK-Slot" -#: .\AKModel\models.py:405 .\AKModel\models.py:414 +#: .\AKModel\models.py:416 .\AKModel\models.py:425 msgid "Not scheduled yet" msgstr "Noch nicht geplant" -#: .\AKModel\models.py:443 +#: .\AKModel\models.py:454 msgid "AK this message belongs to" msgstr "AK zu dem die Nachricht gehört" -#: .\AKModel\models.py:444 +#: .\AKModel\models.py:455 msgid "Message text" msgstr "Nachrichtentext" -#: .\AKModel\models.py:445 +#: .\AKModel\models.py:456 msgid "Message to the organizers. This is not publicly visible." msgstr "" "Nachricht an die Organisator*innen. Diese ist nicht öffentlich sichtbar." -#: .\AKModel\models.py:451 +#: .\AKModel\models.py:462 msgid "AK Orga Message" msgstr "AK-Organachricht" -#: .\AKModel\models.py:452 +#: .\AKModel\models.py:463 msgid "AK Orga Messages" msgstr "AK-Organachrichten" -#: .\AKModel\models.py:461 +#: .\AKModel\models.py:472 msgid "Constraint Violation" msgstr "Constraintverletzung" -#: .\AKModel\models.py:462 .\AKModel\templates\admin\AKModel\status.html:79 +#: .\AKModel\models.py:473 .\AKModel\templates\admin\AKModel\status.html:79 msgid "Constraint Violations" msgstr "Constraintverletzungen" -#: .\AKModel\models.py:466 +#: .\AKModel\models.py:477 msgid "Owner has two parallel slots" msgstr "Leitung hat zwei Slots parallel" -#: .\AKModel\models.py:467 +#: .\AKModel\models.py:478 msgid "AK Slot was scheduled outside the AK's availabilities" msgstr "AK Slot wurde außerhalb der Verfügbarkeit des AKs platziert" -#: .\AKModel\models.py:468 +#: .\AKModel\models.py:479 msgid "Room has two AK slots scheduled at the same time" msgstr "Raum hat zwei AK Slots gleichzeitig" -#: .\AKModel\models.py:469 +#: .\AKModel\models.py:480 msgid "Room does not satisfy the requirement of the scheduled AK" msgstr "Room erfüllt die Anforderungen des platzierten AKs nicht" -#: .\AKModel\models.py:470 +#: .\AKModel\models.py:481 msgid "AK Slot is scheduled at the same time as an AK listed as a conflict" msgstr "" "AK Slot wurde wurde zur gleichen Zeit wie ein Konflikt des AKs platziert" -#: .\AKModel\models.py:471 +#: .\AKModel\models.py:482 msgid "AK Slot is scheduled before an AK listed as a prerequisite" msgstr "AK Slot wurde vor einem als Voraussetzung gelisteten AK platziert" -#: .\AKModel\models.py:473 +#: .\AKModel\models.py:484 msgid "" "AK Slot for AK with intention to submit a resolution is scheduled after " "resolution deadline" msgstr "" "AK Slot eines AKs mit Resoabsicht wurde nach der Resodeadline platziert" -#: .\AKModel\models.py:474 +#: .\AKModel\models.py:485 msgid "AK Slot in a category is outside that categories availabilities" msgstr "AK Slot wurde außerhalb der Verfügbarkeiten seiner Kategorie" -#: .\AKModel\models.py:475 +#: .\AKModel\models.py:486 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:476 +#: .\AKModel\models.py:487 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:477 +#: .\AKModel\models.py:488 msgid "AK Slot is scheduled outside the event's availabilities" msgstr "AK Slot wurde außerhalb der Verfügbarkeit des Events platziert" -#: .\AKModel\models.py:480 +#: .\AKModel\models.py:491 msgid "Warning" msgstr "Warnung" -#: .\AKModel\models.py:481 +#: .\AKModel\models.py:492 msgid "Violation" msgstr "Verletzung" -#: .\AKModel\models.py:483 +#: .\AKModel\models.py:494 msgid "Type" msgstr "Art" -#: .\AKModel\models.py:484 +#: .\AKModel\models.py:495 msgid "Type of violation, i.e. what kind of constraint was violated" msgstr "Art der Verletzung, gibt an welche Art Constraint verletzt wurde" -#: .\AKModel\models.py:485 +#: .\AKModel\models.py:496 msgid "Level" msgstr "Level" -#: .\AKModel\models.py:486 +#: .\AKModel\models.py:497 msgid "Severity level of the violation" msgstr "Schweregrad der Verletzung" -#: .\AKModel\models.py:492 +#: .\AKModel\models.py:503 msgid "AK(s) belonging to this constraint" msgstr "AK(s), die zu diesem Constraint gehören" -#: .\AKModel\models.py:494 +#: .\AKModel\models.py:505 msgid "AK Slot(s) belonging to this constraint" msgstr "AK Slot(s), die zu diesem Constraint gehören" -#: .\AKModel\models.py:496 +#: .\AKModel\models.py:507 msgid "AK Owner belonging to this constraint" msgstr "AK Leitung(en), die zu diesem Constraint gehören" -#: .\AKModel\models.py:498 +#: .\AKModel\models.py:509 msgid "Room belonging to this constraint" msgstr "Raum, der zu diesem Constraint gehört" -#: .\AKModel\models.py:501 +#: .\AKModel\models.py:512 msgid "AK Requirement belonging to this constraint" msgstr "AK Anforderung, die zu diesem Constraint gehört" -#: .\AKModel\models.py:503 +#: .\AKModel\models.py:514 msgid "AK Category belonging to this constraint" msgstr "AK Kategorie, di zu diesem Constraint gehört" -#: .\AKModel\models.py:505 +#: .\AKModel\models.py:516 msgid "Comment" msgstr "Kommentar" -#: .\AKModel\models.py:505 +#: .\AKModel\models.py:516 msgid "Comment or further details for this violation" msgstr "Kommentar oder weitere Details zu dieser Vereletzung" -#: .\AKModel\models.py:508 +#: .\AKModel\models.py:519 msgid "Timestamp" msgstr "Timestamp" -#: .\AKModel\models.py:508 +#: .\AKModel\models.py:519 msgid "Time of creation" msgstr "Zeitpunkt der ERstellung" -#: .\AKModel\models.py:509 +#: .\AKModel\models.py:520 msgid "Manually Resolved" msgstr "Manuell behoben" -#: .\AKModel\models.py:510 +#: .\AKModel\models.py:521 msgid "Mark this violation manually as resolved" msgstr "Markiere diese Verletzung manuell als behoben" -#: .\AKModel\models.py:537 +#: .\AKModel\models.py:548 #: .\AKModel\templates\admin\AKModel\requirements_overview.html:27 msgid "Details" msgstr "Details" @@ -779,6 +779,17 @@ msgstr "" msgid "Logout" msgstr "Ausloggen" +#: .\AKModel\templates\admin\AKModel\action_intermediate.html:23 +msgid "Confirm" +msgstr "Bestätigen" + +#: .\AKModel\templates\admin\AKModel\action_intermediate.html:27 +#: .\AKModel\templates\admin\AKModel\event_wizard\import.html:24 +#: .\AKModel\templates\admin\AKModel\event_wizard\settings.html:29 +#: .\AKModel\templates\admin\AKModel\event_wizard\start.html:23 +msgid "Cancel" +msgstr "Abbrechen" + #: .\AKModel\templates\admin\AKModel\event_wizard\activate.html:9 #: .\AKModel\templates\admin\AKModel\event_wizard\created_prepare_import.html:9 #: .\AKModel\templates\admin\AKModel\event_wizard\finish.html:9 @@ -794,7 +805,7 @@ msgid "Successfully imported.<br><br>Do you want to activate your event now?" msgstr "Erfolgreich importiert.<br><br>Soll das Event jetzt aktiviert werden?" #: .\AKModel\templates\admin\AKModel\event_wizard\activate.html:27 -#: .\AKModel\views.py:225 +#: .\AKModel\views.py:247 msgid "Finish" msgstr "Abschluss" @@ -821,13 +832,6 @@ msgstr "Fortfahren" msgid "Congratulations. Everything is set up!" msgstr "Herzlichen Glückwunsch. Alles ist eingerichtet!" -#: .\AKModel\templates\admin\AKModel\event_wizard\import.html:24 -#: .\AKModel\templates\admin\AKModel\event_wizard\settings.html:29 -#: .\AKModel\templates\admin\AKModel\event_wizard\start.html:23 -#: .\AKModel\templates\admin\AKModel\message_delete.html:21 -msgid "Cancel" -msgstr "Abbrechen" - #: .\AKModel\templates\admin\AKModel\event_wizard\settings.html:26 msgid "Back" msgstr "Zurück" @@ -844,15 +848,7 @@ msgstr "" msgid "Step" msgstr "Schritt" -#: .\AKModel\templates\admin\AKModel\message_delete.html:7 -msgid "Delete Orga-Messages" -msgstr "Organachrichten löschen" - -#: .\AKModel\templates\admin\AKModel\message_delete.html:10 -msgid "Delete AK Orga Messages" -msgstr "AK-Organachrichten löschen" - -#: .\AKModel\templates\admin\AKModel\message_delete.html:11 +#: .\AKModel\templates\admin\AKModel\message_delete.html:8 #, python-format msgid "" "Are you sure you want to delete all orga messages for %(event)s? This will " @@ -861,10 +857,6 @@ msgstr "" "Sollen wirklich alle Organachrichten für %(event)s gelöscht werden? Dadurch " "werden %(message_count)s Nachricht(en) dauerhaft gelöscht:" -#: .\AKModel\templates\admin\AKModel\message_delete.html:17 -msgid "Delete" -msgstr "Löschen" - #: .\AKModel\templates\admin\AKModel\requirements_overview.html:12 msgid "Requirements Overview" msgstr "Übersicht Anforderungen" @@ -996,86 +988,93 @@ msgstr "Login" msgid "Register" msgstr "Registrieren" -#: .\AKModel\views.py:143 +#: .\AKModel\views.py:144 msgid "Event Status" msgstr "Eventstatus" -#: .\AKModel\views.py:156 +#: .\AKModel\views.py:157 msgid "Requirements for Event" msgstr "Anforderungen für das Event" -#: .\AKModel\views.py:170 +#: .\AKModel\views.py:171 msgid "AK CSV Export" msgstr "AK-CSV-Export" -#: .\AKModel\views.py:184 +#: .\AKModel\views.py:185 msgid "AK Wiki Export" msgstr "AK-Wiki-Export" -#: .\AKModel\views.py:192 .\AKModel\views.py:323 +#: .\AKModel\views.py:193 .\AKModel\views.py:345 msgid "Wishes" msgstr "Wünsche" -#: .\AKModel\views.py:211 +#: .\AKModel\views.py:215 +msgid "Delete AK Orga Messages" +msgstr "AK-Organachrichten löschen" + +#: .\AKModel\views.py:233 msgid "AK Orga Messages successfully deleted" msgstr "AK-Organachrichten erfolgreich gelöscht" -#: .\AKModel\views.py:221 +#: .\AKModel\views.py:243 msgid "Settings" msgstr "Einstellungen" -#: .\AKModel\views.py:222 +#: .\AKModel\views.py:244 msgid "Event created, Prepare Import" msgstr "Event angelegt, Import vorbereiten" -#: .\AKModel\views.py:223 +#: .\AKModel\views.py:245 msgid "Import categories & requirements" msgstr "Kategorien & Anforderungen kopieren" -#: .\AKModel\views.py:224 +#: .\AKModel\views.py:246 #, fuzzy #| msgid "Active State" msgid "Activate?" msgstr "Aktivieren?" -#: .\AKModel\views.py:283 +#: .\AKModel\views.py:305 #, python-format msgid "Copied '%(obj)s'" msgstr "'%(obj)s' kopiert" -#: .\AKModel\views.py:286 +#: .\AKModel\views.py:308 #, python-format msgid "Could not copy '%(obj)s' (%(error)s)" msgstr "'%(obj)s' konnte nicht kopiert werden (%(error)s)" -#: .\AKModel\views.py:318 +#: .\AKModel\views.py:340 msgid "Symbols" msgstr "Symbole" -#: .\AKModel\views.py:319 +#: .\AKModel\views.py:341 msgid "Who?" msgstr "Wer?" -#: .\AKModel\views.py:320 +#: .\AKModel\views.py:342 msgid "Duration(s)" msgstr "Dauer(n)" -#: .\AKModel\views.py:321 +#: .\AKModel\views.py:343 msgid "Reso intention?" msgstr "Resolutionsabsicht?" -#: .\AKModel\views.py:322 +#: .\AKModel\views.py:344 msgid "Category (for Wishes)" msgstr "Kategorie (für Wünsche)" +#~ msgid "Delete Orga-Messages" +#~ msgstr "Organachrichten löschen" + +#~ msgid "Delete" +#~ msgstr "Löschen" + #~ 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" - #~ msgid "messages will be permanently deleted:" #~ msgstr "Nachrichten werden dauerhaft gelöscht:" diff --git a/AKModel/models.py b/AKModel/models.py index 7f82cbd6..e8fcf8aa 100644 --- a/AKModel/models.py +++ b/AKModel/models.py @@ -2,6 +2,7 @@ import itertools from datetime import timedelta from django.db import models +from django.db.models import Count from django.urls import reverse_lazy from django.utils import timezone from django.utils.datetime_safe import datetime @@ -105,6 +106,8 @@ class Event(models.Model): categories_with_aks.append((category, ak_list)) return categories_with_aks + def get_unscheduled_wish_slots(self): + return self.akslot_set.filter(start__isnull=True).annotate(Count('ak__owners')).filter(ak__owners__count=0) class AKOwner(models.Model): """ An AKOwner describes the person organizing/holding an AK. @@ -328,6 +331,14 @@ class AK(models.Model): def availabilities(self): return "Availability".objects.filter(ak=self) + @property + def availabilities_total_duration(self): + from AKModel.availability.models import Availability + for a in Availability.objects.filter(ak=self): + print(a) + print(a.end) + # [a.end - a.start for a in ] + return 0 class Room(models.Model): """ A room describes where an AK can be held. diff --git a/AKScheduling/locale/de_DE/LC_MESSAGES/django.po b/AKScheduling/locale/de_DE/LC_MESSAGES/django.po index 5729c218..3bf6a9e5 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: 2022-08-17 22:41+0200\n" +"POT-Creation-Date: 2022-09-27 14:14+0200\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" @@ -79,7 +79,7 @@ msgstr "Seit" #: .\AKScheduling\templates\admin\AKScheduling\constraint_violations.html:139 #: .\AKScheduling\templates\admin\AKScheduling\manage_tracks.html:243 #: .\AKScheduling\templates\admin\AKScheduling\scheduling.html:208 -#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:43 +#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:44 #: .\AKScheduling\templates\admin\AKScheduling\unscheduled.html:34 msgid "Event Status" msgstr "Event-Status" @@ -161,7 +161,11 @@ msgstr "AKs ohne Verfügbarkeiten" msgid "AK wishes with slots" msgstr "AK-Wünsche mit Slots" -#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:35 +#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:34 +msgid "Delete slots for wishes" +msgstr "" + +#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:36 msgid "AKs without slots" msgstr "AKs ohne Slots" @@ -173,10 +177,29 @@ msgstr "Noch nicht geschedulte AK-Slots" msgid "Count" msgstr "Anzahl" -#: .\AKScheduling\views.py:103 +#: .\AKScheduling\views.py:108 msgid "Interest updated" msgstr "Interesse aktualisiert" -#: .\AKScheduling\views.py:141 +#: .\AKScheduling\views.py:146 msgid "Wishes" msgstr "Wünsche" + +#: .\AKScheduling\views.py:154 +msgid "Cleanup: Delete unscheduled slots for wishes" +msgstr "Aufräumen: Noch nicht geplante Slots für Wünsche löschen" + +#: .\AKScheduling\views.py:160 +#, python-brace-format +msgid "" +"The following {count} unscheduled slots of wishes will be deleted:\n" +"\n" +" {slots}" +msgstr "" +"Die folgenden {count} noch nicht geplanten Slots von Wünschen werden gelöscht:\n" +"\n" +" {slots}" + +#: .\AKScheduling\views.py:167 +msgid "Unscheduled slots for wishes successfully deleted" +msgstr "Noch nicht geplante Slots für Wünsche erfolgreich gelöscht" diff --git a/AKScheduling/templates/admin/AKScheduling/special_attention.html b/AKScheduling/templates/admin/AKScheduling/special_attention.html index 8977c199..a3c48d19 100644 --- a/AKScheduling/templates/admin/AKScheduling/special_attention.html +++ b/AKScheduling/templates/admin/AKScheduling/special_attention.html @@ -31,6 +31,7 @@ {% empty %} - {% endfor %} + <a class="btn btn-warning" href="{% url "admin:cleanup-wish-slots" event_slug=event.slug %}">{% trans "Delete slots for wishes" %}</a> <h4 class="mt-4 mb-4">{% trans "AKs without slots" %}</h4> {% for ak in aks_without_slots %} diff --git a/AKScheduling/urls.py b/AKScheduling/urls.py index e0fc27f5..40f293a1 100644 --- a/AKScheduling/urls.py +++ b/AKScheduling/urls.py @@ -1,7 +1,7 @@ from django.urls import path from AKScheduling.views import SchedulingAdminView, UnscheduledSlotsAdminView, TrackAdminView, \ - ConstraintViolationsAdminView, SpecialAttentionAKsAdminView, InterestEnteringAdminView + ConstraintViolationsAdminView, SpecialAttentionAKsAdminView, InterestEnteringAdminView, WishSlotCleanupView def get_admin_urls_scheduling(admin_site): @@ -14,6 +14,8 @@ def get_admin_urls_scheduling(admin_site): name="constraint-violations"), path('<slug:slug>/special-attention/', admin_site.admin_view(SpecialAttentionAKsAdminView.as_view()), name="special-attention"), + path('<slug:event_slug>/cleanup-wish-slots/', admin_site.admin_view(WishSlotCleanupView.as_view()), + name="cleanup-wish-slots"), path('<slug:event_slug>/tracks/', admin_site.admin_view(TrackAdminView.as_view()), name="tracks_manage"), path('<slug:event_slug>/enter-interest/<int:pk>', admin_site.admin_view(InterestEnteringAdminView.as_view()), diff --git a/AKScheduling/views.py b/AKScheduling/views.py index 47781cf0..1332060a 100644 --- a/AKScheduling/views.py +++ b/AKScheduling/views.py @@ -1,9 +1,11 @@ +from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin +from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView, DetailView, UpdateView from AKModel.models import AKSlot, AKTrack, Event, AK, AKCategory -from AKModel.views import AdminViewMixin, FilterByEventSlugMixin, EventSlugMixin +from AKModel.views import AdminViewMixin, FilterByEventSlugMixin, EventSlugMixin, IntermediateAdminView from AKScheduling.forms import AKInterestForm @@ -153,3 +155,21 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi context["categories_with_aks"] = categories_with_aks return context + + +class WishSlotCleanupView(EventSlugMixin, IntermediateAdminView): + title = _('Cleanup: Delete unscheduled slots for wishes') + def get_success_url(self): + return reverse_lazy('admin:special-attention', kwargs={'slug': self.event.slug}) + + def get_preview(self): + slots = self.event.get_unscheduled_wish_slots() + return _("The following {count} unscheduled slots of wishes will be deleted:\n\n {slots}").format( + count=len(slots), + slots=", ".join(str(s.ak) for s in slots) + ) + + def form_valid(self, form): + self.event.get_unscheduled_wish_slots().delete() + messages.add_message(self.request, messages.SUCCESS, _("Unscheduled slots for wishes successfully deleted")) + return super().form_valid(form) -- GitLab From 8abe5f1f211793713af92178f0347232d81765af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Tue, 27 Sep 2022 14:37:52 +0200 Subject: [PATCH 4/7] Speedup special attention ak admin view Use annotations to query counts of owners, slots and availabilities directly in main query --- AKScheduling/views.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/AKScheduling/views.py b/AKScheduling/views.py index 1332060a..381c0a1e 100644 --- a/AKScheduling/views.py +++ b/AKScheduling/views.py @@ -1,5 +1,6 @@ from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin +from django.db.models import Count from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views.generic import ListView, DetailView, UpdateView @@ -73,7 +74,7 @@ class SpecialAttentionAKsAdminView(AdminViewMixin, DetailView): context = super().get_context_data(**kwargs) context["title"] = f"{_('AKs requiring special attention for')} {context['event']}" - aks = AK.objects.filter(event=context["event"]) + aks = AK.objects.filter(event=context["event"]).annotate(Count('owners', distinct=True)).annotate(Count('akslot', distinct=True)).annotate(Count('availabilities', distinct=True)) aks_with_comment = [] ak_wishes_with_slots = [] aks_without_availabilities = [] @@ -83,13 +84,13 @@ class SpecialAttentionAKsAdminView(AdminViewMixin, DetailView): if ak.notes != "": aks_with_comment.append(ak) - if ak.wish: - if ak.akslot_set.count() > 0: + if ak.owners__count == 0: + if ak.akslot__count > 0: ak_wishes_with_slots.append(ak) else: - if ak.akslot_set.count() == 0: + if ak.akslot__count == 0: aks_without_slots.append(ak) - if ak.availabilities.count() == 0: + if ak.availabilities__count == 0: aks_without_availabilities.append(ak) context["aks_with_comment"] = aks_with_comment -- GitLab From ea1401beaafbcaa507bdf1f28d701fec4de80582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Tue, 27 Sep 2022 17:26:34 +0200 Subject: [PATCH 5/7] Improve page for special attention requiring AKs Show count of slots for wishes and link directly to a filtered list --- .../templates/admin/AKScheduling/special_attention.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AKScheduling/templates/admin/AKScheduling/special_attention.html b/AKScheduling/templates/admin/AKScheduling/special_attention.html index a3c48d19..68cbb11e 100644 --- a/AKScheduling/templates/admin/AKScheduling/special_attention.html +++ b/AKScheduling/templates/admin/AKScheduling/special_attention.html @@ -27,11 +27,12 @@ <h4 class="mt-4 mb-4">{% trans "AK wishes with slots" %}</h4> {% for ak in ak_wishes_with_slots %} - <a href="{% url "submit:ak_detail" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a><br> + <a href="{% url "submit:ak_detail" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a> <a href="{% url "admin:AKModel_akslot_changelist" %}?ak={{ ak.pk }}">({{ ak.akslot__count }})</a><br> {% empty %} - {% endfor %} - <a class="btn btn-warning" href="{% url "admin:cleanup-wish-slots" event_slug=event.slug %}">{% trans "Delete slots for wishes" %}</a> + + <a class="btn btn-warning mt-2" href="{% url "admin:cleanup-wish-slots" event_slug=event.slug %}">{% trans "Delete slots for wishes" %}</a> <h4 class="mt-4 mb-4">{% trans "AKs without slots" %}</h4> {% for ak in aks_without_slots %} -- GitLab From e340dfe1a63651e37a16eff52dfd43a42a7b686e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Tue, 27 Sep 2022 17:55:56 +0200 Subject: [PATCH 6/7] Enhance robustness by deactivating print statements in cv signal receivers --- AKScheduling/models.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/AKScheduling/models.py b/AKScheduling/models.py index cabf78e1..6664a8ae 100644 --- a/AKScheduling/models.py +++ b/AKScheduling/models.py @@ -157,7 +157,7 @@ def ak_owners_changed_handler(sender, instance: AK, action: str, **kwargs): c.ak_slots_tmp.add(other_slot) new_violations.append(c) - print(f"{owner} has the following conflicts: {new_violations}") + #print(f"{owner} has the following conflicts: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -203,7 +203,7 @@ def ak_conflicts_changed_handler(sender, instance: AK, action: str, **kwargs): c.ak_slots_tmp.add(other_slot) new_violations.append(c) - print(f"{instance} has the following conflicts: {new_violations}") + # print(f"{instance} has the following conflicts: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -249,7 +249,7 @@ def ak_prerequisites_changed_handler(sender, instance: AK, action: str, **kwargs c.ak_slots_tmp.add(other_slot) new_violations.append(c) - print(f"{instance} has the following conflicts: {new_violations}") + # print(f"{instance} has the following conflicts: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -298,7 +298,7 @@ def ak_requirements_changed_handler(sender, instance: AK, action: str, **kwargs) c.ak_slots_tmp.add(slot) new_violations.append(c) - print(f"{instance} has the following conflicts: {new_violations}") + # print(f"{instance} has the following conflicts: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -310,7 +310,7 @@ def ak_requirements_changed_handler(sender, instance: AK, action: str, **kwargs) @receiver(post_save, sender=AKSlot) def akslot_changed_handler(sender, instance: AKSlot, **kwargs): # Changes might affect: Duplicate parallel, Two in room, Resodeadline - print(f"{sender} changed") + # print(f"{sender} changed") event = instance.event # == Check for two parallel slots by one of the owners == @@ -341,7 +341,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): c.ak_slots_tmp.add(other_slot) new_violations.append(c) - print(f"{owner} has the following conflicts: {new_violations}") + # print(f"{owner} has the following conflicts: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -373,7 +373,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): c.ak_slots_tmp.add(other_slot) new_violations.append(c) - print(f"Multiple slots in room {instance.room}: {new_violations}") + # print(f"Multiple slots in room {instance.room}: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the slot that was recently changed (important!) @@ -437,7 +437,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): c.ak_slots_tmp.add(instance) new_violations.append(c) - print(f"{instance.ak} has the following slots outside 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!) @@ -470,7 +470,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): c.ak_slots_tmp.add(instance) new_violations.append(c) - print(f"{instance} has the following conflicts: {new_violations}") + # print(f"{instance} has the following conflicts: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -502,7 +502,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): c.ak_slots_tmp.add(other_slot) new_violations.append(c) - print(f"{instance} has the following conflicts: {new_violations}") + # print(f"{instance} has the following conflicts: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -534,7 +534,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs): c.ak_slots_tmp.add(other_slot) new_violations.append(c) - print(f"{instance} has the following conflicts: {new_violations}") + # print(f"{instance} has the following conflicts: {new_violations}") # ... and compare to/update list of existing violations of this type # belonging to the AK that was recently changed (important!) @@ -556,7 +556,7 @@ def akslot_deleted_handler(sender, instance: AKSlot, **kwargs): # Manually clean up or remove constraint violations that belong to this slot since there is no cascade deletion # for many2many relationships. Explicitly listening for AK deletion signals is not necessary since they will # transitively trigger this signal and we always set both AK and AKSlot references in a constraint violation - print(f"{instance} deleted") + # print(f"{instance} deleted") for cv in instance.constraintviolation_set.all(): # Make sure not delete CVs that e.g., show three parallel slots in a single room @@ -599,7 +599,7 @@ def room_requirements_changed_handler(sender, instance: Room, action: str, **kwa @receiver(post_save, sender=Availability) def availability_changed_handler(sender, instance: Availability, **kwargs): # Changes might affect: category availability, AK availability, Room availability - print(f"{instance} changed") + # print(f"{instance} changed") event = instance.event @@ -627,7 +627,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 outside 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!) -- GitLab From 5fd2fc96090c8aad8a02cc4548259c2c5882c724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?= <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de> Date: Tue, 27 Sep 2022 18:10:03 +0200 Subject: [PATCH 7/7] Create admin action to create default availabilities Introduce admin action Add function to find AKs without availabilities to event model Link new action on view of AKs requiring special attention Minor design improvements This implements the final part of #145 --- AKModel/models.py | 12 ++--- .../locale/de_DE/LC_MESSAGES/django.po | 52 +++++++++++++++---- .../admin/AKScheduling/special_attention.html | 9 ++-- AKScheduling/urls.py | 5 +- AKScheduling/views.py | 37 +++++++++++++ 5 files changed, 93 insertions(+), 22 deletions(-) diff --git a/AKModel/models.py b/AKModel/models.py index e8fcf8aa..ef86329e 100644 --- a/AKModel/models.py +++ b/AKModel/models.py @@ -109,6 +109,10 @@ class Event(models.Model): def get_unscheduled_wish_slots(self): return self.akslot_set.filter(start__isnull=True).annotate(Count('ak__owners')).filter(ak__owners__count=0) + def get_aks_without_availabilities(self): + return self.ak_set.annotate(Count('availabilities', distinct=True)).annotate(Count('owners', distinct=True)).filter(availabilities__count=0, owners__count__gt=0) + + class AKOwner(models.Model): """ An AKOwner describes the person organizing/holding an AK. """ @@ -331,14 +335,6 @@ class AK(models.Model): def availabilities(self): return "Availability".objects.filter(ak=self) - @property - def availabilities_total_duration(self): - from AKModel.availability.models import Availability - for a in Availability.objects.filter(ak=self): - print(a) - print(a.end) - # [a.end - a.start for a in ] - return 0 class Room(models.Model): """ A room describes where an AK can be held. diff --git a/AKScheduling/locale/de_DE/LC_MESSAGES/django.po b/AKScheduling/locale/de_DE/LC_MESSAGES/django.po index 3bf6a9e5..ceb03d7f 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: 2022-09-27 14:14+0200\n" +"POT-Creation-Date: 2022-09-27 17:59+0200\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" @@ -79,7 +79,7 @@ msgstr "Seit" #: .\AKScheduling\templates\admin\AKScheduling\constraint_violations.html:139 #: .\AKScheduling\templates\admin\AKScheduling\manage_tracks.html:243 #: .\AKScheduling\templates\admin\AKScheduling\scheduling.html:208 -#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:44 +#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:48 #: .\AKScheduling\templates\admin\AKScheduling\unscheduled.html:34 msgid "Event Status" msgstr "Event-Status" @@ -158,14 +158,18 @@ msgid "AKs without availabilities" msgstr "AKs ohne Verfügbarkeiten" #: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:28 +msgid "Create default availabilities" +msgstr "Standardverfügbarkeiten anlegen" + +#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:31 msgid "AK wishes with slots" msgstr "AK-Wünsche mit Slots" -#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:34 +#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:38 msgid "Delete slots for wishes" msgstr "" -#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:36 +#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:40 msgid "AKs without slots" msgstr "AKs ohne Slots" @@ -177,29 +181,57 @@ msgstr "Noch nicht geschedulte AK-Slots" msgid "Count" msgstr "Anzahl" -#: .\AKScheduling\views.py:108 +#: .\AKScheduling\views.py:109 msgid "Interest updated" msgstr "Interesse aktualisiert" -#: .\AKScheduling\views.py:146 +#: .\AKScheduling\views.py:147 msgid "Wishes" msgstr "Wünsche" -#: .\AKScheduling\views.py:154 +#: .\AKScheduling\views.py:155 msgid "Cleanup: Delete unscheduled slots for wishes" msgstr "Aufräumen: Noch nicht geplante Slots für Wünsche löschen" -#: .\AKScheduling\views.py:160 +#: .\AKScheduling\views.py:162 #, python-brace-format msgid "" "The following {count} unscheduled slots of wishes will be deleted:\n" "\n" " {slots}" msgstr "" -"Die folgenden {count} noch nicht geplanten Slots von Wünschen werden gelöscht:\n" +"Die folgenden {count} noch nicht geplanten Slots von Wünschen werden " +"gelöscht:\n" "\n" " {slots}" -#: .\AKScheduling\views.py:167 +#: .\AKScheduling\views.py:169 msgid "Unscheduled slots for wishes successfully deleted" msgstr "Noch nicht geplante Slots für Wünsche erfolgreich gelöscht" + +#: .\AKScheduling\views.py:174 +msgid "Create default availabilities for AKs" +msgstr "Standardverfügbarkeiten für AKs anlegen" + +#: .\AKScheduling\views.py:181 +#, python-brace-format +msgid "" +"The following {count} AKs don't have any availability information. Create " +"default availability for them:\n" +"\n" +" {aks}" +msgstr "" +"Die folgenden {count} AKs haben keine Verfügbarkeitsinformationen. " +"Standardverfügbarkeiten für sie anlegen:\n" +"\n" +" {aks}" + +#: .\AKScheduling\views.py:199 +#, python-brace-format +msgid "Could not create default availabilities for AK: {ak}" +msgstr "Konnte keine Verfügbarkeit anlegen für AK: {ak}" + +#: .\AKScheduling\views.py:204 +#, python-brace-format +msgid "Created default availabilities for {count} AKs" +msgstr "Standardverfügbarkeiten für {count} AKs angelegt" diff --git a/AKScheduling/templates/admin/AKScheduling/special_attention.html b/AKScheduling/templates/admin/AKScheduling/special_attention.html index 68cbb11e..44d2ced1 100644 --- a/AKScheduling/templates/admin/AKScheduling/special_attention.html +++ b/AKScheduling/templates/admin/AKScheduling/special_attention.html @@ -22,14 +22,17 @@ {% for ak in aks_without_availabilities %} <a href="{% url "submit:ak_edit" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a><br> {% empty %} - - + -<br> {% endfor %} + <a class="btn btn-warning mt-2" href="{% url "admin:autocreate-availabilities" event_slug=event.slug %}">{% trans "Create default availabilities" %}</a> + + <h4 class="mt-4 mb-4">{% trans "AK wishes with slots" %}</h4> {% for ak in ak_wishes_with_slots %} <a href="{% url "submit:ak_detail" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a> <a href="{% url "admin:AKModel_akslot_changelist" %}?ak={{ ak.pk }}">({{ ak.akslot__count }})</a><br> {% empty %} - - + -<br> {% endfor %} <a class="btn btn-warning mt-2" href="{% url "admin:cleanup-wish-slots" event_slug=event.slug %}">{% trans "Delete slots for wishes" %}</a> @@ -38,7 +41,7 @@ {% for ak in aks_without_slots %} <a href="{% url "submit:ak_detail" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a><br> {% empty %} - - + -<br> {% endfor %} <div class="mt-5"> diff --git a/AKScheduling/urls.py b/AKScheduling/urls.py index 40f293a1..1db4c182 100644 --- a/AKScheduling/urls.py +++ b/AKScheduling/urls.py @@ -1,7 +1,8 @@ from django.urls import path from AKScheduling.views import SchedulingAdminView, UnscheduledSlotsAdminView, TrackAdminView, \ - ConstraintViolationsAdminView, SpecialAttentionAKsAdminView, InterestEnteringAdminView, WishSlotCleanupView + ConstraintViolationsAdminView, SpecialAttentionAKsAdminView, InterestEnteringAdminView, WishSlotCleanupView, \ + AvailabilityAutocreateView def get_admin_urls_scheduling(admin_site): @@ -16,6 +17,8 @@ def get_admin_urls_scheduling(admin_site): name="special-attention"), path('<slug:event_slug>/cleanup-wish-slots/', admin_site.admin_view(WishSlotCleanupView.as_view()), name="cleanup-wish-slots"), + path('<slug:event_slug>/autocreate-availabilities/', admin_site.admin_view(AvailabilityAutocreateView.as_view()), + name="autocreate-availabilities"), path('<slug:event_slug>/tracks/', admin_site.admin_view(TrackAdminView.as_view()), name="tracks_manage"), path('<slug:event_slug>/enter-interest/<int:pk>', admin_site.admin_view(InterestEnteringAdminView.as_view()), diff --git a/AKScheduling/views.py b/AKScheduling/views.py index 381c0a1e..3b40d07c 100644 --- a/AKScheduling/views.py +++ b/AKScheduling/views.py @@ -160,6 +160,7 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi class WishSlotCleanupView(EventSlugMixin, IntermediateAdminView): title = _('Cleanup: Delete unscheduled slots for wishes') + def get_success_url(self): return reverse_lazy('admin:special-attention', kwargs={'slug': self.event.slug}) @@ -174,3 +175,39 @@ class WishSlotCleanupView(EventSlugMixin, IntermediateAdminView): self.event.get_unscheduled_wish_slots().delete() messages.add_message(self.request, messages.SUCCESS, _("Unscheduled slots for wishes successfully deleted")) return super().form_valid(form) + + +class AvailabilityAutocreateView(EventSlugMixin, IntermediateAdminView): + title = _('Create default availabilities for AKs') + + def get_success_url(self): + return reverse_lazy('admin:special-attention', kwargs={'slug': self.event.slug}) + + def get_preview(self): + aks = self.event.get_aks_without_availabilities() + return _("The following {count} AKs don't have any availability information. " + "Create default availability for them:\n\n {aks}").format( + count=len(aks), + aks=", ".join(str(ak) for ak in aks) + ) + + def form_valid(self, form): + from AKModel.availability.models import Availability + + success_count = 0 + for ak in self.event.get_aks_without_availabilities(): + try: + availability = Availability.with_event_length(event=self.event, ak=ak) + availability.save() + success_count += 1 + except: + messages.add_message( + self.request, messages.WARNING, + _("Could not create default availabilities for AK: {ak}").format(ak=ak) + ) + + messages.add_message( + self.request, messages.SUCCESS, + _("Created default availabilities for {count} AKs").format(count=success_count) + ) + return super().form_valid(form) -- GitLab