From f3422dfe07960f28fddf8bbf470db58a52d18c6e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?=
 <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de>
Date: Tue, 16 Feb 2021 15:00:53 +0100
Subject: [PATCH 1/3] Add django bootstrap date picker dependency

---
 AKPlanning/settings.py | 1 +
 requirements.txt       | 1 +
 2 files changed, 2 insertions(+)

diff --git a/AKPlanning/settings.py b/AKPlanning/settings.py
index 0dbfeb5c..b6745ea1 100644
--- a/AKPlanning/settings.py
+++ b/AKPlanning/settings.py
@@ -51,6 +51,7 @@ INSTALLED_APPS = [
     'rest_framework',
     'simple_history',
     'registration',
+    'bootstrap_datepicker_plus',
 ]
 
 MIDDLEWARE = [
diff --git a/requirements.txt b/requirements.txt
index a32a4ed1..a7bc7027 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,5 +7,6 @@ djangorestframework==3.12.2
 django-simple-history==2.12.0
 django-registration-redux==2.9
 django-debug-toolbar==3.2
+django-bootstrap-datepicker-plus==3.0.5
 mysqlclient==2.0.3  # for production deployment
 pytz==2021.1
-- 
GitLab


From c8c87946122ce1c60a7fc27339b56b8d62dc7fff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?=
 <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de>
Date: Tue, 16 Feb 2021 15:46:46 +0100
Subject: [PATCH 2/3] Introduce and use wizard for event creation and
 configuration

The wizard is used instead of the normal built-in creation workflow
It consists of multiple steps to specify the most fundamental settings (especially the timezone), further settings, copying AKCategories and AKRequirements from existing events and activating the event once the basic configuration is done
The steps after the creation (first two steps) can be skipped
---
 .../locale/de_DE/LC_MESSAGES/django.po        |   2 +-
 AKModel/admin.py                              |  32 +++-
 AKModel/forms.py                              |  68 ++++++++
 AKModel/locale/de_DE/LC_MESSAGES/django.po    | 145 +++++++++++++++---
 .../admin/AKModel/event_wizard/activate.html  |  36 +++++
 .../event_wizard/created_prepare_import.html  |  52 +++++++
 .../admin/AKModel/event_wizard/finish.html    |  24 +++
 .../admin/AKModel/event_wizard/import.html    |  28 ++++
 .../admin/AKModel/event_wizard/settings.html  |  34 ++++
 .../admin/AKModel/event_wizard/start.html     |  26 ++++
 .../AKModel/event_wizard/wizard_steps.html    |  15 ++
 AKModel/views.py                              | 102 +++++++++++-
 AKPlan/locale/de_DE/LC_MESSAGES/django.po     |   2 +-
 AKPlanning/locale/de_DE/LC_MESSAGES/django.po |   6 +-
 .../locale/de_DE/LC_MESSAGES/django.po        |   2 +-
 .../locale/de_DE/LC_MESSAGES/django.po        |   2 +-
 locale/de_DE/LC_MESSAGES/django.po            |   8 +-
 17 files changed, 543 insertions(+), 41 deletions(-)
 create mode 100644 AKModel/forms.py
 create mode 100644 AKModel/templates/admin/AKModel/event_wizard/activate.html
 create mode 100644 AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html
 create mode 100644 AKModel/templates/admin/AKModel/event_wizard/finish.html
 create mode 100644 AKModel/templates/admin/AKModel/event_wizard/import.html
 create mode 100644 AKModel/templates/admin/AKModel/event_wizard/settings.html
 create mode 100644 AKModel/templates/admin/AKModel/event_wizard/start.html
 create mode 100644 AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html

diff --git a/AKDashboard/locale/de_DE/LC_MESSAGES/django.po b/AKDashboard/locale/de_DE/LC_MESSAGES/django.po
index 16106ec2..0e9393ff 100644
--- a/AKDashboard/locale/de_DE/LC_MESSAGES/django.po
+++ b/AKDashboard/locale/de_DE/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-04-29 21:09+0000\n"
+"POT-Creation-Date: 2021-04-29 22:48+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
diff --git a/AKModel/admin.py b/AKModel/admin.py
index 6f9b752b..bf8cd7cc 100644
--- a/AKModel/admin.py
+++ b/AKModel/admin.py
@@ -3,7 +3,7 @@ from django.contrib import admin
 from django.contrib.admin import SimpleListFilter
 from django.db.models import Count, F
 from django import forms
-from django.shortcuts import render
+from django.shortcuts import render, redirect
 from django.urls import path, reverse_lazy
 from django.utils import timezone
 from django.utils.html import format_html
@@ -16,7 +16,9 @@ from AKModel.availability.forms import AvailabilitiesFormMixin
 from AKModel.availability.models import Availability
 from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequirement, AK, AKSlot, Room, AKOrgaMessage, \
     ConstraintViolation
-from AKModel.views import EventStatusView, AKCSVExportView, AKWikiExportView, AKMessageDeleteView, AKRequirementOverview
+from AKModel.views import EventStatusView, AKCSVExportView, AKWikiExportView, AKMessageDeleteView, AKRequirementOverview, \
+    NewEventWizardStartView, NewEventWizardSettingsView, NewEventWizardPrepareImportView, NewEventWizardFinishView, \
+    NewEventWizardImportView, NewEventWizardActivateView
 
 
 @admin.register(Event)
@@ -27,9 +29,29 @@ class EventAdmin(admin.ModelAdmin):
     list_editable = ['active']
     ordering = ['-start']
 
+    def add_view(self, request, form_url='', extra_context=None):
+        # Always use wizard to create new events
+        # (the built-in form wouldn't work anyways since the timezone cannot be specified before starting to fill the form)
+        return redirect("admin:new_event_wizard_start")
+
     def get_urls(self):
         urls = super().get_urls()
         custom_urls = [
+            path('add/wizard/start/', self.admin_site.admin_view(NewEventWizardStartView.as_view()),
+                 name="new_event_wizard_start"),
+            path('add/wizard/settings/', self.admin_site.admin_view(NewEventWizardSettingsView.as_view()),
+                 name="new_event_wizard_settings"),
+            path('add/wizard/created/<slug:event_slug>/', self.admin_site.admin_view(NewEventWizardPrepareImportView.as_view()),
+                 name="new_event_wizard_prepare_import"),
+            path('add/wizard/import/<slug:event_slug>/from/<slug:import_slug>/',
+                 self.admin_site.admin_view(NewEventWizardImportView.as_view()),
+                 name="new_event_wizard_import"),
+            path('add/wizard/activate/<slug:slug>/',
+                 self.admin_site.admin_view(NewEventWizardActivateView.as_view()),
+                 name="new_event_wizard_activate"),
+            path('add/wizard/finish/<slug:slug>/',
+                 self.admin_site.admin_view(NewEventWizardFinishView.as_view()),
+                 name="new_event_wizard_finish"),
             path('<slug:slug>/status/', self.admin_site.admin_view(EventStatusView.as_view()), name="event_status"),
             path('<slug:event_slug>/requirements/', self.admin_site.admin_view(AKRequirementOverview.as_view()), name="event_requirement_overview"),
             path('<slug:event_slug>/ak-csv-export/', self.admin_site.admin_view(AKCSVExportView.as_view()), name="ak_csv_export"),
@@ -46,11 +68,7 @@ class EventAdmin(admin.ModelAdmin):
 
     def get_form(self, request, obj=None, change=False, **kwargs):
         # Use timezone of event
-        if obj is not None and obj.timezone:
-            timezone.activate(obj.timezone)
-        # No timezone available? Use UTC
-        else:
-            timezone.activate("UTC")
+        timezone.activate(obj.timezone)
         return super().get_form(request, obj, change, **kwargs)
 
 
diff --git a/AKModel/forms.py b/AKModel/forms.py
new file mode 100644
index 00000000..bdb4e8dd
--- /dev/null
+++ b/AKModel/forms.py
@@ -0,0 +1,68 @@
+from bootstrap_datepicker_plus import DateTimePickerInput
+from django import forms
+from django.forms.utils import ErrorList
+from django.utils.translation import ugettext_lazy as _
+
+from AKModel.models import Event, AKCategory, AKRequirement
+
+
+class NewEventWizardStartForm(forms.ModelForm):
+    class Meta:
+        model = Event
+        fields = ['name', 'slug', 'timezone']
+
+    is_init = forms.BooleanField(initial=True, widget=forms.HiddenInput)
+
+
+class NewEventWizardSettingsForm(forms.ModelForm):
+    class Meta:
+        model = Event
+        exclude = []
+        widgets = {
+            'name': forms.HiddenInput(),
+            'slug': forms.HiddenInput(),
+            'timezone': forms.HiddenInput(),
+            'active': forms.HiddenInput(),
+            'plan_hidden': forms.HiddenInput(),
+            'start': DateTimePickerInput(options={"format": "YYYY-MM-DD HH:mm"}),
+            'end': DateTimePickerInput(options={"format": "YYYY-MM-DD HH:mm"}),
+            'reso_deadline': DateTimePickerInput(options={"format": "YYYY-MM-DD HH:mm"}),
+        }
+
+
+class NewEventWizardPrepareImportForm(forms.Form):
+    import_event = forms.ModelChoiceField(
+        queryset=Event.objects.all(),
+        label=_("Copy ak requirements and ak categories of existing event"),
+        help_text=_("You can choose what to copy in the next step")
+    )
+
+
+class NewEventWizardImportForm(forms.Form):
+    import_categories = forms.ModelMultipleChoiceField(
+        queryset=AKCategory.objects.all(),
+        widget=forms.CheckboxSelectMultiple,
+        label=_("Copy ak categories"),
+        required=False,
+    )
+
+    import_requirements = forms.ModelMultipleChoiceField(
+        queryset=AKRequirement.objects.all(),
+        widget=forms.CheckboxSelectMultiple,
+        label=_("Copy ak requirements"),
+        required=False,
+    )
+
+    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, error_class=ErrorList,
+                 label_suffix=None, empty_permitted=False, field_order=None, use_required_attribute=None,
+                 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"])
+
+
+class NewEventWizardActivateForm(forms.ModelForm):
+    class Meta:
+        fields = ["active"]
+        model = Event
diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po
index 3f02a022..0f5209a9 100644
--- a/AKModel/locale/de_DE/LC_MESSAGES/django.po
+++ b/AKModel/locale/de_DE/LC_MESSAGES/django.po
@@ -2,7 +2,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-04-29 21:09+0000\n"
+"POT-Creation-Date: 2021-04-29 22:48+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -11,30 +11,33 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: AKModel/admin.py:44 AKModel/admin.py:45
+#: AKModel/admin.py:66 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
 #: AKModel/templates/admin/AKModel/requirements_overview.html:8
 #: AKModel/templates/admin/AKModel/status.html:7
 #: AKModel/templates/admin/ak_index.html:15
 msgid "Status"
 msgstr "Status"
 
-#: AKModel/admin.py:135
+#: AKModel/admin.py:153
 msgid "Wish"
 msgstr "AK-Wunsch"
 
-#: AKModel/admin.py:141
+#: AKModel/admin.py:159
 msgid "Is wish"
 msgstr "Ist ein Wunsch"
 
-#: AKModel/admin.py:142
+#: AKModel/admin.py:160
 msgid "Is not a wish"
 msgstr "Ist kein Wunsch"
 
-#: AKModel/admin.py:169
+#: AKModel/admin.py:187
 msgid "Export to wiki syntax"
 msgstr "In Wiki-Syntax exportieren"
 
-#: AKModel/admin.py:265
+#: AKModel/admin.py:283
 msgid "AK Details"
 msgstr "AK-Details"
 
@@ -114,6 +117,23 @@ msgstr "AK-Kategorie, deren Verfügbarkeit hier abgebildet wird"
 msgid "Availabilities"
 msgstr "Verfügbarkeiten"
 
+#: AKModel/forms.py:36
+msgid "Copy ak requirements and ak categories of existing event"
+msgstr "AK-Anforderungen und AK-Kategorien eines existierenden Events kopieren"
+
+#: AKModel/forms.py:37
+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:45
+msgid "Copy ak categories"
+msgstr "AK-Kategorien kopieren"
+
+#: AKModel/forms.py:52
+msgid "Copy ak requirements"
+msgstr "AK-Anforderungen kopieren"
+
 #: AKModel/models.py:16 AKModel/models.py:123 AKModel/models.py:144
 #: AKModel/models.py:163 AKModel/models.py:177 AKModel/models.py:195
 #: AKModel/models.py:291
@@ -150,7 +170,7 @@ msgstr "Zeitzone"
 msgid "Time Zone where this event takes place in"
 msgstr "Zeitzone in der das Event stattfindet"
 
-#: AKModel/models.py:25
+#: AKModel/models.py:25 AKModel/views.py:206
 msgid "Start"
 msgstr "Start"
 
@@ -543,7 +563,7 @@ msgstr "AK-Slot"
 
 #: AKModel/models.py:337 AKModel/models.py:442
 msgid "AK Slots"
-msgstr "AK-Slots"
+msgstr "AK-Slot"
 
 #: AKModel/models.py:359 AKModel/models.py:368
 msgid "Not scheduled yet"
@@ -726,6 +746,71 @@ msgstr ""
 msgid "Logout"
 msgstr "Ausloggen"
 
+#: 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
+#: AKModel/templates/admin/AKModel/event_wizard/import.html:9
+#: AKModel/templates/admin/AKModel/event_wizard/settings.html:9
+#: AKModel/templates/admin/AKModel/event_wizard/start.html:8
+#: AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html:3
+msgid "New event wizard"
+msgstr "Assistent zum Anlegen eines neuen Events"
+
+#: AKModel/templates/admin/AKModel/event_wizard/activate.html:18
+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:211
+msgid "Finish"
+msgstr "Abschluss"
+
+#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:16
+msgid "New event:"
+msgstr "Neues Event:"
+
+#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:30
+msgid "Your event was created and can now be further configured."
+msgstr "Das Event wurde angelegt und kann nun weiter konfiguriert werden."
+
+#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:39
+msgid "Skip Import"
+msgstr "Import überspringen"
+
+#: AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html:43
+#: AKModel/templates/admin/AKModel/event_wizard/import.html:20
+#: AKModel/templates/admin/AKModel/event_wizard/settings.html:22
+#: AKModel/templates/admin/AKModel/event_wizard/start.html:19
+msgid "Continue"
+msgstr "Fortfahren"
+
+#: AKModel/templates/admin/AKModel/event_wizard/finish.html:18
+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"
+
+#: AKModel/templates/admin/AKModel/event_wizard/start.html:13
+msgid ""
+"Add a new event. Please start by filling these basic properties. You can "
+"specify more settings later."
+msgstr ""
+"Neues Event anlegen. Bitte zunächst diese Grundeinstellungen ausfüllen, "
+"weitere Einstellungen können später gesetzt werden."
+
+#: AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html:15
+msgid "Step"
+msgstr "Schritt"
+
 #: AKModel/templates/admin/AKModel/message_delete.html:7
 msgid "Delete Orga-Messages"
 msgstr "Organachrichten löschen"
@@ -747,10 +832,6 @@ msgstr ""
 msgid "Delete"
 msgstr "Löschen"
 
-#: AKModel/templates/admin/AKModel/message_delete.html:21
-msgid "Cancel"
-msgstr "Abbrechen"
-
 #: AKModel/templates/admin/AKModel/requirements_overview.html:12
 msgid "Requirements Overview"
 msgstr "Ãœbersicht Anforderungen"
@@ -837,26 +918,54 @@ msgstr "Alle Nachrichten löschen"
 msgid "Active Events"
 msgstr "Aktive Events"
 
-#: AKModel/views.py:135
+#: AKModel/views.py:136
 msgid "Event Status"
 msgstr "Eventstatus"
 
-#: AKModel/views.py:148
+#: AKModel/views.py:149
 msgid "Requirements for Event"
 msgstr "Anforderungen für das Event"
 
-#: AKModel/views.py:162
+#: AKModel/views.py:163
 msgid "AK CSV Export"
 msgstr "AK-CSV-Export"
 
-#: AKModel/views.py:176
+#: AKModel/views.py:177
 msgid "AK Wiki Export"
 msgstr "AK-Wiki-Export"
 
-#: AKModel/views.py:196
+#: AKModel/views.py:197
 msgid "AK Orga Messages successfully deleted"
 msgstr "AK-Organachrichten erfolgreich gelöscht"
 
+#: AKModel/views.py:207
+msgid "Settings"
+msgstr "Einstellungen"
+
+#: AKModel/views.py:208
+msgid "Event created, Prepare Import"
+msgstr "Event angelegt, Import vorbereiten"
+
+#: AKModel/views.py:209
+msgid "Import categories & requirements"
+msgstr "Kategorien & Anforderungen kopieren"
+
+#: AKModel/views.py:210
+#, fuzzy
+#| msgid "Active State"
+msgid "Activate?"
+msgstr "Aktivieren?"
+
+#: AKModel/views.py:270
+#, python-format
+msgid "Copied '%(obj)s'"
+msgstr "'%(obj)s' kopiert"
+
+#: AKModel/views.py:272
+#, python-format
+msgid "Could not copy '%(obj)s' (%(error)s)"
+msgstr "'%(obj)s' konnte nicht kopiert werden (%(error)s)"
+
 #~ msgid "Confirm"
 #~ msgstr "Bestätigen"
 
diff --git a/AKModel/templates/admin/AKModel/event_wizard/activate.html b/AKModel/templates/admin/AKModel/event_wizard/activate.html
new file mode 100644
index 00000000..eb460420
--- /dev/null
+++ b/AKModel/templates/admin/AKModel/event_wizard/activate.html
@@ -0,0 +1,36 @@
+{% extends "admin/base_site.html" %}
+{% load tags_AKModel %}
+
+{% load i18n %}
+{% load bootstrap4 %}
+{% load fontawesome_5 %}
+{% load tz %}
+
+{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %}
+
+{% block content %}
+    {% include "admin/AKModel/event_wizard/wizard_steps.html" %}
+
+    <div class="text-center btn-success disabled mt-3 mb-3" style="font-size: 8em;">
+        {% fa5_icon "copy" "fas" %}
+    </div>
+
+    <h5 class="mb-3">{% trans "Successfully imported.<br><br>Do you want to activate your event now?" %}</h5>
+
+    {{ form.media }}
+
+    <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 "Finish" %}
+        </button>
+        </div>
+
+        <a href="{% url 'admin:event_status' event.slug %}" class="btn btn-info">
+            {% fa5_icon "info" 'fas' %} {% trans "Status" %}
+        </a>
+    </form>
+
+{% endblock %}
diff --git a/AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html b/AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html
new file mode 100644
index 00000000..e1fc7165
--- /dev/null
+++ b/AKModel/templates/admin/AKModel/event_wizard/created_prepare_import.html
@@ -0,0 +1,52 @@
+{% extends "admin/base_site.html" %}
+{% load tags_AKModel %}
+
+{% load i18n %}
+{% load bootstrap4 %}
+{% load fontawesome_5 %}
+{% load tz %}
+
+{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %}
+
+{% block content %}
+    {% include "admin/AKModel/event_wizard/wizard_steps.html" %}
+
+    {% timezone event.timezone %}
+        <div class="card border-success mt-3 mb-3" style="max-width: 100%;">
+          <div class="card-header">{% trans "New event:" %}</div>
+          <div class="card-body">
+            <h4 class="card-title">{{event}}</h4>
+            <p class="card-text">{{ event.start }} - {{ event.end }}</p>
+          </div>
+        </div>
+    {% endtimezone %}
+
+    <div class="text-center btn-success disabled mb-3" style="font-size: 8em;">
+        {% fa5_icon "calendar-plus" "fas" %}
+    </div>
+
+
+
+    <h5 class="mb-3">{% trans "Your event was created and can now be further configured." %}</h5>
+
+    {{ form.media }}
+
+    <form method="post">{% csrf_token %}
+        {% bootstrap_form form %}
+
+        <div class="float-right">
+            <a href="{% url 'admin:new_event_wizard_activate' event.slug %}" class="btn btn-info">
+                {% fa5_icon "forward" 'fas' %} {% trans "Skip Import" %}
+            </a>
+
+            <button type="submit" class="save btn btn-success" value="Submit">
+            {% fa5_icon "check" 'fas' %} {% trans "Continue" %}
+        </button>
+        </div>
+
+        <a href="{% url 'admin:event_status' event.slug %}" class="btn btn-info">
+            {% fa5_icon "info" 'fas' %} {% trans "Status" %}
+        </a>
+    </form>
+
+{% endblock %}
diff --git a/AKModel/templates/admin/AKModel/event_wizard/finish.html b/AKModel/templates/admin/AKModel/event_wizard/finish.html
new file mode 100644
index 00000000..16d3090b
--- /dev/null
+++ b/AKModel/templates/admin/AKModel/event_wizard/finish.html
@@ -0,0 +1,24 @@
+{% extends "admin/base_site.html" %}
+{% load tags_AKModel %}
+
+{% load i18n %}
+{% load bootstrap4 %}
+{% load fontawesome_5 %}
+{% load tz %}
+
+{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %}
+
+{% block content %}
+    {% include "admin/AKModel/event_wizard/wizard_steps.html" %}
+
+    <div class="text-center btn-success disabled mt-3 mb-3" style="font-size: 8em;">
+        {% fa5_icon "check-circle" "fas" %}
+    </div>
+
+    <h5>{% trans "Congratulations. Everything is set up!" %}</h5>
+
+    <a href="{% url 'admin:event_status' event.slug %}" class="btn btn-info float-right">
+            {% fa5_icon "info" 'fas' %}&nbsp;{% trans "Status" %}
+    </a>
+
+{% endblock %}
diff --git a/AKModel/templates/admin/AKModel/event_wizard/import.html b/AKModel/templates/admin/AKModel/event_wizard/import.html
new file mode 100644
index 00000000..d899a2d0
--- /dev/null
+++ b/AKModel/templates/admin/AKModel/event_wizard/import.html
@@ -0,0 +1,28 @@
+{% extends "admin/base_site.html" %}
+{% load tags_AKModel %}
+
+{% load i18n %}
+{% load bootstrap4 %}
+{% load fontawesome_5 %}
+{% load tz %}
+
+{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %}
+
+{% block content %}
+    {% include "admin/AKModel/event_wizard/wizard_steps.html" %}
+
+    {{ form.media }}
+
+    <form method="post">{% csrf_token %}
+        {% bootstrap_form form %}
+
+        <button type="submit" class="save btn btn-success float-right" value="Submit">
+            {% fa5_icon "check" 'fas' %} {% trans "Continue" %}
+        </button>
+
+        <a href="{% url 'admin:index' %}" class="btn btn-info">
+            {% fa5_icon "times" 'fas' %} {% trans "Cancel" %}
+        </a>
+    </form>
+
+{% endblock %}
diff --git a/AKModel/templates/admin/AKModel/event_wizard/settings.html b/AKModel/templates/admin/AKModel/event_wizard/settings.html
new file mode 100644
index 00000000..0b306d7b
--- /dev/null
+++ b/AKModel/templates/admin/AKModel/event_wizard/settings.html
@@ -0,0 +1,34 @@
+{% extends "admin/base_site.html" %}
+{% load tags_AKModel %}
+
+{% load i18n %}
+{% load bootstrap4 %}
+{% load fontawesome_5 %}
+{% load tz %}
+
+{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %}
+
+{% block content %}
+    {% include "admin/AKModel/event_wizard/wizard_steps.html" %}
+
+    {{ form.media }}
+
+    {% timezone timezone %}
+
+    <form method="post">{% csrf_token %}
+        {% bootstrap_form form %}
+
+        <button type="submit" class="save btn btn-success float-right" value="Submit">
+            {% fa5_icon "check" 'fas' %} {% trans "Continue" %}
+        </button>
+
+        <a href="{% url 'admin:new_event_wizard_start' %}" class="btn btn-info">
+            {% fa5_icon "chevron-left" 'fas' %} {% trans "Back" %}
+        </a>
+        <a href="{% url 'admin:index' %}" class="btn btn-warning">
+            {% fa5_icon "times" 'fas' %} {% trans "Cancel" %}
+        </a>
+    </form>
+
+    {% endtimezone %}
+{% endblock %}
diff --git a/AKModel/templates/admin/AKModel/event_wizard/start.html b/AKModel/templates/admin/AKModel/event_wizard/start.html
new file mode 100644
index 00000000..762cadb5
--- /dev/null
+++ b/AKModel/templates/admin/AKModel/event_wizard/start.html
@@ -0,0 +1,26 @@
+{% extends "admin/base_site.html" %}
+{% load tags_AKModel %}
+
+{% load i18n %}
+{% load bootstrap4 %}
+{% load fontawesome_5 %}
+
+{% block title %}{% trans "New event wizard" %}: {{ wizard_step_text }}{% endblock %}
+
+{% block content %}
+    {% include "admin/AKModel/event_wizard/wizard_steps.html" %}
+
+    {%  trans "Add a new event. Please start by filling these basic properties. You can specify more settings later." %}
+
+    <form method="post" action="{% url 'admin:new_event_wizard_settings' %}">{% csrf_token %}
+        {% bootstrap_form form %}
+
+        <button type="submit" class="save btn btn-success float-right" value="Submit">
+            {% fa5_icon "check" 'fas' %} {% trans "Continue" %}
+        </button>
+
+        <a href="{% url 'admin:index' %}" class="btn btn-info">
+            {% fa5_icon "times" 'fas' %} {% trans "Cancel" %}
+        </a>
+    </form>
+{% endblock %}
diff --git a/AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html b/AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html
new file mode 100644
index 00000000..a006f3d3
--- /dev/null
+++ b/AKModel/templates/admin/AKModel/event_wizard/wizard_steps.html
@@ -0,0 +1,15 @@
+{% load i18n %}
+
+<h2>{% trans "New event wizard" %}</h2>
+
+<div>
+  <ul class="pagination pagination-sm">
+    {% for step in wizard_steps %}
+    <li class="page-item {% if forloop.counter == wizard_step %}active{% else %}disabled{% endif %}">
+      <a class="page-link" href="#">{{ step }}</a>
+    </li>
+    {% endfor %}
+  </ul>
+</div>
+
+<h3>{% trans "Step" %} {{ wizard_step }}: {{ wizard_step_text }}</h3>
diff --git a/AKModel/views.py b/AKModel/views.py
index 72be1c0a..cc78a0c5 100644
--- a/AKModel/views.py
+++ b/AKModel/views.py
@@ -1,12 +1,13 @@
 from django.contrib import admin, messages
 from django.http import HttpResponseRedirect
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, redirect
 from django.urls import reverse_lazy
 from django.utils.translation import gettext_lazy as _
-
-from django.views.generic import TemplateView, DetailView, ListView, DeleteView
+from django.views.generic import TemplateView, DetailView, ListView, DeleteView, CreateView, FormView, UpdateView
 from rest_framework import viewsets, permissions, mixins
 
+from AKModel.forms import NewEventWizardStartForm, NewEventWizardSettingsForm, NewEventWizardPrepareImportForm, \
+    NewEventWizardImportForm, NewEventWizardActivateForm
 from AKModel.models import Event, AK, AKSlot, Room, AKTrack, AKCategory, AKOwner, AKOrgaMessage, AKRequirement
 from AKModel.serializers import AKSerializer, AKSlotSerializer, RoomSerializer, AKTrackSerializer, AKCategorySerializer, \
     AKOwnerSerializer
@@ -38,10 +39,10 @@ class EventSlugMixin:
         self._load_event()
         return super().create(request, *args, **kwargs)
 
-    def initial(self, request, *args, **kwargs):
+    def dispatch(self, request, *args, **kwargs):
         if self.event is None:
             self._load_event()
-        super().initial(request, *args, **kwargs)
+        return super().dispatch(request, *args, **kwargs)
 
     def get_context_data(self, *, object_list=None, **kwargs):
         context = super().get_context_data(object_list=object_list, **kwargs)
@@ -195,3 +196,94 @@ class AKMessageDeleteView(AdminViewMixin, DeleteView):
         self.get_orga_messages_for_event(self.get_object()).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}))
