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
Branches
No related tags found
1 merge request!39Introduce custom admin
Pipeline #4032 passed
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 to comment