Skip to content

Commit

Permalink
Improve grants recap
Browse files Browse the repository at this point in the history
  • Loading branch information
estyxx committed Dec 29, 2023
1 parent eb3619b commit 8ae2a9a
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 80 deletions.
95 changes: 92 additions & 3 deletions backend/grants/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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"]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
<li>
<a href="{% url 'grant-summary' %}" class="button">Grant Summary</a>
<a href="{% url 'admin:grant_stats' %}?{{ request.GET.urlencode }}" class="button">Grant Summary</a>
</li>
{{ block.super }}
{% endblock %}
140 changes: 94 additions & 46 deletions backend/grants/templates/admin/grants/grant_summary.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,102 @@
{% load markdownify %}
{% load localize countryname get_item %}
{% load i18n admin_urls static admin_list %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" href="{% static "admin/css/changelists.css" %}">
<style>
.back-button {
margin-bottom: 20px;
}

{% block content %}

<h1>Grant Summary</h1>

<div>
<label><input type="checkbox" class="toggle-column" data-column="2" checked> Pending</label>
<label><input type="checkbox" class="toggle-column" data-column="3" checked> Approved</label>
<label><input type="checkbox" class="toggle-column" data-column="4" checked> Waiting List</label>
<label><input type="checkbox" class="toggle-column" data-column="5" checked> Waiting List, Maybe</label>
<label><input type="checkbox" class="toggle-column" data-column="6" checked> Waiting for confirmation</label>
<label><input type="checkbox" class="toggle-column" data-column="7" checked> Confirmed</label>
<label><input type="checkbox" class="toggle-column" data-column="8" checked> Refused</label>
<label><input type="checkbox" class="toggle-column" data-column="9" checked> Rejected</label>
</div>
.back-button a.button {
padding: 6px 12px;
}

<table id="grantSummaryTable">
<thead>
<tr>
<th>Continent</th>
<th>Pending</th>
<th>Approved</th>
<th>Waiting List</th>
<th>Waiting List, Maybe</th>
<th>Waiting for confirmation</th>
<th>Confirmed</th>
<th>Refused</th>
<th>Rejected</th>
</tr>
</thead>
<tbody>
{% for continent, statuses in summary.items %}
<tr>
<td>{{ continent }}</td>
<td>{{ statuses.pending }}</td>
<td>{{ statuses.approved }}</td>
<td>{{ statuses.waiting_list }}</td>
<td>{{ statuses.waiting_list_maybe }}</td>
<td>{{ statuses.waiting_for_confirmation }}</td>
<td>{{ statuses.confirmed }}</td>
<td>{{ statuses.refused }}</td>
<td>{{ statuses.rejected }}</td>
</tr>
{% endfor %}
</tbody>
</table>
.back-button a.button:hover {
background-color: #e6e6e6;
}

<script>
</style>
<style>
.status-filter {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #ddd;
background-color: #f9f9f9;
}
.status-filter h3 {
margin-top: 0;
}
.checkbox-list {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.status-checkbox {
margin-right: 20px;
margin-bottom: 10px;
}
.status-checkbox input {
margin-right: 5px;
}
</style>
{% endblock %}
{% block content %}
<div class="back-button">
<a href="{% url 'admin:grants_grant_changelist' %}" class="button">Back to List</a>
</div>
<h1>Grant Summary</h1>
<!-- Display Active Filters -->
<div class="active-filters">
<h3>Active Filters:</h3>
<ul>
{% for filter_name, value in filters.items %}
<li>{{ filter_name }}: {{ value }}</li>
{% empty %}
<li>No filters applied</li>
{% endfor %}
</ul>
</div>
<div class="status-filter">
<h3>Toggle Status Columns</h3>
<div class="checkbox-list">
{% for status in statuses %}
<label class="status-checkbox">
<input type="checkbox"
class="toggle-column"
data-column="{{ forloop.counter|add:'2' }}"
checked>
{{ status.1|title }}
</label>
{% endfor %}
</div>
</div>
<table id="grantSummaryTable">
<thead>
<tr>
<th>Continent</th>
<th>Country</th>
{% for status in statuses %}<th>{{ status.1|title }}</th>{% endfor %}
</tr>
</thead>
<tbody>
{% for row in template_data %}
<tr>
<td>{{ row.continent }}</td>
<td>{{ row.country }}</td>
{% for count in row.counts %}<td>{{ count }}</td>{% endfor %}
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="2 }}">Total Grants: {{ total }}</td>
{% for count in totals_by_status.values %}<td>{{ count }}</td>{% endfor %}
</tr>
</tfoot>
</table>
<script>
document.addEventListener('DOMContentLoaded', function () {
var checkboxes = document.querySelectorAll('.toggle-column');
checkboxes.forEach(function(checkbox) {
Expand All @@ -61,5 +109,5 @@ <h1>Grant Summary</h1>
});
});
});
</script>
</script>
{% endblock %}
6 changes: 0 additions & 6 deletions backend/grants/urls.py

This file was deleted.

23 changes: 0 additions & 23 deletions backend/grants/views.py

This file was deleted.

1 change: 0 additions & 1 deletion backend/pycon/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")),
Expand Down

0 comments on commit 8ae2a9a

Please sign in to comment.