diff --git a/AKModel/admin.py b/AKModel/admin.py index 3d37232595a7efacfa17ebab69466103214c44d5..962293cb93594d1a0b1bd9bf57da039bacdb693f 100644 --- a/AKModel/admin.py +++ b/AKModel/admin.py @@ -19,7 +19,7 @@ from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequire ConstraintViolation, DefaultSlot from AKModel.urls import get_admin_urls_event_wizard, get_admin_urls_event from AKModel.views import CVMarkResolvedView, CVSetLevelViolationView, CVSetLevelWarningView, AKResetInterestView, \ - AKResetInterestCounterView, PlanPublishView, PlanUnpublishView, DefaultSlotEditorView + AKResetInterestCounterView, PlanPublishView, PlanUnpublishView, DefaultSlotEditorView, RoomBatchCreationView class EventRelatedFieldListFilter(RelatedFieldListFilter): @@ -56,6 +56,7 @@ class EventAdmin(admin.ModelAdmin): path('plan/publish/', PlanPublishView.as_view(), name="plan-publish"), path('plan/unpublish/', PlanUnpublishView.as_view(), name="plan-unpublish"), path('<slug:event_slug>/defaultSlots/', DefaultSlotEditorView.as_view(), name="default-slots-editor"), + path('<slug:event_slug>/importRooms/', RoomBatchCreationView.as_view(), name="room-import"), ]) urls.extend(super().get_urls()) return urls diff --git a/AKModel/forms.py b/AKModel/forms.py index 0f09a63b1d0bbd312201863743a6c080972a9b91..c1386538de6845cae78e7b7fdfbb3934cdab883c 100644 --- a/AKModel/forms.py +++ b/AKModel/forms.py @@ -1,3 +1,6 @@ +import csv +import io + from bootstrap_datepicker_plus import DateTimePickerInput from django import forms from django.forms.utils import ErrorList @@ -61,8 +64,10 @@ class NewEventWizardImportForm(forms.Form): renderer=None): super().__init__(data, files, auto_id, prefix, initial, error_class, label_suffix, empty_permitted, field_order, use_required_attribute, renderer) - self.fields["import_categories"].queryset = self.fields["import_categories"].queryset.filter(event=self.initial["import_event"]) - self.fields["import_requirements"].queryset = self.fields["import_requirements"].queryset.filter(event=self.initial["import_event"]) + self.fields["import_categories"].queryset = self.fields["import_categories"].queryset.filter( + event=self.initial["import_event"]) + self.fields["import_requirements"].queryset = self.fields["import_requirements"].queryset.filter( + event=self.initial["import_event"]) class NewEventWizardActivateForm(forms.ModelForm): @@ -112,3 +117,22 @@ class DefaultSlotEditorForm(AdminIntermediateForm): widget=forms.TextInput(attrs={'class': 'availabilities-editor-data'}), required=True, ) + + +class RoomBatchCreationForm(AdminIntermediateForm): + rooms = forms.CharField( + label=_('New rooms'), + help_text=_('Enter room details in CSV format. Required colum is "name", optional colums are "location", ' + '"capacity", and "url" for online/hybrid rooms. Delimiter: Semicolon'), + widget=forms.Textarea, + required=True, + ) + + def clean_rooms(self): + rooms_raw_text = self.cleaned_data["rooms"] + rooms_raw_dict = csv.DictReader(io.StringIO(rooms_raw_text), delimiter=";") + + if "name" not in rooms_raw_dict.fieldnames: + raise forms.ValidationError(_("CSV must contain a name column")) + + return rooms_raw_dict diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po index 5a302db39b93066d738d3fb84561945f57ddaf87..ba25e4c43f909e01b61eed2ebd9899692b8f395a 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-11-29 00:13+0100\n" +"POT-Creation-Date: 2022-11-30 01:57+0100\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" @@ -11,7 +11,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: AKModel/admin.py:63 AKModel/admin.py:66 +#: AKModel/admin.py:64 AKModel/admin.py:67 #: AKModel/templates/admin/AKModel/event_wizard/activate.html:32 #: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:48 #: AKModel/templates/admin/AKModel/event_wizard/finish.html:21 @@ -21,59 +21,59 @@ msgstr "" msgid "Status" msgstr "Status" -#: AKModel/admin.py:68 +#: AKModel/admin.py:69 msgid "Toggle plan visibility" msgstr "Plansichtbarkeit ändern" -#: AKModel/admin.py:72 AKModel/admin.py:83 AKModel/views.py:483 +#: AKModel/admin.py:73 AKModel/admin.py:84 AKModel/views.py:485 msgid "Publish plan" msgstr "Plan veröffentlichen" -#: AKModel/admin.py:75 AKModel/admin.py:88 AKModel/views.py:493 +#: AKModel/admin.py:76 AKModel/admin.py:89 AKModel/views.py:495 msgid "Unpublish plan" msgstr "Plan verbergen" -#: AKModel/admin.py:160 +#: AKModel/admin.py:161 msgid "Wish" msgstr "AK-Wunsch" -#: AKModel/admin.py:166 +#: AKModel/admin.py:167 msgid "Is wish" msgstr "Ist ein Wunsch" -#: AKModel/admin.py:167 +#: AKModel/admin.py:168 msgid "Is not a wish" msgstr "Ist kein Wunsch" -#: AKModel/admin.py:211 +#: AKModel/admin.py:212 msgid "Export to wiki syntax" msgstr "In Wiki-Syntax exportieren" -#: AKModel/admin.py:220 +#: AKModel/admin.py:221 msgid "Cannot export AKs from more than one event at the same time." msgstr "Kann nicht AKs von mehreren Events zur selben Zeit exportieren." -#: AKModel/admin.py:235 AKModel/views.py:463 +#: AKModel/admin.py:236 AKModel/views.py:465 msgid "Reset interest in AKs" msgstr "Interesse an AKs zurücksetzen" -#: AKModel/admin.py:240 AKModel/views.py:473 +#: AKModel/admin.py:241 AKModel/views.py:475 msgid "Reset AKs' interest counters" msgstr "Interessenszähler der AKs zurücksetzen" -#: AKModel/admin.py:324 AKModel/admin.py:331 +#: AKModel/admin.py:325 AKModel/admin.py:332 msgid "AK Details" msgstr "AK-Details" -#: AKModel/admin.py:383 AKModel/views.py:433 +#: AKModel/admin.py:384 AKModel/views.py:435 msgid "Mark Constraint Violations as manually resolved" msgstr "Markiere Constraintverletzungen als manuell behoben" -#: AKModel/admin.py:388 AKModel/views.py:443 +#: AKModel/admin.py:389 AKModel/views.py:445 msgid "Set Constraint Violations to level \"violation\"" msgstr "Constraintverletzungen auf Level \"Violation\" setzen" -#: AKModel/admin.py:393 AKModel/views.py:453 +#: AKModel/admin.py:394 AKModel/views.py:455 msgid "Set Constraint Violations to level \"warning\"" msgstr "Constraintverletzungen auf Level \"Warning\" setzen" @@ -81,7 +81,7 @@ msgstr "Constraintverletzungen auf Level \"Warning\" setzen" msgid "Availability" msgstr "Verfügbarkeit" -#: AKModel/availability/forms.py:23 AKModel/forms.py:110 +#: AKModel/availability/forms.py:23 AKModel/forms.py:115 msgid "" "Click and drag to mark the availability during the event, double-click to " "delete." @@ -154,52 +154,52 @@ msgstr "AK-Kategorie, deren Verfügbarkeit hier abgebildet wird" msgid "Availabilities" msgstr "Verfügbarkeiten" -#: AKModel/forms.py:39 +#: AKModel/forms.py:42 msgid "Copy ak requirements and ak categories of existing event" msgstr "AK-Anforderungen und AK-Kategorien eines existierenden Events kopieren" -#: AKModel/forms.py:40 +#: AKModel/forms.py:43 msgid "You can choose what to copy in the next step" msgstr "" "Im nächsten Schritt kann ausgewählt werden, was genau kopiert werden soll" -#: AKModel/forms.py:48 +#: AKModel/forms.py:51 msgid "Copy ak categories" msgstr "AK-Kategorien kopieren" -#: AKModel/forms.py:55 +#: AKModel/forms.py:58 msgid "Copy ak requirements" msgstr "AK-Anforderungen kopieren" -#: AKModel/forms.py:87 +#: AKModel/forms.py:92 msgid "# next AKs" msgstr "# nächste AKs" -#: AKModel/forms.py:88 +#: AKModel/forms.py:93 msgid "How many next AKs should be shown on a slide?" msgstr "Wie viele nächste AKs sollen auf einer Folie angezeigt werden?" -#: AKModel/forms.py:91 +#: AKModel/forms.py:96 msgid "Presentation only?" msgstr "Nur Vorstellung?" -#: AKModel/forms.py:93 AKModel/forms.py:100 +#: AKModel/forms.py:98 AKModel/forms.py:105 msgid "Yes" msgstr "Ja" -#: AKModel/forms.py:93 AKModel/forms.py:100 +#: AKModel/forms.py:98 AKModel/forms.py:105 msgid "No" msgstr "Nein" -#: AKModel/forms.py:95 +#: AKModel/forms.py:100 msgid "Restrict AKs to those that asked for chance to be presented?" msgstr "AKs auf solche, die um eine Vorstellung gebeten haben, einschränken?" -#: AKModel/forms.py:98 +#: AKModel/forms.py:103 msgid "Space for notes in wishes?" msgstr "Platz für Notizen bei den Wünschen?" -#: AKModel/forms.py:102 +#: AKModel/forms.py:107 msgid "" "Create symbols indicating space to note down owners and timeslots for " "wishes, e.g., to be filled out on a touch screen while presenting?" @@ -208,10 +208,28 @@ msgstr "" "fürWünsche markieren, z.B. um während der Präsentation auf einem Touchscreen " "ausgefüllt zu werden?" -#: AKModel/forms.py:108 AKModel/models.py:658 +#: AKModel/forms.py:113 AKModel/models.py:658 msgid "Default Slots" msgstr "Standardslots" +#: AKModel/forms.py:124 +msgid "New rooms" +msgstr "Neue Räume" + +#: AKModel/forms.py:125 +msgid "" +"Enter room details in CSV format. Required colum is \"name\", optional " +"colums are \"location\", \"capacity\", and \"url\" for online/hybrid rooms. " +"Delimiter: Semicolon" +msgstr "" +"Raumdetails im CSV-Format eingeben. Benötigte Spalte ist \"name\", optionale " +"Spalten sind \"location\", \"capacity\", und \"url\" for Online-/Hybridräume" +"Trennzeichen: Semikolon" + +#: AKModel/forms.py:136 +msgid "CSV must contain a name column" +msgstr "CSV muss eine name-Spalte enthalten" + #: AKModel/models.py:18 AKModel/models.py:175 AKModel/models.py:199 #: AKModel/models.py:218 AKModel/models.py:232 AKModel/models.py:250 #: AKModel/models.py:346 @@ -248,7 +266,7 @@ msgstr "Zeitzone" msgid "Time Zone where this event takes place in" msgstr "Zeitzone in der das Event stattfindet" -#: AKModel/models.py:27 AKModel/views.py:244 +#: AKModel/models.py:27 AKModel/views.py:246 msgid "Start" msgstr "Start" @@ -544,7 +562,7 @@ msgstr "AK präsentieren" msgid "Present results of this AK" msgstr "Die Ergebnisse dieses AKs vorstellen" -#: AKModel/models.py:273 AKModel/templates/admin/AKModel/status.html:104 +#: AKModel/models.py:273 AKModel/templates/admin/AKModel/status.html:105 msgid "Requirements" msgstr "Anforderungen" @@ -600,8 +618,8 @@ msgid "People who have indicated interest online" msgstr "Anzahl Personen, die online Interesse bekundet haben" #: AKModel/models.py:295 AKModel/models.py:502 -#: AKModel/templates/admin/AKModel/status.html:56 -#: AKModel/templates/admin/AKModel/status.html:63 AKModel/views.py:362 +#: AKModel/templates/admin/AKModel/status.html:57 +#: AKModel/templates/admin/AKModel/status.html:64 AKModel/views.py:364 msgid "AKs" msgstr "AKs" @@ -710,7 +728,7 @@ msgstr "AK-Organachrichten" msgid "Constraint Violation" msgstr "Constraintverletzung" -#: AKModel/models.py:473 AKModel/templates/admin/AKModel/status.html:86 +#: AKModel/models.py:473 AKModel/templates/admin/AKModel/status.html:87 msgid "Constraint Violations" msgstr "Constraintverletzungen" @@ -906,7 +924,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:249 +#: AKModel/views.py:251 msgid "Finish" msgstr "Abschluss" @@ -971,7 +989,7 @@ msgid "No AKs with this requirement" msgstr "Kein AK mit dieser Anforderung" #: AKModel/templates/admin/AKModel/requirements_overview.html:45 -#: AKModel/templates/admin/AKModel/status.html:120 +#: AKModel/templates/admin/AKModel/status.html:121 msgid "Add Requirement" msgstr "Anforderung hinzufügen" @@ -999,64 +1017,68 @@ msgstr "Bisher keine Räume" msgid "Add Room" msgstr "Raum hinzufügen" -#: AKModel/templates/admin/AKModel/status.html:58 +#: AKModel/templates/admin/AKModel/status.html:55 AKModel/views.py:582 +msgid "Import Rooms from CSV" +msgstr "Räume aus CSV importieren" + +#: AKModel/templates/admin/AKModel/status.html:59 msgid "No AKs yet" msgstr "Bisher keine AKs" -#: AKModel/templates/admin/AKModel/status.html:66 +#: AKModel/templates/admin/AKModel/status.html:67 msgid "Slots" msgstr "Slots" -#: AKModel/templates/admin/AKModel/status.html:69 +#: AKModel/templates/admin/AKModel/status.html:70 msgid "Unscheduled Slots" msgstr "Ungeplante Slots" -#: AKModel/templates/admin/AKModel/status.html:83 +#: AKModel/templates/admin/AKModel/status.html:84 #: AKModel/templates/admin/ak_index.html:16 msgid "Scheduling" msgstr "Scheduling" -#: AKModel/templates/admin/AKModel/status.html:88 +#: AKModel/templates/admin/AKModel/status.html:89 msgid "AKs requiring special attention" msgstr "AKs, die besondere Aufmerksamkeit benötigen" -#: AKModel/templates/admin/AKModel/status.html:90 +#: AKModel/templates/admin/AKModel/status.html:91 msgid "Enter Interest" msgstr "Interesse erfassen" -#: AKModel/templates/admin/AKModel/status.html:93 AKModel/views.py:505 +#: AKModel/templates/admin/AKModel/status.html:94 AKModel/views.py:507 msgid "Edit Default Slots" msgstr "Standardslots bearbeiten" -#: AKModel/templates/admin/AKModel/status.html:95 +#: AKModel/templates/admin/AKModel/status.html:96 msgid "Manage ak tracks" msgstr "AK-Tracks verwalten" -#: AKModel/templates/admin/AKModel/status.html:97 +#: AKModel/templates/admin/AKModel/status.html:98 msgid "Export AKs as CSV" msgstr "AKs als CSV exportieren" -#: AKModel/templates/admin/AKModel/status.html:99 +#: AKModel/templates/admin/AKModel/status.html:100 msgid "Export AKs for Wiki" msgstr "AKs im Wiki-Format exportieren" -#: AKModel/templates/admin/AKModel/status.html:101 AKModel/views.py:332 +#: AKModel/templates/admin/AKModel/status.html:102 AKModel/views.py:334 msgid "Export AK Slides" msgstr "AK-Folien exportieren" -#: AKModel/templates/admin/AKModel/status.html:106 +#: AKModel/templates/admin/AKModel/status.html:107 msgid "No requirements yet" msgstr "Bisher keine Anforderungen" -#: AKModel/templates/admin/AKModel/status.html:119 +#: AKModel/templates/admin/AKModel/status.html:120 msgid "Show AKs for requirements" msgstr "Zu Anforderungen gehörige AKs anzeigen" -#: AKModel/templates/admin/AKModel/status.html:123 +#: AKModel/templates/admin/AKModel/status.html:124 msgid "Messages" msgstr "Nachrichten" -#: AKModel/templates/admin/AKModel/status.html:125 +#: AKModel/templates/admin/AKModel/status.html:126 msgid "Delete all messages" msgstr "Alle Nachrichten löschen" @@ -1093,148 +1115,162 @@ msgstr "Login" msgid "Register" msgstr "Registrieren" -#: AKModel/views.py:150 +#: AKModel/views.py:152 msgid "Event Status" msgstr "Eventstatus" -#: AKModel/views.py:163 +#: AKModel/views.py:165 msgid "Requirements for Event" msgstr "Anforderungen für das Event" -#: AKModel/views.py:177 +#: AKModel/views.py:179 msgid "AK CSV Export" msgstr "AK-CSV-Export" -#: AKModel/views.py:191 +#: AKModel/views.py:193 msgid "AK Wiki Export" msgstr "AK-Wiki-Export" -#: AKModel/views.py:199 AKModel/views.py:348 +#: AKModel/views.py:201 AKModel/views.py:350 msgid "Wishes" msgstr "Wünsche" -#: AKModel/views.py:220 +#: AKModel/views.py:222 msgid "Delete AK Orga Messages" msgstr "AK-Organachrichten löschen" -#: AKModel/views.py:235 +#: AKModel/views.py:237 msgid "AK Orga Messages successfully deleted" msgstr "AK-Organachrichten erfolgreich gelöscht" -#: AKModel/views.py:245 +#: AKModel/views.py:247 msgid "Settings" msgstr "Einstellungen" -#: AKModel/views.py:246 +#: AKModel/views.py:248 msgid "Event created, Prepare Import" msgstr "Event angelegt, Import vorbereiten" -#: AKModel/views.py:247 +#: AKModel/views.py:249 msgid "Import categories & requirements" msgstr "Kategorien & Anforderungen kopieren" -#: AKModel/views.py:248 +#: AKModel/views.py:250 msgid "Activate?" msgstr "Aktivieren?" -#: AKModel/views.py:307 +#: AKModel/views.py:309 #, python-format msgid "Copied '%(obj)s'" msgstr "'%(obj)s' kopiert" -#: AKModel/views.py:310 +#: AKModel/views.py:312 #, python-format msgid "Could not copy '%(obj)s' (%(error)s)" msgstr "'%(obj)s' konnte nicht kopiert werden (%(error)s)" -#: AKModel/views.py:343 +#: AKModel/views.py:345 msgid "Symbols" msgstr "Symbole" -#: AKModel/views.py:344 +#: AKModel/views.py:346 msgid "Who?" msgstr "Wer?" -#: AKModel/views.py:345 +#: AKModel/views.py:347 msgid "Duration(s)" msgstr "Dauer(n)" -#: AKModel/views.py:346 +#: AKModel/views.py:348 msgid "Reso intention?" msgstr "Resolutionsabsicht?" -#: AKModel/views.py:347 +#: AKModel/views.py:349 msgid "Category (for Wishes)" msgstr "Kategorie (für Wünsche)" -#: AKModel/views.py:435 +#: AKModel/views.py:437 msgid "The following Constraint Violations will be marked as manually resolved" msgstr "" "Die folgenden Constraintverletzungen werden als manuell behoben markiert." -#: AKModel/views.py:436 +#: AKModel/views.py:438 msgid "Constraint Violations marked as resolved" msgstr "Constraintverletzungen als manuell behoben markiert" -#: AKModel/views.py:445 +#: AKModel/views.py:447 msgid "The following Constraint Violations will be set to level 'violation'" msgstr "" "Die folgenden Constraintverletzungen werden auf das Level \"Violation\" " "gesetzt." -#: AKModel/views.py:446 +#: AKModel/views.py:448 msgid "Constraint Violations set to level 'violation'" msgstr "Constraintverletzungen auf Level \"Violation\" gesetzt" -#: AKModel/views.py:455 +#: AKModel/views.py:457 msgid "The following Constraint Violations will be set to level 'warning'" msgstr "" "Die folgenden Constraintverletzungen werden auf das Level 'warning' gesetzt." -#: AKModel/views.py:456 +#: AKModel/views.py:458 msgid "Constraint Violations set to level 'warning'" msgstr "Constraintverletzungen auf Level \"Warning\" gesetzt" -#: AKModel/views.py:465 +#: AKModel/views.py:467 msgid "Interest of the following AKs will be set to not filled (-1):" msgstr "Interesse an den folgenden AKs wird auf nicht ausgefüllt (-1) gesetzt:" -#: AKModel/views.py:466 +#: AKModel/views.py:468 msgid "Reset of interest in AKs successful." msgstr "Interesse an AKs erfolgreich zurückgesetzt." -#: AKModel/views.py:475 +#: AKModel/views.py:477 msgid "Interest counter of the following AKs will be set to 0:" msgstr "Interessensbekundungszähler der folgenden AKs wird auf 0 gesetzt:" -#: AKModel/views.py:476 +#: AKModel/views.py:478 msgid "AKs' interest counters set back to 0." msgstr "Interessenszähler der AKs zurückgesetzt" -#: AKModel/views.py:485 +#: AKModel/views.py:487 msgid "Publish the plan(s) of:" msgstr "Den Plan/die Pläne veröffentlichen von:" -#: AKModel/views.py:486 +#: AKModel/views.py:488 msgid "Plan published" msgstr "Plan veröffentlicht" -#: AKModel/views.py:495 +#: AKModel/views.py:497 msgid "Unpublish the plan(s) of:" msgstr "Den Plan/die Pläne verbergen von:" -#: AKModel/views.py:496 +#: AKModel/views.py:498 msgid "Plan unpublished" msgstr "Plan verborgen" -#: AKModel/views.py:542 +#: AKModel/views.py:544 #, python-brace-format msgid "Could not update slot {id} since it does not belong to {event}" msgstr "" "Konnte Slot {id} nicht bearbeiten, da er nicht zum Event {event} gehört" -#: AKModel/views.py:572 +#: AKModel/views.py:574 #, python-brace-format msgid "Updated {u} slot(s). created {c} new slot(s) and deleted {d} slot(s)" msgstr "" "{u} Slot(s) aktualisiert, {c} Slot(s) hinzugefügt und {d} Slot(s) gelöscht" + +#: AKModel/views.py:611 +#, python-brace-format +msgid "Could not import room {name}: {e}" +msgstr "Konnte Raum {name} nicht importieren: {e}" + +#: AKModel/views.py:615 +#, python-brace-format +msgid "Imported {count} room(s)" +msgstr "{count} Raum/Räume importiert" + +#: AKModel/views.py:617 +msgid "No rooms imported" +msgstr "Keine Räume importiert" diff --git a/AKModel/templates/admin/AKModel/status.html b/AKModel/templates/admin/AKModel/status.html index 8530e444719bbb2be948dc55f7376ed0f23bee01..d315c396e51cdd49004117978de766edc21b5dcc 100644 --- a/AKModel/templates/admin/AKModel/status.html +++ b/AKModel/templates/admin/AKModel/status.html @@ -52,6 +52,7 @@ </p> {% endif %} <a class="btn btn-success" href="{% url 'admin:AKModel_room_add' %}">{% trans "Add Room" %}</a> + <a class="btn btn-success" href="{% url 'admin:room-import' event_slug=event.slug %}">{% trans "Import Rooms from CSV" %}</a> <h3 class="block-header">{% trans "AKs" %}</h3> {% if event.ak_set.count == 0 %} diff --git a/AKModel/views.py b/AKModel/views.py index 76ead0da0e45f5a2b57089558ba13b6a15f6411c..dde2d17fd8b3165b703370fd9e57ee2044448cc0 100644 --- a/AKModel/views.py +++ b/AKModel/views.py @@ -1,9 +1,11 @@ +import csv import json import os import tempfile from abc import ABC, abstractmethod from itertools import zip_longest +import django.db from django.contrib import admin, messages from django.db.models.functions import Now from django.shortcuts import get_object_or_404, redirect @@ -17,7 +19,7 @@ from rest_framework import viewsets, permissions, mixins from AKModel.forms import NewEventWizardStartForm, NewEventWizardSettingsForm, NewEventWizardPrepareImportForm, \ NewEventWizardImportForm, NewEventWizardActivateForm, AdminIntermediateForm, SlideExportForm, \ - AdminIntermediateActionForm, DefaultSlotEditorForm + AdminIntermediateActionForm, DefaultSlotEditorForm, RoomBatchCreationForm from AKModel.models import Event, AK, AKSlot, Room, AKTrack, AKCategory, AKOwner, AKOrgaMessage, AKRequirement, \ ConstraintViolation, DefaultSlot from AKModel.serializers import AKSerializer, AKSlotSerializer, RoomSerializer, AKTrackSerializer, AKCategorySerializer, \ @@ -573,3 +575,44 @@ class DefaultSlotEditorView(EventSlugMixin, IntermediateAdminView): .format(u=str(updated_count), c=str(created_count), d=str(deleted_count)) ) return super().form_valid(form) + + +class RoomBatchCreationView(EventSlugMixin, IntermediateAdminView): + form_class = RoomBatchCreationForm + title = _("Import Rooms from CSV") + + def get_success_url(self): + return reverse_lazy('admin:event_status', kwargs={'slug': self.event.slug}) + + def form_valid(self, form): + from django.apps import apps + VIRTUAL_ROOMS_SUPPORT = False + if apps.is_installed("AKOnline"): + VIRTUAL_ROOMS_SUPPORT = True + from AKOnline.models import VirtualRoom + + created_count = 0 + + rooms_raw_dict: csv.DictReader = form.cleaned_data["rooms"] + for raw_room in rooms_raw_dict: + name = raw_room["name"] + location = raw_room["location"] if "location" in rooms_raw_dict.fieldnames else "" + capacity = raw_room["capacity"] if "capacity" in rooms_raw_dict.fieldnames else -1 + url = raw_room["url"] if "url" in rooms_raw_dict.fieldnames else "" + + try: + if VIRTUAL_ROOMS_SUPPORT and url != "": + VirtualRoom.objects.create(name=name, location=location, capacity=capacity, url=url, event=self.event) + else: + Room.objects.create(name=name, location=location, capacity=capacity, event=self.event) + created_count += 1 + except django.db.Error as e: + messages.add_message(self.request, messages.WARNING, + _("Could not import room {name}: {e}").format(name=name, e=str(e))) + + if created_count > 0: + messages.add_message(self.request, messages.SUCCESS, + _("Imported {count} room(s)").format(count=created_count)) + else: + messages.add_message(self.request, messages.WARNING, _("No rooms imported")) + return super().form_valid(form)