Skip to content
Snippets Groups Projects
Commit ef6521fb authored by Nadja Geisler's avatar Nadja Geisler :sunny:
Browse files

Merge branch 'feature-custom-admin' into 'master'

Introduce custom admin

Closes #47

See merge request !39
parents 01ccf980 c45978b8
No related branches found
No related tags found
No related merge requests found
from django.apps import apps
from django.contrib import admin from django.contrib import admin
from django.contrib.admin import SimpleListFilter from django.contrib.admin import SimpleListFilter
from django.db.models import Count, F from django.db.models import Count, F
from django.shortcuts import render from django.shortcuts import render
from django.urls import path, reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from simple_history.admin import SimpleHistoryAdmin from simple_history.admin import SimpleHistoryAdmin
from AKModel.availability.models import Availability from AKModel.availability.models import Availability
from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequirement, AK, AKSlot, Room from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequirement, AK, AKSlot, Room
from AKModel.views import EventStatusView
@admin.register(Event) @admin.register(Event)
class EventAdmin(admin.ModelAdmin): class EventAdmin(admin.ModelAdmin):
model = Event model = Event
list_display = ['name', 'place', 'start', 'end', 'active'] list_display = ['name', 'status_url', 'place', 'start', 'end', 'active']
list_filter = ['active'] list_filter = ['active']
list_editable = ['active'] list_editable = ['active']
ordering = ['-start'] 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): def get_form(self, request, obj=None, change=False, **kwargs):
# Use timezone of event # Use timezone of event
if obj is not None and obj.timezone: if obj is not None and obj.timezone:
...@@ -164,6 +180,18 @@ class AKSlotAdmin(admin.ModelAdmin): ...@@ -164,6 +180,18 @@ class AKSlotAdmin(admin.ModelAdmin):
readonly_fields = ['updated'] 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): def get_form(self, request, obj=None, change=False, **kwargs):
# Use timezone of associated event # Use timezone of associated event
if obj is not None and obj.event.timezone: if obj is not None and obj.event.timezone:
......
from django.apps import AppConfig from django.apps import AppConfig
from django.contrib.admin.apps import AdminConfig
class AkmodelConfig(AppConfig): class AkmodelConfig(AppConfig):
name = 'AKModel' name = 'AKModel'
class AKAdminConfig(AdminConfig):
default_site = 'AKModel.site.AKAdminSite'
...@@ -2,7 +2,7 @@ msgid "" ...@@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -11,19 +11,24 @@ msgstr "" ...@@ -11,19 +11,24 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\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" msgid "Wish"
msgstr "AK-Wunsch" msgstr "AK-Wunsch"
#: AKModel/admin.py:103 #: AKModel/admin.py:119
msgid "Is wish" msgid "Is wish"
msgstr "Ist ein Wunsch" msgstr "Ist ein Wunsch"
#: AKModel/admin.py:104 #: AKModel/admin.py:120
msgid "Is not a wish" msgid "Is not a wish"
msgstr "Ist kein Wunsch" msgstr "Ist kein Wunsch"
#: AKModel/admin.py:131 #: AKModel/admin.py:147
msgid "Export to wiki syntax" msgid "Export to wiki syntax"
msgstr "In Wiki-Syntax exportieren" msgstr "In Wiki-Syntax exportieren"
...@@ -412,7 +417,8 @@ msgstr "Interessenszähler" ...@@ -412,7 +417,8 @@ msgstr "Interessenszähler"
msgid "People who have indicated interest online" msgid "People who have indicated interest online"
msgstr "Anzahl Personen, die online Interesse bekundet haben" 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" msgid "AKs"
msgstr "AKs" msgstr "AKs"
...@@ -444,7 +450,7 @@ msgstr "Eigenschaften" ...@@ -444,7 +450,7 @@ msgstr "Eigenschaften"
msgid "AK requirements fulfilled by the room" msgid "AK requirements fulfilled by the room"
msgstr "AK Anforderungen, die dieser Raum erfüllt" 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" msgid "Rooms"
msgstr "Räume" msgstr "Räume"
...@@ -488,6 +494,10 @@ msgstr "AK Slot" ...@@ -488,6 +494,10 @@ msgstr "AK Slot"
msgid "Not scheduled yet" msgid "Not scheduled yet"
msgstr "Noch nicht geplant" msgstr "Noch nicht geplant"
#: AKModel/site.py:10
msgid "Administration"
msgstr "Verwaltung"
#: AKModel/templates/AKModel/user.html:31 #: AKModel/templates/AKModel/user.html:31
msgid "Hello" msgid "Hello"
msgstr "Hallo" msgstr "Hallo"
...@@ -504,3 +514,50 @@ msgstr "" ...@@ -504,3 +514,50 @@ msgstr ""
#: AKModel/templates/AKModel/user.html:40 #: AKModel/templates/AKModel/user.html:40
msgid "Logout" msgid "Logout"
msgstr "Ausloggen" 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"
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)
{% 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 %}
&middot;
{% 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 %}
&middot;
{% 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 %}
{% 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 %}
from django.contrib import admin
from django.shortcuts import get_object_or_404 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 rest_framework import viewsets, permissions, mixins
from AKModel.models import Event, AK, AKSlot, Room, AKTrack, AKCategory, AKOwner from AKModel.models import Event, AK, AKSlot, Room, AKTrack, AKCategory, AKOwner
...@@ -50,6 +54,22 @@ class FilterByEventSlugMixin(EventSlugMixin): ...@@ -50,6 +54,22 @@ class FilterByEventSlugMixin(EventSlugMixin):
return super().get_queryset().filter(event=self.event) 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): class AKOwnerViewSet(EventSlugMixin, mixins.RetrieveModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet):
permission_classes = (permissions.DjangoModelPermissionsOrAnonReadOnly,) permission_classes = (permissions.DjangoModelPermissionsOrAnonReadOnly,)
serializer_class = AKOwnerSerializer serializer_class = AKOwnerSerializer
...@@ -100,3 +120,16 @@ class AKSlotViewSet(EventSlugMixin, mixins.RetrieveModelMixin, mixins.ListModelM ...@@ -100,3 +120,16 @@ class AKSlotViewSet(EventSlugMixin, mixins.RetrieveModelMixin, mixins.ListModelM
class UserView(TemplateView): class UserView(TemplateView):
template_name = "AKModel/user.html" 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
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
......
...@@ -38,7 +38,7 @@ INSTALLED_APPS = [ ...@@ -38,7 +38,7 @@ INSTALLED_APPS = [
'AKScheduling.apps.AkschedulingConfig', 'AKScheduling.apps.AkschedulingConfig',
'AKPlan.apps.AkplanConfig', 'AKPlan.apps.AkplanConfig',
'AKOnline.apps.AkonlineConfig', 'AKOnline.apps.AkonlineConfig',
'django.contrib.admin', 'AKModel.apps.AKAdminConfig',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
......
{% 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 %}
# 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)
...@@ -283,8 +283,8 @@ msgid "" ...@@ -283,8 +283,8 @@ msgid ""
"System is not yet configured for AK submission and listing. Please try again " "System is not yet configured for AK submission and listing. Please try again "
"later." "later."
msgstr "" msgstr ""
"Das System ist noch nicht fertig eingerichtet. Bitte versuch es später noch " "Das System ist bisher nicht für Eintragen und Anzeige von AKs konfiguriert. "
"einmal." "Bitte versuche es später wieder."
#: AKSubmission/templates/AKSubmission/submission_overview.html:42 #: AKSubmission/templates/AKSubmission/submission_overview.html:42
msgid "" msgid ""
......
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -34,6 +34,18 @@ msgstr "Virtueller Raum" ...@@ -34,6 +34,18 @@ msgstr "Virtueller Raum"
msgid "Virtual Rooms" msgid "Virtual Rooms"
msgstr "Virtuelle Räume" 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 #: templates/base.html:78
msgid "Impress" msgid "Impress"
msgstr "Impressum" msgstr "Impressum"
......
{% 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 %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment