diff --git a/AKDashboard/admin.py b/AKDashboard/admin.py index 724ac31cb275d61c92e01cb3b4c0d65e3af288ea..ffc7b41c68ac7a013606f4388dc867fbd47ee4f9 100644 --- a/AKDashboard/admin.py +++ b/AKDashboard/admin.py @@ -4,6 +4,9 @@ from AKDashboard.models import DashboardButton @admin.register(DashboardButton) class DashboardButtonAdmin(admin.ModelAdmin): + """ + Admin interface for dashboard buttons + """ list_display = ['text', 'url', 'event'] list_filter = ['event'] search_fields = ['text', 'url'] diff --git a/AKDashboard/apps.py b/AKDashboard/apps.py index ad0d431686f975c681fa583b0fcd1438653c2af0..c71dc24053822f11dd86ad7a1b4a608a8c6a417a 100644 --- a/AKDashboard/apps.py +++ b/AKDashboard/apps.py @@ -2,4 +2,7 @@ from django.apps import AppConfig class AkdashboardConfig(AppConfig): + """ + App configuration for dashboard (default) + """ name = 'AKDashboard' diff --git a/AKDashboard/models.py b/AKDashboard/models.py index 130a8e37889624047d4bda8f945b4bdbf8455506..6691e962ec149aac76667cd8ada71bc16738df68 100644 --- a/AKDashboard/models.py +++ b/AKDashboard/models.py @@ -6,6 +6,17 @@ from AKModel.models import Event class DashboardButton(models.Model): + """ + Model for a single dashboard button + + Allows to specify + * a text (currently without possibility to translate), + * a color (based on predefined design colors) + * a url the button should point to (internal or external) + * an icon (from the collection of fontawesome) + + Each button is associated with a single event and will be deleted when the event is deleted. + """ class Meta: verbose_name = _("Dashboard Button") verbose_name_plural = _("Dashboard Buttons") diff --git a/AKDashboard/views.py b/AKDashboard/views.py index 88e3443e3f4c4378f52ff63b002186176d1fe546..601edda274cf0a5d893571d6307d001c49affb63 100644 --- a/AKDashboard/views.py +++ b/AKDashboard/views.py @@ -1,5 +1,4 @@ from django.apps import apps -from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.views.decorators.csrf import ensure_csrf_cookie from django.views.generic import TemplateView, DetailView @@ -10,6 +9,11 @@ from AKPlanning import settings class DashboardView(TemplateView): + """ + Index view of dashboard and therefore the main entry point for AKPlanning + + Displays information and buttons for all public events + """ template_name = 'AKDashboard/dashboard.html' @method_decorator(ensure_csrf_cookie) @@ -23,6 +27,14 @@ class DashboardView(TemplateView): class DashboardEventView(DetailView): + """ + Dashboard view for a single event + + In addition to the basic information and the buttons, + an overview over recent events (new and changed AKs, moved AKSlots) for the given event is shown. + + The event dashboard also exists for non-public events (one only needs to know the URL/slug of the event). + """ template_name = 'AKDashboard/dashboard_event.html' context_object_name = 'event' model = Event @@ -32,11 +44,16 @@ class DashboardEventView(DetailView): # Show feed of recent changes (if activated) if settings.DASHBOARD_SHOW_RECENT: + # Create a list of chronically sorted events (both AK and plan changes): recent_changes = [] - # Newest AKs + # Newest AKs (if AKSubmission is used) if apps.is_installed("AKSubmission"): - submission_changes = AK.history.filter(event=context['event'])[:int(settings.DASHBOARD_RECENT_MAX)] + # Get the latest x changes (if there are that many), + # where x corresponds to the entry threshold configured in the settings + # (such that the list will be completely filled even if there are no (newer) plan changes) + submission_changes = AK.history.filter(event=context['event'])[:int(settings.DASHBOARD_RECENT_MAX)] # pylint: disable=no-member, line-too-long + # Create textual representation including icons for s in submission_changes: if s.history_type == '+': text = _('New AK: %(ak)s.') % {'ak': s.name} @@ -48,18 +65,21 @@ class DashboardEventView(DetailView): text = _('AK "%(ak)s" deleted.') % {'ak': s.name} icon = ('times', 'fas') - recent_changes.append({'icon': icon, 'text': text, 'link': s.instance.detail_url, 'timestamp': s.history_date}) - - # Changes in plan - if apps.is_installed("AKPlan"): - if not context['event'].plan_hidden: - last_changed_slots = AKSlot.objects.select_related('ak').filter(event=context['event'], start__isnull=False).order_by('-updated')[ - :int(settings.DASHBOARD_RECENT_MAX)] - for changed_slot in last_changed_slots: - recent_changes.append({'icon': ('clock', 'far'), - 'text': _('AK "%(ak)s" (re-)scheduled.') % {'ak': changed_slot.ak.name}, - 'link': changed_slot.ak.detail_url, - 'timestamp': changed_slot.updated}) + # Store representation in change list (still unsorted) + recent_changes.append( + {'icon': icon, 'text': text, 'link': s.instance.detail_url, 'timestamp': s.history_date} + ) + + # Changes in plan (if AKPlan is used and plan is publicly visible) + if apps.is_installed("AKPlan") and not context['event'].plan_hidden: + # Get the latest plan changes (again using a threshold, see above) + last_changed_slots = AKSlot.objects.select_related('ak').filter(event=context['event'], start__isnull=False).order_by('-updated')[:int(settings.DASHBOARD_RECENT_MAX)] #pylint: disable=line-too-long + for changed_slot in last_changed_slots: + # Create textual representation including icons and links and store in list (still unsorted) + recent_changes.append({'icon': ('clock', 'far'), + 'text': _('AK "%(ak)s" (re-)scheduled.') % {'ak': changed_slot.ak.name}, + 'link': changed_slot.ak.detail_url, + 'timestamp': changed_slot.updated}) # Sort by change date... recent_changes.sort(key=lambda x: x['timestamp'], reverse=True)