diff --git a/AKModel/admin.py b/AKModel/admin.py index 219246236c6383161475fac2239cd182c47963b3..ad55accba8067851b09767b1dfc726ed9962f1bf 100644 --- a/AKModel/admin.py +++ b/AKModel/admin.py @@ -1,23 +1,39 @@ +from django.apps import apps from django.contrib import admin from django.contrib.admin import SimpleListFilter from django.db.models import Count, F from django.shortcuts import render +from django.urls import path, reverse_lazy from django.utils import timezone +from django.utils.html import format_html from django.utils.translation import gettext_lazy as _ from simple_history.admin import SimpleHistoryAdmin from AKModel.availability.models import Availability from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequirement, AK, AKSlot, Room +from AKModel.views import EventStatusView @admin.register(Event) class EventAdmin(admin.ModelAdmin): model = Event - list_display = ['name', 'place', 'start', 'end', 'active'] + list_display = ['name', 'status_url', 'place', 'start', 'end', 'active'] list_filter = ['active'] list_editable = ['active'] ordering = ['-start'] + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path('<slug:slug>/status/', self.admin_site.admin_view(EventStatusView.as_view()), name="event_status") + ] + return custom_urls + urls + + def status_url(self, obj): + return format_html("<a href='{url}'>{text}</a>", + url=reverse_lazy('admin:event_status', kwargs={'slug': obj.slug}), text=_("Status")) + status_url.short_description = text=_("Status") + def get_form(self, request, obj=None, change=False, **kwargs): # Use timezone of event if obj is not None and obj.timezone: @@ -164,6 +180,18 @@ class AKSlotAdmin(admin.ModelAdmin): readonly_fields = ['updated'] + def get_urls(self): + urls = super().get_urls() + custom_urls = [] + if apps.is_installed("AKScheduling"): + from AKScheduling.views import UnscheduledSlotsAdminView + + custom_urls.extend([ + path('<slug:event_slug>/unscheduled/', self.admin_site.admin_view(UnscheduledSlotsAdminView.as_view()), + name="slots_unscheduled") + ]) + return custom_urls + urls + def get_form(self, request, obj=None, change=False, **kwargs): # Use timezone of associated event if obj is not None and obj.event.timezone: diff --git a/AKModel/apps.py b/AKModel/apps.py index c7d4c432e622c998f8f8f9d370b2e556eb158c3d..5af2af0d5ddb4c45cf30635743e869d3c32a62f1 100644 --- a/AKModel/apps.py +++ b/AKModel/apps.py @@ -1,5 +1,10 @@ from django.apps import AppConfig +from django.contrib.admin.apps import AdminConfig class AkmodelConfig(AppConfig): name = 'AKModel' + + +class AKAdminConfig(AdminConfig): + default_site = 'AKModel.site.AKAdminSite' diff --git a/AKModel/locale/de_DE/LC_MESSAGES/django.po b/AKModel/locale/de_DE/LC_MESSAGES/django.po index a3b9c6b794bce34dc13cb60072acc9e0016e6bef..8035108806cb181f600b2409e63e33825e1abbbd 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: 2020-05-17 21:09+0000\n" +"POT-Creation-Date: 2020-05-19 06:38+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,19 +11,24 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: AKModel/admin.py:97 +#: AKModel/admin.py:34 AKModel/admin.py:35 +#: AKModel/templates/admin/AKModel/status.html:7 +msgid "Status" +msgstr "Status" + +#: AKModel/admin.py:113 msgid "Wish" msgstr "AK-Wunsch" -#: AKModel/admin.py:103 +#: AKModel/admin.py:119 msgid "Is wish" msgstr "Ist ein Wunsch" -#: AKModel/admin.py:104 +#: AKModel/admin.py:120 msgid "Is not a wish" msgstr "Ist kein Wunsch" -#: AKModel/admin.py:131 +#: AKModel/admin.py:147 msgid "Export to wiki syntax" msgstr "In Wiki-Syntax exportieren" @@ -412,7 +417,8 @@ msgstr "Interessenszähler" msgid "People who have indicated interest online" msgstr "Anzahl Personen, die online Interesse bekundet haben" -#: AKModel/models.py:231 +#: AKModel/models.py:231 AKModel/templates/admin/AKModel/status.html:47 +#: AKModel/templates/admin/AKModel/status.html:54 msgid "AKs" msgstr "AKs" @@ -444,7 +450,7 @@ msgstr "Eigenschaften" msgid "AK requirements fulfilled by the room" msgstr "AK Anforderungen, die dieser Raum erfüllt" -#: AKModel/models.py:271 +#: AKModel/models.py:271 AKModel/templates/admin/AKModel/status.html:31 msgid "Rooms" msgstr "Räume" @@ -488,6 +494,10 @@ msgstr "AK Slot" msgid "Not scheduled yet" msgstr "Noch nicht geplant" +#: AKModel/site.py:10 +msgid "Administration" +msgstr "Verwaltung" + #: AKModel/templates/AKModel/user.html:31 msgid "Hello" msgstr "Hallo" @@ -504,3 +514,50 @@ msgstr "" #: AKModel/templates/AKModel/user.html:40 msgid "Logout" msgstr "Ausloggen" + +#: AKModel/templates/admin/AKModel/status.html:14 +#, fuzzy +#| msgid "AK Categories" +msgid "Categories" +msgstr "AK Kategorien" + +#: AKModel/templates/admin/AKModel/status.html:16 +#, fuzzy +#| msgid "No categories yet" +msgid "No categroies yet" +msgstr "Bisher keine Kategorien" + +#: AKModel/templates/admin/AKModel/status.html:29 +msgid "Add category" +msgstr "Kategorie hinzufügen" + +#: AKModel/templates/admin/AKModel/status.html:33 +msgid "No rooms yet" +msgstr "Bisher keine Räume" + +#: AKModel/templates/admin/AKModel/status.html:45 +msgid "Add Room" +msgstr "Raum hinzufügen" + +#: AKModel/templates/admin/AKModel/status.html:49 +msgid "No AKs yet" +msgstr "Bisher keine AKs" + +#: AKModel/templates/admin/AKModel/status.html:57 +msgid "Slots" +msgstr "Slots" + +#: AKModel/templates/admin/AKModel/status.html:60 +msgid "Unscheduled Slots" +msgstr "Ungeplante Slots" + +#: AKModel/templates/admin/ak_index.html:7 +msgid "Active Events" +msgstr "Aktive Events" + +#: AKModel/views.py:129 +msgid "Event Status" +msgstr "Eventstatus" + +#~ msgid "Notes to organizers" +#~ msgstr "Notizen an die Organisator*innen" diff --git a/AKModel/site.py b/AKModel/site.py new file mode 100644 index 0000000000000000000000000000000000000000..480689ef0d780732e59a560d0959e4ae03469680 --- /dev/null +++ b/AKModel/site.py @@ -0,0 +1,25 @@ +from django.contrib.admin import AdminSite +from django.utils.translation import gettext_lazy as _ + +from AKModel.models import Event + + +class AKAdminSite(AdminSite): + index_template = "admin/ak_index.html" + site_header = f"AKPlanning - {_('Administration')}" + index_title = _('Administration') + + def get_urls(self): + from django.urls import path + + urls = super().get_urls() + urls += [ + # path('...', self.admin_view(...)), + ] + return urls + + def index(self, request, extra_context=None): + if extra_context is None: + extra_context = {} + extra_context["active_events"] = Event.objects.filter(active=True) + return super().index(request, extra_context) diff --git a/AKModel/templates/admin/AKModel/status.html b/AKModel/templates/admin/AKModel/status.html new file mode 100644 index 0000000000000000000000000000000000000000..043eb85983776be171ad21db88ddcfc2ed2a7883 --- /dev/null +++ b/AKModel/templates/admin/AKModel/status.html @@ -0,0 +1,75 @@ +{% extends "admin_base.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load tz %} + +{% block title %}{% trans "Status" %}: {{event}}{% endblock %} + +{% block content %} + <h2><a href="{% url 'admin:AKModel_event_change' event.pk %}">{{event}}</a></h2> + {% timezone event.timezone %} + <h5>{{ event.start }} - {{ event.end }}</h5> + + <h3 class="block-header">{% trans "Categories" %}</h3> + {% if event.akcategory_set.count == 0 %} + <p class="text-danger">{% trans "No categroies yet" %}</p> + {% else %} + <p> + {{ event.akcategory_set.count }}: + {% for category in event.akcategory_set.all %} + {% if forloop.counter0 > 0 %} + · + {% endif %} + <a href="{% url 'admin:AKModel_akcategory_change' category.pk %}">{{ category }}</a> + ({{ category.ak_set.count }}) + {% endfor %} + </p> + {% endif %} + <a class="btn btn-success" href="{% url 'admin:AKModel_akcategory_add' %}">{% trans "Add category" %}</a> + + <h3 class="block-header">{% trans "Rooms" %}</h3> + {% if event.room_set.count == 0 %} + <p class="text-danger">{% trans "No rooms yet" %}</p> + {% else %} + <p> + {{ event.room_set.count }}: + {% for room in event.room_set.all %} + {% if forloop.counter0 > 0 %} + · + {% endif %} + <a href="{% url 'admin:AKModel_room_change' room.pk %}">{{ room }}</a> + {% endfor %} + </p> + {% endif %} + <a class="btn btn-success" href="{% url 'admin:AKModel_room_add' %}">{% trans "Add Room" %}</a> + + <h3 class="block-header">{% trans "AKs" %}</h3> + {% if event.ak_set.count == 0 %} + <p class="text-danger">{% trans "No AKs yet" %}</p> + {% else %} + <table> + <tbody> + <tr> + <td>{% trans "AKs" %}</td><td>{{ event.ak_set.count }}</td> + </tr> + <tr> + <td>{% trans "Slots" %}</td><td>{{ event.akslot_set.count }}</td> + </tr> + <tr> + <td>{% trans "Unscheduled Slots" %}</td><td> + {% if "AKScheduling"|check_app_installed %} + <a href="{% url 'admin:slots_unscheduled' event_slug=event.slug %}"> + {{ unscheduled_slots_count }} + </a> + {% else %} + {{ unscheduled_slots_count }} + {% endif %} + </td> + </tr> + </tbody> + </table> + {% endif %} + + {% endtimezone %} +{% endblock %} diff --git a/AKModel/templates/admin/ak_index.html b/AKModel/templates/admin/ak_index.html new file mode 100644 index 0000000000000000000000000000000000000000..1aea83f3135db5c3a5185ac4136264663b2d2055 --- /dev/null +++ b/AKModel/templates/admin/ak_index.html @@ -0,0 +1,20 @@ +{% extends "admin/index.html" %} + +{% load i18n tz %} + +{% block content %} + <div style="margin-bottom: 20px;"> + <h2>{% trans "Active Events" %}:</h2> + + <ul> + {% for event in active_events %} + <li> + <a href="{% url 'admin:event_status' slug=event.slug %}">{{ event }}</a> + ({{ event.start|timezone:event.timezone|date:"d.m.y"}} - + {{ event.end|timezone:event.timezone|date:"d.m.y"}}) + </li> + {% endfor %} + </ul> + </div> +{{ block.super }} +{% endblock %} diff --git a/AKModel/views.py b/AKModel/views.py index 1d6b95eb26e2d456f76e6c55f8fd6c759cedb5a9..323954682b5111d71b0667ffd47909342cd6c821 100644 --- a/AKModel/views.py +++ b/AKModel/views.py @@ -1,5 +1,9 @@ +from django.contrib import admin from django.shortcuts import get_object_or_404 -from django.views.generic import TemplateView +from django.urls import reverse_lazy +from django.utils.translation import gettext_lazy as _ + +from django.views.generic import TemplateView, DetailView from rest_framework import viewsets, permissions, mixins from AKModel.models import Event, AK, AKSlot, Room, AKTrack, AKCategory, AKOwner @@ -50,6 +54,22 @@ class FilterByEventSlugMixin(EventSlugMixin): return super().get_queryset().filter(event=self.event) +class AdminViewMixin: + site_url = '' + title = '' + + def get_context_data(self, **kwargs): + extra = admin.site.each_context(self.request) + extra.update(super().get_context_data(**kwargs)) + + if self.site_url != '': + extra["site_url"] = self.site_url + if self.title != '': + extra["title"] = self.title + + return extra + + class AKOwnerViewSet(EventSlugMixin, mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): permission_classes = (permissions.DjangoModelPermissionsOrAnonReadOnly,) serializer_class = AKOwnerSerializer @@ -100,3 +120,16 @@ class AKSlotViewSet(EventSlugMixin, mixins.RetrieveModelMixin, mixins.ListModelM class UserView(TemplateView): template_name = "AKModel/user.html" + + +class EventStatusView(AdminViewMixin, DetailView): + template_name = "admin/AKModel/status.html" + model = Event + context_object_name = "event" + title = _("Event Status") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["unscheduled_slots_count"] = context["event"].akslot_set.filter(start=None).count + context["site_url"] = reverse_lazy("dashboard:dashboard_event", kwargs={'slug': context["event"].slug}) + return context diff --git a/AKPlanning/locale/de_DE/LC_MESSAGES/django.po b/AKPlanning/locale/de_DE/LC_MESSAGES/django.po index c0edc69e3d52780b8e1b8e692c061197f477fd5a..6f94ccd2d6a30a0eda5f7ec35e8accb86bf59ef3 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: 2020-05-16 20:26+0000\n" +"POT-Creation-Date: 2020-05-19 06:38+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/settings.py b/AKPlanning/settings.py index 957b13043413537a9029fcf2f621663c671615da..04302f411194498fbb5e327bec60cff6f6004915 100644 --- a/AKPlanning/settings.py +++ b/AKPlanning/settings.py @@ -38,7 +38,7 @@ INSTALLED_APPS = [ 'AKScheduling.apps.AkschedulingConfig', 'AKPlan.apps.AkplanConfig', 'AKOnline.apps.AkonlineConfig', - 'django.contrib.admin', + 'AKModel.apps.AKAdminConfig', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', diff --git a/AKScheduling/templates/admin/AKScheduling/unscheduled.html b/AKScheduling/templates/admin/AKScheduling/unscheduled.html new file mode 100644 index 0000000000000000000000000000000000000000..2fc9eb71ae23b5934d04f070cd5cc7cafcb3fb57 --- /dev/null +++ b/AKScheduling/templates/admin/AKScheduling/unscheduled.html @@ -0,0 +1,35 @@ +{% extends "admin_base.html" %} +{% load tags_AKModel %} + +{% load i18n %} +{% load tz %} + +{% block title %}{% trans "Unscheduled AK Slots" %}: {{event}}{% endblock %} + +{% block content %} + + <h3>{% trans "Count" %}: {{ akslots.count }}</h3> + + {% regroup akslots by ak as unscheduled_by_ak %} + + <ul> + {% for group in unscheduled_by_ak %} + <li> + {% with group.grouper as ak %} + {% if "AKSubmission"|check_app_installed %} + <a href="{% url 'submit:ak_detail' event_slug=ak.event.slug pk=ak.pk %}">{{ ak }}</a> + {% else %} + {{ ak }} + {% endif %} + {% endwith %} + <ul> + {% for slot in group.list %} + <li><a href="{% url 'admin:AKModel_akslot_change' slot.pk %}">{{ slot.duration }}</a></li> + {% endfor %} + </ul> + </li> + {% endfor %} + </ul> + + <a href="{% url 'admin:event_status' event.slug %}">{% trans "Event Status" %}</a> +{% endblock %} diff --git a/AKScheduling/views.py b/AKScheduling/views.py index 60f00ef0ef347811e7b0c0921b7fda097acd9fcc..88523e54dad18c2083450b71c868f8417c92b6e1 100644 --- a/AKScheduling/views.py +++ b/AKScheduling/views.py @@ -1 +1,20 @@ -# Create your views here. +from django.urls import reverse_lazy +from django.views.generic import ListView +from django.utils.translation import gettext_lazy as _ + +from AKModel.models import AKSlot +from AKModel.views import AdminViewMixin, FilterByEventSlugMixin + + +class UnscheduledSlotsAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView): + template_name = "admin/AKScheduling/unscheduled.html" + model = AKSlot + context_object_name = "akslots" + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["title"] = f"{_('Unscheduled AK Slots')} for {context['event']}" + return context + + def get_queryset(self): + return super().get_queryset().filter(start=None) diff --git a/AKSubmission/locale/de_DE/LC_MESSAGES/django.po b/AKSubmission/locale/de_DE/LC_MESSAGES/django.po index 61c3236dde9c983a5557d19332ab9da5e890c165..11605aa38db2e4685af2dce64d3b92073f9b38da 100644 --- a/AKSubmission/locale/de_DE/LC_MESSAGES/django.po +++ b/AKSubmission/locale/de_DE/LC_MESSAGES/django.po @@ -283,8 +283,8 @@ msgid "" "System is not yet configured for AK submission and listing. Please try again " "later." msgstr "" -"Das System ist noch nicht fertig eingerichtet. Bitte versuch es später noch " -"einmal." +"Das System ist bisher nicht für Eintragen und Anzeige von AKs konfiguriert. " +"Bitte versuche es später wieder." #: AKSubmission/templates/AKSubmission/submission_overview.html:42 msgid "" diff --git a/locale/de_DE/LC_MESSAGES/django.po b/locale/de_DE/LC_MESSAGES/django.po index cff6314d53a95eeeaa80c95f6de6f0dfea58dc0d..821d4c31f3704716f9163d208e8c73e8b7b52c06 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: 2020-05-17 21:09+0000\n" +"POT-Creation-Date: 2020-05-19 06:38+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" @@ -34,6 +34,18 @@ msgstr "Virtueller Raum" msgid "Virtual Rooms" msgstr "Virtuelle Räume" +#: AKScheduling/templates/admin/AKScheduling/unscheduled.html:7 +msgid "Unscheduled AK Slots" +msgstr "Noch nicht geschedulte AK-Slots" + +#: AKScheduling/templates/admin/AKScheduling/unscheduled.html:11 +msgid "Count" +msgstr "Anzahl" + +#: AKScheduling/templates/admin/AKScheduling/unscheduled.html:34 +msgid "Event Status" +msgstr "Event-Status" + #: templates/base.html:78 msgid "Impress" msgstr "Impressum" diff --git a/templates/admin_base.html b/templates/admin_base.html new file mode 100644 index 0000000000000000000000000000000000000000..cecf50f3694855670212591a35db6791be08c152 --- /dev/null +++ b/templates/admin_base.html @@ -0,0 +1,20 @@ +{% extends "admin/base_site.html" %} + +{% load bootstrap4 %} +{% load fontawesome_5 %} + +{% block extrahead %} + {% bootstrap_css %} + {% bootstrap_javascript jquery='slim' %} + {% fontawesome_5_static %} + + <style> + a.btn { + color: #FFFFFF; + } + + .block-header { + margin-top: 20px; + } + </style> +{% endblock %}