diff --git a/analytics.py b/analytics.py
index 785c9bf..c73b08b 100644
--- a/analytics.py
+++ b/analytics.py
@@ -572,6 +572,24 @@ def dashboard():
cursor = log_db.execute(query, params_for_filter)
locations = cursor.fetchall()
+ # Map data grouped by coordinates
+ map_rows = []
+ try:
+ query = f'''
+ SELECT city, country, latitude, longitude, COUNT(*) as count
+ FROM file_access_log
+ WHERE timestamp >= ? {filetype_filter_sql}
+ AND latitude IS NOT NULL AND longitude IS NOT NULL
+ GROUP BY city, country, latitude, longitude
+ ORDER BY count DESC
+ '''
+ with log_db:
+ cursor = log_db.execute(query, params_for_filter)
+ map_rows = cursor.fetchall()
+ except sqlite3.OperationalError as exc:
+ # Keep dashboard working even if older DBs lack location columns
+ print(f"[dashboard] map query skipped: {exc}")
+
# 7. Summary stats
# total_accesses
query = f'''
@@ -629,6 +647,20 @@ def dashboard():
location_data.sort(key=lambda x: x['count'], reverse=True)
location_data = location_data[:20]
+ # Prepare map data (limit to keep map readable)
+ map_data = []
+ for city, country, lat, lon, cnt in map_rows:
+ if lat is None or lon is None:
+ continue
+ map_data.append({
+ 'city': city,
+ 'country': country,
+ 'lat': lat,
+ 'lon': lon,
+ 'count': cnt
+ })
+ map_data = map_data[:200]
+
title_short = app_config.get('TITLE_SHORT', 'Default Title')
title_long = app_config.get('TITLE_LONG' , 'Default Title')
@@ -644,6 +676,7 @@ def dashboard():
unique_user=unique_user,
cached_percentage=cached_percentage,
timeframe_data=timeframe_data,
+ map_data=map_data,
admin_enabled=auth.is_admin(),
title_short=title_short,
title_long=title_long
diff --git a/templates/dashboard.html b/templates/dashboard.html
index f2e2d68..1f7ae54 100644
--- a/templates/dashboard.html
+++ b/templates/dashboard.html
@@ -4,6 +4,20 @@
{# page title #}
{% block title %}Dashboard{% endblock %}
+{% block head_extra %}
+
+
+
+{% endblock %}
+
{# page content #}
{% block content %}
@@ -185,35 +199,47 @@
-
-
-