From 52faccea50a5a88941fd93f3034ddf11fb3dbd6b Mon Sep 17 00:00:00 2001 From: glenneth Date: Sun, 18 Jan 2026 12:22:01 +0300 Subject: [PATCH] Add sort dropdown for admin geo stats (minutes vs listeners) - Add dropdown to admin template to choose sort order - Update get-geo-stats to accept order-by parameter - Update API endpoint to pass sort-by parameter - Update ParenScript to read dropdown and pass to API - Default sort is by minutes (engagement time) --- asteroid.lisp | 7 ++++--- listener-stats.lisp | 16 ++++++++++------ parenscript/admin.lisp | 8 +++++--- template/admin.ctml | 7 +++++++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/asteroid.lisp b/asteroid.lisp index bc0f0ff..8b26fac 100644 --- a/asteroid.lisp +++ b/asteroid.lisp @@ -1403,10 +1403,11 @@ ("avg_session_minutes" . ,(sixth row)))) stats)))))) -(define-api asteroid/stats/geo (&optional (days "7")) () - "Get geographic distribution of listeners (admin only)" +(define-api asteroid/stats/geo (&optional (days "7") (sort-by "minutes")) () + "Get geographic distribution of listeners (admin only). + SORT-BY can be 'minutes' (default) or 'listeners'." (require-role :admin) - (let ((stats (get-geo-stats (parse-integer days :junk-allowed t)))) + (let ((stats (get-geo-stats (parse-integer days :junk-allowed t) sort-by))) (api-output `(("status" . "success") ("geo" . ,(mapcar (lambda (row) `(("country_code" . ,(first row)) diff --git a/listener-stats.lisp b/listener-stats.lisp index 8b185f9..2530a27 100644 --- a/listener-stats.lisp +++ b/listener-stats.lisp @@ -369,17 +369,21 @@ (log:error "Failed to get daily stats: ~a" e) nil))) -(defun get-geo-stats (&optional (days 7)) - "Get geographic distribution for the last N days" +(defun get-geo-stats (&optional (days 7) (order-by "minutes")) + "Get geographic distribution for the last N days. + ORDER-BY can be 'minutes' (default) or 'listeners'." (handler-case (with-db - (postmodern:query - (format nil "SELECT country_code, SUM(listener_count) as total_listeners, SUM(listen_minutes) as total_minutes + (let ((order-column (if (string= order-by "listeners") + "total_listeners" + "total_minutes"))) + (postmodern:query + (format nil "SELECT country_code, SUM(listener_count) as total_listeners, SUM(listen_minutes) as total_minutes FROM listener_geo_stats WHERE date > NOW() - INTERVAL '~a days' GROUP BY country_code - ORDER BY total_minutes DESC - LIMIT 20" days))) + ORDER BY ~a DESC + LIMIT 20" days order-column)))) (error (e) (log:error "Failed to get geo stats: ~a" e) nil))) diff --git a/parenscript/admin.lisp b/parenscript/admin.lisp index ff8f01c..02086e4 100644 --- a/parenscript/admin.lisp +++ b/parenscript/admin.lisp @@ -970,8 +970,10 @@ ;; Refresh geo stats from API (defun refresh-geo-stats () - (ps:chain - (fetch "/api/asteroid/stats/geo?days=7") + (let* ((sort-select (ps:chain document (get-element-by-id "geo-sort-by"))) + (sort-by (if sort-select (ps:@ sort-select value) "minutes"))) + (ps:chain + (fetch (+ "/api/asteroid/stats/geo?days=7&sort-by=" sort-by)) (then (lambda (response) (ps:chain response (json)))) (then (lambda (result) (let ((data (or (ps:@ result data) result)) @@ -1009,7 +1011,7 @@ (let ((tbody (ps:chain document (get-element-by-id "geo-stats-body")))) (when tbody (setf (ps:@ tbody inner-h-t-m-l) - "Error loading geo data"))))))) + "Error loading geo data")))))))) ;; Toggle city display for a country (defun toggle-country-cities (country) diff --git a/template/admin.ctml b/template/admin.ctml index 43c3bbd..c981b48 100644 --- a/template/admin.ctml +++ b/template/admin.ctml @@ -86,6 +86,13 @@

🌍 Listener Locations (Last 7 Days)

+
+ + +