+
+
+class WizardViewMixin:
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context["wizard_step"] = self.wizard_step
+        context["wizard_steps"] = [
+            _("Start"),
+            _("Settings"),
+            _("Event created, Prepare Import"),
+            _("Import categories & requirements"),
+            _("Activate?"),
+            _("Finish")
+        ]
+        context["wizard_step_text"] = context["wizard_steps"][self.wizard_step - 1]
+        context["wizard_steps_total"] = len(context["wizard_steps"])
+        return context
+
+
+class NewEventWizardStartView(AdminViewMixin, WizardViewMixin,  CreateView):
+    model = Event
+    form_class = NewEventWizardStartForm
+    template_name = "admin/AKModel/event_wizard/start.html"
+    wizard_step = 1
+
+
+class NewEventWizardSettingsView(AdminViewMixin, WizardViewMixin, CreateView):
+    model = Event
+    form_class = NewEventWizardSettingsForm
+    template_name = "admin/AKModel/event_wizard/settings.html"
+    wizard_step = 2
+
+    def get_context_data(self, **kwargs):
+        context = super().get_context_data(**kwargs)
+        context["timezone"] = context["form"].cleaned_data["timezone"]
+        return context
+
+    def get_success_url(self):
+        return reverse_lazy("admin:new_event_wizard_prepare_import", kwargs={"event_slug": self.object.slug})
+
+
+class NewEventWizardPrepareImportView(WizardViewMixin, EventSlugMixin, FormView):
+    form_class = NewEventWizardPrepareImportForm
+    template_name = "admin/AKModel/event_wizard/created_prepare_import.html"
+    wizard_step = 3
+
+
+
+    def form_valid(self, form):
+        # Selected a valid event to import from? Use this to go to next step of wizard
+        return redirect("admin:new_event_wizard_import", event_slug=self.event.slug, import_slug=form.cleaned_data["import_event"].slug)
+
+
+class NewEventWizardImportView(EventSlugMixin, WizardViewMixin, FormView):
+    form_class = NewEventWizardImportForm
+    template_name = "admin/AKModel/event_wizard/import.html"
+    wizard_step = 4
+
+    def get_initial(self):
+        initial = super().get_initial()
+        initial["import_event"] = Event.objects.get(slug=self.kwargs["import_slug"])
+        return initial
+
+    def form_valid(self, form):
+        for import_type in ["import_categories", "import_requirements"]:
+            for import_obj in form.cleaned_data.get(import_type):
+                # clone existing entry
+                try:
+                    import_obj.event = self.event
+                    import_obj.pk = None
+                    import_obj.save()
+                    messages.add_message(self.request, messages.SUCCESS, _("Copied '%(obj)s'" % {'obj': import_obj}))
+                except BaseException as e:
+                    messages.add_message(self.request, messages.ERROR, _("Could not copy '%(obj)s' (%(error)s)" %  {'obj': import_obj, "error": str(e)}))
+        return redirect("admin:new_event_wizard_activate", slug=self.event.slug)
+
+
+class NewEventWizardActivateView(WizardViewMixin, UpdateView):
+    model = Event
+    template_name = "admin/AKModel/event_wizard/activate.html"
+    form_class = NewEventWizardActivateForm
+    wizard_step = 5
+
+    def get_success_url(self):
+        return reverse_lazy("admin:new_event_wizard_finish", kwargs={"slug": self.object.slug})
+
+
+class NewEventWizardFinishView(WizardViewMixin, DetailView):
+    model = Event
+    template_name = "admin/AKModel/event_wizard/finish.html"
+    wizard_step = 6
diff --git a/AKPlan/locale/de_DE/LC_MESSAGES/django.po b/AKPlan/locale/de_DE/LC_MESSAGES/django.po
index d4db4090..120f98ec 100644
--- a/AKPlan/locale/de_DE/LC_MESSAGES/django.po
+++ b/AKPlan/locale/de_DE/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-04-29 21:09+0000\n"
+"POT-Creation-Date: 2021-04-29 22:48+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
diff --git a/AKPlanning/locale/de_DE/LC_MESSAGES/django.po b/AKPlanning/locale/de_DE/LC_MESSAGES/django.po
index 6830c35c..7338ca7c 100644
--- a/AKPlanning/locale/de_DE/LC_MESSAGES/django.po
+++ b/AKPlanning/locale/de_DE/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-04-29 21:09+0000\n"
+"POT-Creation-Date: 2021-04-29 22:48+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,10 +17,10 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: AKPlanning/settings.py:133
+#: AKPlanning/settings.py:134
 msgid "German"
 msgstr "Deutsch"
 
