diff --git a/backend/grants/admin.py b/backend/grants/admin.py index d878bfc4f4..999791e2ee 100644 --- a/backend/grants/admin.py +++ b/backend/grants/admin.py @@ -5,7 +5,8 @@ from typing import Dict, List, Optional from countries.filters import CountryFilter from django.urls import path -from .views import grant_summary_view +from django.template.response import TemplateResponse +from django.db.models import Count from django import forms from django.contrib import admin, messages @@ -504,7 +505,7 @@ def is_speaker(self, obj): def get_queryset(self, request): qs = super().get_queryset(request) if not self.speaker_ids: - conference_id = request.GET.get("conference__id__exact") + conference_id = request.GET.get("conference__id") self.speaker_ids = ScheduleItem.objects.filter( conference__id=conference_id, submission__speaker_id__isnull=False, @@ -556,10 +557,98 @@ def save_form(self, request, form, change): def get_urls(self): urls = super().get_urls() custom_urls = [ - path("grant-summary/", grant_summary_view, name="grant-summary"), + path( + "stats/", + self.admin_site.admin_view(self.stats_view), + name="grant_stats", + ), ] return custom_urls + urls + def stats_view(self, request): + # Initialize statuses + statuses = [status for status in Grant.Status.choices] + + # Filter the grants from the request + filter_params = request.GET.dict() + grants = Grant.objects.filter(**filter_params) + + # Fetch grant data + grants_data = grants.values("travelling_from", "status").annotate( + total=Count("id") + ) + + # Aggregate data + summary = {} + totals_by_status = {status[0]: 0 for status in statuses} + for data in grants_data: + country = countries.get(code=data["travelling_from"]) + continent = country.continent.name if country else "Unknown" + country_name = f"{country.name} {country.emoji}" if country else "Unknown" + country_code = country.code if country else "Unknown" + + key = (continent, country_name, country_code) + + if key not in summary: + summary[key] = {status[0]: 0 for status in statuses} + summary[key][data["status"]] += data["total"] + totals_by_status[data["status"]] += data["total"] + + # Sort by continent and country code + sorted_keys = sorted(summary.keys(), key=lambda x: (x[0], x[2])) + + # Prepare data for the template + template_data = [] + for key in sorted_keys: + continent, country_name, _ = key + counts = summary[key] + row = { + "continent": continent, + "country": country_name, + "counts": [counts.get(status[0], 0) for status in statuses], + } + template_data.append(row) + + # Mapping of raw filter keys to user-friendly names + filter_mapping = { + "conference__id": "Conference ID", + "status": "Status", + "country_type": "Country Type", + "occupation": "Occupation", + "grant_type": "Grant Type", + "travelling_from": "Country", + } + + # Function to strip filter suffixes and map to user-friendly names + def map_filter_key(key): + for suffix in [ + "__exact", + "__in", + "__gt", + "__lt", + "__contains", + "__startswith", + ]: + if key.endswith(suffix): + key = key[: -len(suffix)] # Strip the suffix + break + return filter_mapping.get(key, key) + + # Format filters for display + human_readable_filters = { + map_filter_key(key): value for key, value in filter_params.items() + } + + context = dict( + self.admin_site.each_context(request), + template_data=template_data, + statuses=statuses, + total=grants.count(), + totals_by_status=totals_by_status, + filters=human_readable_filters, + ) + return TemplateResponse(request, "admin/grants/grant_summary.html", context) + class Media: js = ["admin/js/jquery.init.js"] diff --git a/backend/grants/templates/admin/grants/grant/change_list.html b/backend/grants/templates/admin/grants/grant/change_list.html index fe2276793c..75650ceb39 100644 --- a/backend/grants/templates/admin/grants/grant/change_list.html +++ b/backend/grants/templates/admin/grants/grant/change_list.html @@ -1,7 +1,7 @@ {% extends "admin/change_list.html" %} {% block object-tools-items %}
  • - Grant Summary + Grant Summary
  • {{ block.super }} {% endblock %} diff --git a/backend/grants/templates/admin/grants/grant_summary.html b/backend/grants/templates/admin/grants/grant_summary.html index 877b03eec6..f0ec6bd12e 100644 --- a/backend/grants/templates/admin/grants/grant_summary.html +++ b/backend/grants/templates/admin/grants/grant_summary.html @@ -2,54 +2,102 @@ {% load markdownify %} {% load localize countryname get_item %} {% load i18n admin_urls static admin_list %} +{% block extrastyle %} + {{ block.super }} + + + +{% endblock %} +{% block content %} +
    + Back to List +
    +

    Grant Summary

    + +
    +

    Active Filters:

    + +
    +
    +

    Toggle Status Columns

    +
    + {% for status in statuses %} + + {% endfor %} +
    +
    + + + + + + {% for status in statuses %}{% endfor %} + + + + {% for row in template_data %} + + + + {% for count in row.counts %}{% endfor %} + + {% endfor %} + + + + + {% for count in totals_by_status.values %}{% endfor %} + + +
    ContinentCountry{{ status.1|title }}
    {{ row.continent }}{{ row.country }}{{ count }}
    Total Grants: {{ total }}{{ count }}
    + + {% endblock %} diff --git a/backend/grants/urls.py b/backend/grants/urls.py deleted file mode 100644 index d484fb902c..0000000000 --- a/backend/grants/urls.py +++ /dev/null @@ -1,6 +0,0 @@ -from .views import grant_summary_view -from django.urls import path - -urlpatterns = [ - path("grant-summary/", grant_summary_view, name="grant-summary"), -] diff --git a/backend/grants/views.py b/backend/grants/views.py deleted file mode 100644 index 9f4c639368..0000000000 --- a/backend/grants/views.py +++ /dev/null @@ -1,23 +0,0 @@ -from django.shortcuts import render -from .models import Grant -from collections import defaultdict - -# Mapping countries to continents -country_to_continent = { - "italy": "Europe", - # ... add other countries and their respective continents -} - - -def grant_summary_view(request): - grants = Grant.objects.all() - - # Initialize a dictionary to hold the counts - summary = defaultdict(lambda: defaultdict(int)) - - for grant in grants: - continent = country_to_continent.get(grant.travelling_from, "") - summary[continent][grant.status] += 1 - - context = {"summary": dict(summary)} - return render(request, "admin/grants/grant_summary.html", context) diff --git a/backend/pycon/urls.py b/backend/pycon/urls.py index 949738289b..2d3c881ca4 100644 --- a/backend/pycon/urls.py +++ b/backend/pycon/urls.py @@ -20,7 +20,6 @@ path("admin/", admin.site.urls), path("graphql", csrf_exempt(GraphQLView.as_view(schema=schema)), name="graphql"), path("user/", include("users.urls")), - path("grants/", include("grants.urls")), path("cms-admin/", include(wagtailadmin_urls)), path("cms-documents/", include(wagtaildocs_urls)), path("", include("association_membership.urls")),