add hourly bar diagram
This commit is contained in:
parent
2101669567
commit
2ee5cf3a26
52
analytics.py
52
analytics.py
@ -123,16 +123,44 @@ def dashboard():
|
|||||||
''', (start.isoformat(),))
|
''', (start.isoformat(),))
|
||||||
daily_access_data = [dict(date=row[0], count=row[1]) for row in cursor.fetchall()]
|
daily_access_data = [dict(date=row[0], count=row[1]) for row in cursor.fetchall()]
|
||||||
|
|
||||||
# Top files for bar chart
|
# Aggregate download counts by time bucket according to the timeframe.
|
||||||
cursor.execute('''
|
if timeframe == 'today':
|
||||||
SELECT rel_path, COUNT(*) as access_count
|
# Group by hour (0-23)
|
||||||
FROM file_access_log
|
cursor.execute('''
|
||||||
WHERE timestamp >= ?
|
SELECT strftime('%H', timestamp) as bucket, COUNT(*) as count
|
||||||
GROUP BY rel_path
|
FROM file_access_log
|
||||||
ORDER BY access_count DESC
|
WHERE timestamp >= ?
|
||||||
LIMIT 10
|
GROUP BY bucket
|
||||||
''', (start.isoformat(),))
|
ORDER BY bucket
|
||||||
top_files_data = [dict(rel_path=row[0], access_count=row[1]) for row in cursor.fetchall()]
|
''', (start.isoformat(),))
|
||||||
|
elif timeframe in ('7days', '30days'):
|
||||||
|
# Group by day (YYYY-MM-DD)
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT date(timestamp) as bucket, COUNT(*) as count
|
||||||
|
FROM file_access_log
|
||||||
|
WHERE timestamp >= ?
|
||||||
|
GROUP BY bucket
|
||||||
|
ORDER BY bucket
|
||||||
|
''', (start.isoformat(),))
|
||||||
|
elif timeframe == '365days':
|
||||||
|
# Group by month (YYYY-MM)
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT strftime('%Y-%m', timestamp) as bucket, COUNT(*) as count
|
||||||
|
FROM file_access_log
|
||||||
|
WHERE timestamp >= ?
|
||||||
|
GROUP BY bucket
|
||||||
|
ORDER BY bucket
|
||||||
|
''', (start.isoformat(),))
|
||||||
|
else:
|
||||||
|
# Fallback: group by day
|
||||||
|
cursor.execute('''
|
||||||
|
SELECT date(timestamp) as bucket, COUNT(*) as count
|
||||||
|
FROM file_access_log
|
||||||
|
WHERE timestamp >= ?
|
||||||
|
GROUP BY bucket
|
||||||
|
ORDER BY bucket
|
||||||
|
''', (start.isoformat(),))
|
||||||
|
timeframe_data = [dict(bucket=row[0], count=row[1]) for row in cursor.fetchall()]
|
||||||
|
|
||||||
# User agent distribution (aggregate by device type)
|
# User agent distribution (aggregate by device type)
|
||||||
cursor.execute('''
|
cursor.execute('''
|
||||||
@ -224,10 +252,10 @@ def dashboard():
|
|||||||
timeframe=timeframe,
|
timeframe=timeframe,
|
||||||
rows=rows,
|
rows=rows,
|
||||||
daily_access_data=daily_access_data,
|
daily_access_data=daily_access_data,
|
||||||
top_files_data=top_files_data,
|
|
||||||
user_agent_data=user_agent_data,
|
user_agent_data=user_agent_data,
|
||||||
folder_data=folder_data,
|
folder_data=folder_data,
|
||||||
location_data=location_data,
|
location_data=location_data,
|
||||||
total_accesses=total_accesses,
|
total_accesses=total_accesses,
|
||||||
unique_files=unique_files,
|
unique_files=unique_files,
|
||||||
unique_user=unique_user)
|
unique_user=unique_user,
|
||||||
|
timeframe_data=timeframe_data)
|
||||||
@ -75,12 +75,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Top Files Accessed Chart -->
|
<!-- Timeframe Breakdown Chart (Bar Chart) -->
|
||||||
<div class="col-md-6 mb-4">
|
<div class="col-md-6 mb-4">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Häufig geladene Dateien</h5>
|
<h5 class="card-title">Downloads nach Zeit</h5>
|
||||||
<canvas id="topFilesChart"></canvas>
|
<canvas id="timeframeChart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -171,11 +171,41 @@
|
|||||||
<script>
|
<script>
|
||||||
// Data passed from the backend as JSON
|
// Data passed from the backend as JSON
|
||||||
const dailyAccessData = {{ daily_access_data|tojson }};
|
const dailyAccessData = {{ daily_access_data|tojson }};
|
||||||
const topFilesData = {{ top_files_data|tojson }};
|
// Replace topFilesData usage with timeframeData for this chart
|
||||||
// Note: user_agent_data now contains 'device' and 'count'
|
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 }};
|
||||||
|
|
||||||
|
// shift the labels to local time zone
|
||||||
|
const timeframe = "{{ timeframe }}"; // e.g., 'today', '7days', '30days', or '365days'
|
||||||
|
const shiftedLabels = timeframeData.map(item => {
|
||||||
|
if (timeframe === 'today') {
|
||||||
|
// For "today", the bucket is an hour in UTC (e.g., "14")
|
||||||
|
const utcHour = parseInt(item.bucket, 10);
|
||||||
|
const now = new Date();
|
||||||
|
// Create Date objects for the start and end of the hour in UTC
|
||||||
|
const utcStart = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), utcHour));
|
||||||
|
const utcEnd = new Date(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate(), utcHour + 1));
|
||||||
|
// Convert to local time strings, e.g., "16:00"
|
||||||
|
const localStart = utcStart.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||||
|
const localEnd = utcEnd.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||||
|
return `${localStart} - ${localEnd}`;
|
||||||
|
} else if (timeframe === '7days' || timeframe === '30days') {
|
||||||
|
// For these timeframes, the bucket is a date in the format "YYYY-MM-DD"
|
||||||
|
const utcDate = new Date(item.bucket + 'T00:00:00Z');
|
||||||
|
return utcDate.toLocaleDateString(); // Adjust formatting as needed
|
||||||
|
} else if (timeframe === '365days') {
|
||||||
|
// For this timeframe, the bucket is a month in the format "YYYY-MM"
|
||||||
|
const [year, month] = item.bucket.split('-');
|
||||||
|
const dateObj = new Date(year, month - 1, 1);
|
||||||
|
// Format to something like "Mar 2025"
|
||||||
|
return dateObj.toLocaleString([], { month: 'short', year: 'numeric' });
|
||||||
|
} else {
|
||||||
|
// Fallback: use the bucket value as-is
|
||||||
|
return item.bucket;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Access Trend Chart - Line Chart
|
// Access Trend Chart - Line Chart
|
||||||
const ctxTrend = document.getElementById('accessTrendChart').getContext('2d');
|
const ctxTrend = document.getElementById('accessTrendChart').getContext('2d');
|
||||||
new Chart(ctxTrend, {
|
new Chart(ctxTrend, {
|
||||||
@ -199,25 +229,24 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Top Files Chart - Horizontal Bar Chart
|
// Timeframe Breakdown Chart - Bar Chart (for "today" timeframe)
|
||||||
const ctxTopFiles = document.getElementById('topFilesChart').getContext('2d');
|
const ctxTimeframe = document.getElementById('timeframeChart').getContext('2d');
|
||||||
new Chart(ctxTopFiles, {
|
new Chart(ctxTimeframe, {
|
||||||
type: 'bar',
|
type: 'bar',
|
||||||
data: {
|
data: {
|
||||||
labels: topFilesData.map(item => item.full_path),
|
labels: shiftedLabels,
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Download Count',
|
label: 'Download Count',
|
||||||
data: topFilesData.map(item => item.access_count),
|
data: timeframeData.map(item => item.count),
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
indexAxis: 'y',
|
|
||||||
responsive: true,
|
responsive: true,
|
||||||
plugins: { legend: { display: false } },
|
plugins: { legend: { display: false } },
|
||||||
scales: {
|
scales: {
|
||||||
x: { title: { display: true, text: 'Download Count' } },
|
x: { title: { display: true, text: 'Local Time Range' } },
|
||||||
y: { title: { display: true, text: '' } }
|
y: { title: { display: true, text: 'Download Count' } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user