-#: AKPlanning/settings.py:134
+#: AKPlanning/settings.py:135
 msgid "English"
 msgstr "Englisch"
diff --git a/AKScheduling/locale/de_DE/LC_MESSAGES/django.po b/AKScheduling/locale/de_DE/LC_MESSAGES/django.po
index 2cb2de0e..f25c002d 100644
--- a/AKScheduling/locale/de_DE/LC_MESSAGES/django.po
+++ b/AKScheduling/locale/de_DE/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-04-29 21:09+0000\n"
+"POT-Creation-Date: 2021-04-29 22:48+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
diff --git a/AKSubmission/locale/de_DE/LC_MESSAGES/django.po b/AKSubmission/locale/de_DE/LC_MESSAGES/django.po
index 4ec5841a..103059be 100644
--- a/AKSubmission/locale/de_DE/LC_MESSAGES/django.po
+++ b/AKSubmission/locale/de_DE/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-04-29 21:09+0000\n"
+"POT-Creation-Date: 2021-04-29 22:48+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
diff --git a/locale/de_DE/LC_MESSAGES/django.po b/locale/de_DE/LC_MESSAGES/django.po
index a23d11fd..08f762b7 100644
--- a/locale/de_DE/LC_MESSAGES/django.po
+++ b/locale/de_DE/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-04-29 21:09+0000\n"
+"POT-Creation-Date: 2021-04-29 22:48+0000\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -50,9 +50,6 @@ msgstr "Diese Software ist Open Source"
 #~ msgid "Scheduling for"
 #~ msgstr "Scheduling für"
 
