From c1ab295514ae56c97ad55c4ed2a8ec67b2eb6729 Mon Sep 17 00:00:00 2001 From: Gokuldevx Date: Tue, 26 Aug 2025 15:20:44 +0530 Subject: [PATCH] minor update --- .../core/__pycache__/urls.cpython-313.pyc | Bin 2402 -> 2490 bytes .../core/__pycache__/views.cpython-313.pyc | Bin 19477 -> 21268 bytes .../templates/core/superadmin_reports.html | 91 ++++++++++++++++++ .../dashboard/superadmin_dashboard.html | 2 +- civicfix/core/templatetags/__init__.py | 0 .../__pycache__/__init__.cpython-313.pyc | Bin 0 -> 151 bytes .../custom_filters.cpython-313.pyc | Bin 0 -> 669 bytes civicfix/core/templatetags/custom_filters.py | 11 +++ civicfix/core/urls.py | 1 + civicfix/core/views.py | 48 ++++++++- 10 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 civicfix/core/templates/core/superadmin_reports.html create mode 100644 civicfix/core/templatetags/__init__.py create mode 100644 civicfix/core/templatetags/__pycache__/__init__.cpython-313.pyc create mode 100644 civicfix/core/templatetags/__pycache__/custom_filters.cpython-313.pyc create mode 100644 civicfix/core/templatetags/custom_filters.py diff --git a/civicfix/core/__pycache__/urls.cpython-313.pyc b/civicfix/core/__pycache__/urls.cpython-313.pyc index b6bde5bd993e921320d56d0e9c4890bfbc44b6b0..9e17988b733a59cb9091f77f01e4d7f5f74fa4cf 100644 GIT binary patch delta 189 zcmaDPv`bk1GcPX}0}%YLUz<_K&cN^(#DM`BDC2Y3Ms-7Gtr$inhG2sj<{%500t6jw z7|dqLtS??9n{K3Oyg7kcmszojqbRi?zo?{G|CUg3X+dgHVoGjiUOZILPg8F41h)N5 zMY5B9*gd&4fMziQaq+p$^VzpE%HL$+=#ZKadRmOuB)l;pQf0U1nxK pP1(s8*!EAJ!tTbU4phhp#KpTeKVjd_$n!y0oS&(YyGRu%1^^_n6_5Y` diff --git a/civicfix/core/__pycache__/views.cpython-313.pyc b/civicfix/core/__pycache__/views.cpython-313.pyc index 1c0f9a9a4e36b93bdf9f7ac2ca5874cb933d22f7..bd48bd5c2c976a72dac5f75637e83c49f5fd5fec 100644 GIT binary patch delta 5638 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 GsOP`l@ArcM delta 4265 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 C@t2GM diff --git a/civicfix/core/templates/core/superadmin_reports.html b/civicfix/core/templates/core/superadmin_reports.html new file mode 100644 index 0000000..688cad5 --- /dev/null +++ b/civicfix/core/templates/core/superadmin_reports.html @@ -0,0 +1,91 @@ +{% extends "core/base.html" %} + +{% block content %} +
+
+
+

Analytics & Reports

+
+
+ +
Total Issues Reported: + {{ total_issues }} +
+ + + + + + +
+
+
+ + + +{# Safely pass JSON to JS #} +{{ status_counts|json_script:"status-data" }} +{{ top_departments|json_script:"dept-data" }} +{{ top_citizens|json_script:"citizen-data" }} +{{ issues_last_30_days|json_script:"trend-data" }} + + +{% endblock %} diff --git a/civicfix/core/templates/dashboard/superadmin_dashboard.html b/civicfix/core/templates/dashboard/superadmin_dashboard.html index f4edbb2..3a18696 100644 --- a/civicfix/core/templates/dashboard/superadmin_dashboard.html +++ b/civicfix/core/templates/dashboard/superadmin_dashboard.html @@ -24,7 +24,7 @@
  • - View Analytics & Reports + View Analytics & Reports
  • diff --git a/civicfix/core/templatetags/__init__.py b/civicfix/core/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/civicfix/core/templatetags/__pycache__/__init__.cpython-313.pyc b/civicfix/core/templatetags/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..942736da55934981399850e7f366933a8ed5978f GIT binary patch literal 151 zcmey&%ge<81Z(ToW`O9&AOZ#$p^VQgK*m&tbOudEzm*I{OhDdekklTZlX-=wL W5i8IbkTu01#z$sGM#ds$APWFR0wse0 literal 0 HcmV?d00001 diff --git a/civicfix/core/templatetags/__pycache__/custom_filters.cpython-313.pyc b/civicfix/core/templatetags/__pycache__/custom_filters.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4fab2c738b1ec05209a41b245b9f89e0a1a07c14 GIT binary patch literal 669 zcmZ8e&ubGw6n?Xt#58Nb61+4>$5mJYb*qP5Lg_^UQni$X_R=k7yO~|L-DKm;Y-#*O zJ&1TwF9ol9^WeYXS=)oKAQU`#v+T*6Z;~tlAH28UoA(XhH~X?`wE`GlUwnCVn*jV$ zO&;15WLpSMpbK4cAJzyb6dckigyyv2x}$f>vq_G++QN)e4D?3fw;DA_T9&Lo zj9kgQTviQAm^x;*j^zY4p$1!;(3U}HHMmy;h|Lb#{y#QJb9%a=J-iBF0L2>v?IzX< z@=^1NsI-qHcReZSp&KPkP@l(r>e46_lEyymg=)4pq6chbBiTiZtVOqBqe!Vyk=}TeS$WgyoM;2>A*1 lbEEddsGl13GvnHE`JBwXUVOQDp%uw;rh_&=M*Iq1_y@&{o~!@> literal 0 HcmV?d00001 diff --git a/civicfix/core/templatetags/custom_filters.py b/civicfix/core/templatetags/custom_filters.py new file mode 100644 index 0000000..b081d06 --- /dev/null +++ b/civicfix/core/templatetags/custom_filters.py @@ -0,0 +1,11 @@ +from django import template + +register = template.Library() + +@register.filter +def pluck(queryset_list, key): + """ + Extracts values from a list of dicts by key. + Example: [{'status': 'open', 'count': 5}]|pluck:"status" -> ['open'] + """ + return [d.get(key) for d in queryset_list] diff --git a/civicfix/core/urls.py b/civicfix/core/urls.py index 5cabe27..088018e 100644 --- a/civicfix/core/urls.py +++ b/civicfix/core/urls.py @@ -25,4 +25,5 @@ urlpatterns = [ path('ban-user//', views.ban_user, name='ban_user'), path('unban-user//', views.unban_user, name='unban_user'), path('issues//delete_fake/', views.delete_fake_issue, name='delete_fake_issue'), + path("reports/", views.superadmin_reports, name="superadmin_reports"), ] \ No newline at end of file diff --git a/civicfix/core/views.py b/civicfix/core/views.py index 224144c..c416ab0 100644 --- a/civicfix/core/views.py +++ b/civicfix/core/views.py @@ -3,10 +3,11 @@ from django.contrib.auth import authenticate, login from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.forms import AuthenticationForm from django.db import IntegrityError -from django.db.models import Exists, OuterRef +from django.db.models import Exists, OuterRef, Count from django.http import JsonResponse from django.shortcuts import render, redirect, get_object_or_404 from django.utils import timezone +from django.utils.timezone import now, timedelta from django.views.decorators.http import require_POST from .models import Issue, User, Vote, Comment, Department from .forms import CitizenRegistrationForm, IssueForm, CommentForm @@ -337,6 +338,51 @@ def delete_fake_issue(request, issue_id): messages.success(request, f"✅ Issue deleted and user {reporter.username} has been banned for 7 days.") return redirect("manage_issues") +@login_required +@user_passes_test(superadmin_check) +def superadmin_reports(request): + # 1. Total issues reported + total_issues = Issue.objects.count() + + # 2. Issues per status + status_counts = ( + Issue.objects.values('status') + .annotate(count=Count('id')) + .order_by() + ) + + # 3. Top 5 departments with most assigned issues + top_departments = ( + Issue.objects.values('department__name') + .annotate(count=Count('id')) + .order_by('-count')[:5] + ) + + # 4. Top citizens by number of reports + top_citizens = ( + Issue.objects.values('reporter__username') + .annotate(count=Count('id')) + .order_by('-count')[:5] + ) + + # 5. Issues over time (last 30 days) + last_30_days = now() - timedelta(days=30) + issues_last_30_days = ( + Issue.objects.filter(created_at__gte=last_30_days) + .extra(select={'day': "date(created_at)"}) + .values('day') + .annotate(count=Count('id')) + .order_by('day') + ) + + context = { + "total_issues": total_issues, + "status_counts": list(status_counts), + "top_departments": list(top_departments), + "top_citizens": list(top_citizens), + "issues_last_30_days": list(issues_last_30_days), + } + return render(request, "core/superadmin_reports.html", context) def resolver_check(user): return user.is_resolver