Merge branch 'development' of https://git.ll5.de/lelo/app.bethaus into development
This commit is contained in:
commit
6434f5b410
61
analytics.py
61
analytics.py
@ -1,5 +1,5 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
from flask import render_template, request
|
from flask import render_template, request, session
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import geoip2.database
|
import geoip2.database
|
||||||
from auth import require_secret
|
from auth import require_secret
|
||||||
@ -94,8 +94,13 @@ def connections():
|
|||||||
|
|
||||||
@require_secret
|
@require_secret
|
||||||
def dashboard():
|
def dashboard():
|
||||||
filetype_arg = request.args.get('filetype', 'audio')
|
if 'filetype' not in session:
|
||||||
timeframe = request.args.get('timeframe', 'last24hours')
|
session['filetype'] = 'audio'
|
||||||
|
if 'timeframe' not in session:
|
||||||
|
session['timeframe'] = 'last24hours'
|
||||||
|
session['filetype'] = request.args.get('filetype', session['filetype'])
|
||||||
|
session['timeframe'] = request.args.get('timeframe', session['timeframe'])
|
||||||
|
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
|
||||||
# Determine which file type we're filtering by.
|
# Determine which file type we're filtering by.
|
||||||
@ -106,21 +111,21 @@ def dashboard():
|
|||||||
image_list = ['jpg', 'jpeg', 'image', 'photo']
|
image_list = ['jpg', 'jpeg', 'image', 'photo']
|
||||||
video_list = ['mp4', 'mov', 'wmv', 'avi']
|
video_list = ['mp4', 'mov', 'wmv', 'avi']
|
||||||
|
|
||||||
if filetype_arg.lower() in audio_list:
|
if session['filetype'].lower() in audio_list:
|
||||||
filetype = 'audio/'
|
filetype = 'audio/'
|
||||||
elif filetype_arg.lower() in image_list:
|
elif session['filetype'].lower() in image_list:
|
||||||
filetype = 'image/'
|
filetype = 'image/'
|
||||||
elif filetype_arg.lower() in video_list:
|
elif session['filetype'].lower() in video_list:
|
||||||
filetype = 'video/'
|
filetype = 'video/'
|
||||||
|
|
||||||
# Determine start time based on timeframe
|
# Determine start time based on session['timeframe']
|
||||||
if timeframe == 'last24hours':
|
if session['timeframe'] == 'last24hours':
|
||||||
start_dt = now - timedelta(hours=24)
|
start_dt = now - timedelta(hours=24)
|
||||||
elif timeframe == '7days':
|
elif session['timeframe'] == '7days':
|
||||||
start_dt = now - timedelta(days=7)
|
start_dt = now - timedelta(days=7)
|
||||||
elif timeframe == '30days':
|
elif session['timeframe'] == '30days':
|
||||||
start_dt = now - timedelta(days=30)
|
start_dt = now - timedelta(days=30)
|
||||||
elif timeframe == '365days':
|
elif session['timeframe'] == '365days':
|
||||||
start_dt = now - timedelta(days=365)
|
start_dt = now - timedelta(days=365)
|
||||||
else:
|
else:
|
||||||
start_dt = now - timedelta(hours=24)
|
start_dt = now - timedelta(hours=24)
|
||||||
@ -157,8 +162,8 @@ def dashboard():
|
|||||||
|
|
||||||
# 2. Distinct device trend
|
# 2. Distinct device trend
|
||||||
# We'll group by hour if "today", by day if "7days"/"30days", by month if "365days"
|
# We'll group by hour if "today", by day if "7days"/"30days", by month if "365days"
|
||||||
if timeframe == 'last24hours':
|
if session['timeframe'] == 'last24hours':
|
||||||
# Group by hour
|
# Group by hour: substr(timestamp, 12, 2) -> HH
|
||||||
query = f'''
|
query = f'''
|
||||||
SELECT substr(timestamp, 1, 13) AS bucket, COUNT(DISTINCT device_id) AS count
|
SELECT substr(timestamp, 1, 13) AS bucket, COUNT(DISTINCT device_id) AS count
|
||||||
FROM file_access_log
|
FROM file_access_log
|
||||||
@ -166,7 +171,7 @@ def dashboard():
|
|||||||
GROUP BY bucket
|
GROUP BY bucket
|
||||||
ORDER BY bucket
|
ORDER BY bucket
|
||||||
'''
|
'''
|
||||||
elif timeframe in ('7days', '30days'):
|
elif session['timeframe'] in ('7days', '30days'):
|
||||||
# Group by day: substr(timestamp, 1, 10) -> YYYY-MM-DD
|
# Group by day: substr(timestamp, 1, 10) -> YYYY-MM-DD
|
||||||
query = f'''
|
query = f'''
|
||||||
SELECT substr(timestamp, 1, 10) AS bucket, COUNT(DISTINCT device_id) AS count
|
SELECT substr(timestamp, 1, 10) AS bucket, COUNT(DISTINCT device_id) AS count
|
||||||
@ -175,7 +180,7 @@ def dashboard():
|
|||||||
GROUP BY bucket
|
GROUP BY bucket
|
||||||
ORDER BY bucket
|
ORDER BY bucket
|
||||||
'''
|
'''
|
||||||
elif timeframe == '365days':
|
elif session['timeframe'] == '365days':
|
||||||
# Group by month: substr(timestamp, 1, 7) -> YYYY-MM
|
# Group by month: substr(timestamp, 1, 7) -> YYYY-MM
|
||||||
query = f'''
|
query = f'''
|
||||||
SELECT substr(timestamp, 1, 7) AS bucket, COUNT(DISTINCT device_id) AS count
|
SELECT substr(timestamp, 1, 7) AS bucket, COUNT(DISTINCT device_id) AS count
|
||||||
@ -200,10 +205,10 @@ def dashboard():
|
|||||||
dict(bucket=r[0], count=r[1]) for r in distinct_device_data_rows
|
dict(bucket=r[0], count=r[1]) for r in distinct_device_data_rows
|
||||||
]
|
]
|
||||||
|
|
||||||
# 3. Timeframe-based aggregation
|
# 3. session['timeframe']-based aggregation
|
||||||
# We'll group by hour if "today", by day if "7days"/"30days", by month if "365days".
|
# We'll group by hour if "today", by day if "7days"/"30days", by month if "365days".
|
||||||
if timeframe == 'last24hours':
|
if session['timeframe'] == 'last24hours':
|
||||||
# Group by Hour
|
# Hour: substr(timestamp, 12, 2) -> HH
|
||||||
query = f'''
|
query = f'''
|
||||||
SELECT substr(timestamp, 1, 13) AS bucket, COUNT(*) AS count
|
SELECT substr(timestamp, 1, 13) AS bucket, COUNT(*) AS count
|
||||||
FROM file_access_log
|
FROM file_access_log
|
||||||
@ -211,7 +216,7 @@ def dashboard():
|
|||||||
GROUP BY bucket
|
GROUP BY bucket
|
||||||
ORDER BY bucket
|
ORDER BY bucket
|
||||||
'''
|
'''
|
||||||
elif timeframe in ('7days', '30days'):
|
elif session['timeframe'] in ('7days', '30days'):
|
||||||
# Day: substr(timestamp, 1, 10) -> YYYY-MM-DD
|
# Day: substr(timestamp, 1, 10) -> YYYY-MM-DD
|
||||||
query = f'''
|
query = f'''
|
||||||
SELECT substr(timestamp, 1, 10) AS bucket, COUNT(*) AS count
|
SELECT substr(timestamp, 1, 10) AS bucket, COUNT(*) AS count
|
||||||
@ -220,7 +225,7 @@ def dashboard():
|
|||||||
GROUP BY bucket
|
GROUP BY bucket
|
||||||
ORDER BY bucket
|
ORDER BY bucket
|
||||||
'''
|
'''
|
||||||
elif timeframe == '365days':
|
elif session['timeframe'] == '365days':
|
||||||
# Month: substr(timestamp, 1, 7) -> YYYY-MM
|
# Month: substr(timestamp, 1, 7) -> YYYY-MM
|
||||||
query = f'''
|
query = f'''
|
||||||
SELECT substr(timestamp, 1, 7) AS bucket, COUNT(*) AS count
|
SELECT substr(timestamp, 1, 7) AS bucket, COUNT(*) AS count
|
||||||
@ -328,6 +333,19 @@ def dashboard():
|
|||||||
cursor = log_db.execute(query, params_for_filter)
|
cursor = log_db.execute(query, params_for_filter)
|
||||||
unique_user = cursor.fetchone()[0]
|
unique_user = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# Percentage of cached calls
|
||||||
|
query = f'''
|
||||||
|
SELECT (CAST(SUM(CASE WHEN cached = 1 THEN 1 ELSE 0 END) AS FLOAT) / COUNT(*)) * 100
|
||||||
|
FROM file_access_log
|
||||||
|
WHERE timestamp >= ? {filetype_filter_sql}
|
||||||
|
'''
|
||||||
|
with log_db:
|
||||||
|
cursor = log_db.execute(query, params_for_filter)
|
||||||
|
cached_percentage = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
if cached_percentage is not None:
|
||||||
|
cached_percentage = f"{cached_percentage:.2f}"
|
||||||
|
|
||||||
# 8. Process location data with GeoIP2
|
# 8. Process location data with GeoIP2
|
||||||
reader = geoip2.database.Reader('GeoLite2-City.mmdb')
|
reader = geoip2.database.Reader('GeoLite2-City.mmdb')
|
||||||
location_data_dict = {}
|
location_data_dict = {}
|
||||||
@ -349,7 +367,7 @@ def dashboard():
|
|||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"dashboard.html",
|
"dashboard.html",
|
||||||
timeframe=timeframe,
|
timeframe=session['timeframe'],
|
||||||
rows=rows,
|
rows=rows,
|
||||||
distinct_device_data=distinct_device_data,
|
distinct_device_data=distinct_device_data,
|
||||||
user_agent_data=user_agent_data,
|
user_agent_data=user_agent_data,
|
||||||
@ -358,5 +376,6 @@ def dashboard():
|
|||||||
total_accesses=total_accesses,
|
total_accesses=total_accesses,
|
||||||
unique_files=unique_files,
|
unique_files=unique_files,
|
||||||
unique_user=unique_user,
|
unique_user=unique_user,
|
||||||
|
cached_percentage=cached_percentage,
|
||||||
timeframe_data=timeframe_data
|
timeframe_data=timeframe_data
|
||||||
)
|
)
|
||||||
|
|||||||
@ -30,15 +30,44 @@
|
|||||||
<a href="{{ url_for('dashboard') }}" class="btn btn-primary mt-1">Auswertung</a>
|
<a href="{{ url_for('dashboard') }}" class="btn btn-primary mt-1">Auswertung</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<a href="{{ url_for('dashboard', timeframe='last24hours') }}" class="btn btn-secondary btn-sm mt-1">Last 24 Hours</a>
|
<a href="{{ url_for('dashboard', timeframe='last24hours') }}"
|
||||||
<a href="{{ url_for('dashboard', timeframe='7days') }}" class="btn btn-secondary btn-sm mt-1">Last 7 Days</a>
|
class="btn btn-sm mt-1 {% if session['timeframe'] == 'last24hours' %}btn-warning{% else %}btn-secondary{% endif %}">
|
||||||
<a href="{{ url_for('dashboard', timeframe='30days') }}" class="btn btn-secondary btn-sm mt-1">Last 30 Days</a>
|
Last 24 Hours
|
||||||
<a href="{{ url_for('dashboard', timeframe='365days') }}" class="btn btn-secondary btn-sm mt-1">Last 365 Days</a>
|
</a>
|
||||||
|
<a href="{{ url_for('dashboard', timeframe='7days') }}"
|
||||||
|
class="btn btn-sm mt-1 {% if session['timeframe'] == '7days' %}btn-warning{% else %}btn-secondary{% endif %}">
|
||||||
|
Last 7 Days
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('dashboard', timeframe='30days') }}"
|
||||||
|
class="btn btn-sm mt-1 {% if session['timeframe'] == '30days' %}btn-warning{% else %}btn-secondary{% endif %}">
|
||||||
|
Last 30 Days
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('dashboard', timeframe='365days') }}"
|
||||||
|
class="btn btn-sm mt-1 {% if session['timeframe'] == '365days' %}btn-warning{% else %}btn-secondary{% endif %}">
|
||||||
|
Last 365 Days
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<a href="{{ url_for('dashboard', filetype='audio') }}"
|
||||||
|
class="btn btn-sm mt-1 {% if session['filetype'] == 'audio' %}btn-warning{% else %}btn-secondary{% endif %}">
|
||||||
|
Audio
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('dashboard', filetype='video') }}"
|
||||||
|
class="btn btn-sm mt-1 {% if session['filetype'] == 'video' %}btn-warning{% else %}btn-secondary{% endif %}">
|
||||||
|
Video
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('dashboard', filetype='photo') }}"
|
||||||
|
class="btn btn-sm mt-1 {% if session['filetype'] == 'photo' %}btn-warning{% else %}btn-secondary{% endif %}">
|
||||||
|
Photo
|
||||||
|
</a>
|
||||||
|
<a href="{{ url_for('dashboard', filetype='other') }}"
|
||||||
|
class="btn btn-sm mt-1 {% if session['filetype'] == 'other' %}btn-warning{% else %}btn-secondary{% endif %}">
|
||||||
|
Other
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Summary Cards -->
|
<!-- Summary Cards -->
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-md-4">
|
<div class="col-md-3">
|
||||||
<div class="card text-white bg-info">
|
<div class="card text-white bg-info">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Alle Downloads</h5>
|
<h5 class="card-title">Alle Downloads</h5>
|
||||||
@ -46,7 +75,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-3">
|
||||||
<div class="card text-white bg-success">
|
<div class="card text-white bg-success">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">eindeutige Dateien</h5>
|
<h5 class="card-title">eindeutige Dateien</h5>
|
||||||
@ -54,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-3">
|
||||||
<div class="card text-white bg-warning">
|
<div class="card text-white bg-warning">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">eindeutige Nutzer</h5>
|
<h5 class="card-title">eindeutige Nutzer</h5>
|
||||||
@ -62,6 +91,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<div class="card text-white bg-secondary">
|
||||||
|
<div class="card-body">
|
||||||
|
<h5 class="card-title">beschleunigte Downloads</h5>
|
||||||
|
<p class="card-text">{{ cached_percentage }} %</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Charts Section -->
|
<!-- Charts Section -->
|
||||||
@ -171,7 +208,6 @@
|
|||||||
<script>
|
<script>
|
||||||
// Data passed from the backend as JSON
|
// Data passed from the backend as JSON
|
||||||
const distinctDeviceData = {{ distinct_device_data|tojson }};
|
const distinctDeviceData = {{ distinct_device_data|tojson }};
|
||||||
// Replace topFilesData usage with timeframeData for this chart
|
|
||||||
const timeframeData = {{ timeframe_data|tojson }};
|
const timeframeData = {{ timeframe_data|tojson }};
|
||||||
const userAgentData = {{ user_agent_data|tojson }};
|
const userAgentData = {{ user_agent_data|tojson }};
|
||||||
const folderData = {{ folder_data|tojson }};
|
const folderData = {{ folder_data|tojson }};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user