-#~ msgid "Event Status"
-#~ msgstr "Event-Status"
-
 #~ msgid "Day (Horizontal)"
 #~ msgstr "Tag (horizontal)"
 
@@ -68,6 +65,9 @@ msgstr "Diese Software ist Open Source"
 #~ msgid "Room"
 #~ msgstr "Raum"
 
+#~ msgid "Event Status"
+#~ msgstr "Event-Status"
+
 #~ msgid "Unscheduled AK Slots"
 #~ msgstr "Noch nicht geschedulte AK-Slots"
 
-- 
GitLab


From c0b3478ca469a16ecb5a6ecb3716d21d356ec1cd Mon Sep 17 00:00:00 2001
From: "N. Geisler" <ngeisler@fachschaft.informatik.tu-darmstadt.de>
Date: Mon, 3 May 2021 23:17:53 +0200
Subject: [PATCH 3/3] update dependencies

---
 requirements.txt | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/requirements.txt b/requirements.txt
index a7bc7027..176ecddc 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,12 +1,12 @@
-Django==3.1.5
-django-bootstrap4==2.3.1
+Django==3.1.8
+django-bootstrap4==3.0.1
 django-fontawesome-5==1.0.18
 django-split-settings==1.0.1
 django-timezone-field==4.1.2
-djangorestframework==3.12.2
-django-simple-history==2.12.0
+djangorestframework==3.12.4
+django-simple-history==3.0.0
 django-registration-redux==2.9
-django-debug-toolbar==3.2
+django-debug-toolbar==3.2.1
 django-bootstrap-datepicker-plus==3.0.5
 mysqlclient==2.0.3  # for production deployment
 pytz==2021.1
-- 
GitLab