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 kif/akplanning!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.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:
......
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'
......@@ -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"
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.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
......@@ -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"
......
......@@ -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',
......
{% 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 ""
"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 ""
......
......@@ -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"
......
{% 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.
Please register or to comment