updated speed logger

This commit is contained in:
2025-05-13 02:27:03 +02:00
parent 88b3439822
commit 5081df993f
10 changed files with 233 additions and 73 deletions

65
app.py
View File

@@ -1,38 +1,67 @@
from flask import Flask, render_template from flask import Flask, render_template, request
import sqlite3 import sqlite3
import pandas as pd import pandas as pd
from config import DB_PATH from config import DB_PATH
from datetime import datetime import math
app = Flask(__name__) app = Flask(__name__)
def load_data(): def load_data():
# Connect to SQLite database # 1) grab raw
conn = sqlite3.connect(DB_PATH) conn = sqlite3.connect(DB_PATH)
query = "SELECT * FROM speed_tests" df = pd.read_sql("SELECT * FROM speed_tests", conn)
df = pd.read_sql(query, conn)
conn.close() conn.close()
# 2) parse epoch→UTC datetimes, then shift to Europe/Berlin
df['datetime'] = (
pd.to_datetime(df['timestamp'], unit='s', utc=True)
.dt.tz_convert('Europe/Berlin')
)
# 3) for your table display, format as naive strings
df['recorded_at'] = df['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S')
return df return df
def get_aggregated_data(df: pd.DataFrame, interval: str = '5min'):
# ensure 'datetime' is the index
df = df.set_index('datetime')
# 1) resample into N-minute bins, mean
agg = (
df
.resample(interval)
.agg({
'down_90th': 'mean',
'up_90th': 'mean'
})
.reset_index()
)
# 3) output for Chart.js
return {
"times": agg['recorded_at'].tolist(),
"down_90th": agg['down_90th'].round(2).tolist(),
"up_90th": agg['up_90th'].round(2).tolist()
}
@app.route('/') @app.route('/')
def index(): def index():
df = load_data() df = load_data()
agg = request.args.get("agg", "5min")
chart_data = get_aggregated_data(df, agg)
print(f"down last 10: {chart_data['down_90th'][-10:]}")
print(f"down last 10: {chart_data['up_90th'][-10:]}")
print(math.isnan(chart_data['down_90th'][-1]) or math.isnan(chart_data['up_90th'][-1]))
# Convert timestamps to human-readable format #if math.isnan(chart_data['down_90th'][-1]) or math.isnan(chart_data['up_90th'][-1]):
df['datetime'] = pd.to_datetime(df['timestamp'], unit='s') #return render_template('local.html', data=df.to_dict(orient='records'), chart_data=chart_data)
# Suppose your DataFrame is called `df`
df["timestamp"] = df["timestamp"].apply(lambda ts: datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S"))
# Collect the data for charts return render_template('index.html', aggregation=agg, data=df.to_dict(orient='records'), chart_data=chart_data)
chart_data = {
"times": df['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S').tolist(),
"down_90th": df['down_90th'].tolist(),
"up_90th": df['up_90th'].tolist()
}
return render_template('index.html', data=df.to_dict(orient='records'), chart_data=chart_data)
if __name__ == '__main__': if __name__ == '__main__':
app.run(port=5001) app.run(host='0.0.0.0', port=5001)

17
prometheus_test.py Normal file
View File

@@ -0,0 +1,17 @@
from prometheus_client import CollectorRegistry, Gauge, push_to_gateway
# Config
PUSHGATEWAY_URL = "https://pushgateway.cato447.de"
JOB_NAME = "test_push"
# Create a new registry for the job
registry = CollectorRegistry()
test_metric = Gauge('test_metric_value', 'Just a test metric', registry=registry)
# Set the metric value (e.g., random or fixed)
test_metric.set(42)
# Push to the gateway
push_to_gateway(PUSHGATEWAY_URL, job=JOB_NAME, registry=registry)
print("Pushed test metric to Pushgateway.")

View File

@@ -9,4 +9,5 @@ dependencies = [
"flask>=3.1.0", "flask>=3.1.0",
"getmac>=0.9.5", "getmac>=0.9.5",
"pandas>=2.2.3", "pandas>=2.2.3",
"prometheus-client>=0.21.1",
] ]

30
show_data.py Normal file
View File

@@ -0,0 +1,30 @@
import sqlite3
import pandas as pd
from config import DB_PATH
from datetime import datetime
def load_data():
# Connect to SQLite database
conn = sqlite3.connect(DB_PATH)
query = "SELECT * FROM speed_tests"
df = pd.read_sql(query, conn)
conn.close()
return df
df = load_data()
# Convert timestamps to human-readable format
df['datetime'] = pd.to_datetime(df['timestamp'], unit='s')
# Suppose your DataFrame is called `df`
df["timestamp"] = df["timestamp"].apply(lambda ts: datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S"))
# Collect the data for charts
chart_data = {
"times": df['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S').tolist(),
"down_90th": df['down_90th'].tolist(),
"up_90th": df['up_90th'].tolist()
}
print(df[-60:])

20
static/js/chart.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,40 +1,42 @@
{% extends 'layout.html' %} <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Internet Speed Dashboard</title>
{% block content %} <!-- DataTables CSS -->
<h2>Graph: Download vs. Upload Speeds</h2> <link
<canvas id="speedChart"></canvas> rel="stylesheet"
href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css"
/>
<h2>Test Results</h2> <!-- jQuery + DataTables JS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
<table border="1"> <!-- Chart.js core (umd build) -->
<thead> <script src="https://cdn.jsdelivr.net/npm/chart.js/dist/chart.umd.min.js"></script>
<tr>
<th>Timestamp</th>
<th>ISP</th>
<th>Latency</th>
<th>Jitter</th>
<th>Download (90th Percentile)</th>
<th>Upload (90th Percentile)</th>
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
<td>{{ row.timestamp }}</td>
<td>{{ row.isp }}</td>
<td>{{ row.latency }}</td>
<td>{{ row.jitter }}</td>
<td>{{ row.down_90th }}</td>
<td>{{ row.up_90th }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- date adapter (Moment.js + Chart.js adapter) -->
<script src="https://cdn.jsdelivr.net/npm/moment/min/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment/dist/chartjs-adapter-moment.min.js"></script>
<script> <!-- zoom & pan plugin -->
Chart.register(window['chartjs-plugin-annotation']); <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom/dist/chartjs-plugin-zoom.min.js"></script>
<style>
body { font-family: sans-serif; margin: 2rem; }
#speedChart { max-width: 100%; margin-bottom: 2rem; }
table.dataTable { width: 100% !important; }
</style>
</head>
<body>
<h1>90th-Percentile Speeds (Aggregated every {{ aggregation }})</h1>
<!-- Chart -->
<canvas id="speedChart" width="800" height="300"></canvas>
<script>
var chartData = {{ chart_data | tojson }}; var chartData = {{ chart_data | tojson }};
const acceptableDownloadRange = { const acceptableDownloadRange = {
@@ -181,5 +183,36 @@
} }
}); });
</script> </script>
{% endblock %}
<!-- Data table -->
<h2>Raw Data</h2>
<table id="speedTable" class="display">
<thead>
<tr>
<th>Recorded At</th>
<th>Download (90th)</th>
<th>Upload (90th)</th>
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
<td>{{ row.datetime }}</td>
<td>{{ row.down_90th }}</td>
<td>{{ row.up_90th }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<script>
$(document).ready(function() {
$('#speedTable').DataTable({
pageLength: 50,
order: [[0, 'desc']]
});
});
</script>
</body>
</html>

View File

@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Speed Test Results</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation"></script>
</head>
<body>
<header>
<h1>Network Speed Test Results</h1>
</header>
<main>
{% block content %}
{% endblock %}
</main>
</body>
</html>

34
templates/local.html Normal file
View File

@@ -0,0 +1,34 @@
{% extends 'layout.html' %}
{% block content %}
<h2>Graph: Download vs. Upload Speeds</h2>
<canvas id="speedChart"></canvas>
<h2>Test Results</h2>
<table border="1">
<thead>
<tr>
<th>Timestamp</th>
<th>ISP</th>
<th>Latency</th>
<th>Jitter</th>
<th>Download (90th Percentile)</th>
<th>Upload (90th Percentile)</th>
</tr>
</thead>
<tbody>
{% for row in data|reverse %}
<tr>
<td>{{ row.timestamp }}</td>
<td>{{ row.isp }}</td>
<td>{{ row.latency }}</td>
<td>{{ row.jitter }}</td>
<td>{{ row.down_90th }}</td>
<td>{{ row.up_90th }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

11
uv.lock generated
View File

@@ -213,6 +213,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436, upload-time = "2024-09-20T13:09:48.112Z" }, { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436, upload-time = "2024-09-20T13:09:48.112Z" },
] ]
[[package]]
name = "prometheus-client"
version = "0.21.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/62/14/7d0f567991f3a9af8d1cd4f619040c93b68f09a02b6d0b6ab1b2d1ded5fe/prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb", size = 78551, upload-time = "2024-12-03T14:59:12.164Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ff/c2/ab7d37426c179ceb9aeb109a85cda8948bb269b7561a0be870cc656eefe4/prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301", size = 54682, upload-time = "2024-12-03T14:59:10.935Z" },
]
[[package]] [[package]]
name = "python-dateutil" name = "python-dateutil"
version = "2.9.0.post0" version = "2.9.0.post0"
@@ -267,6 +276,7 @@ dependencies = [
{ name = "flask" }, { name = "flask" },
{ name = "getmac" }, { name = "getmac" },
{ name = "pandas" }, { name = "pandas" },
{ name = "prometheus-client" },
] ]
[package.metadata] [package.metadata]
@@ -275,6 +285,7 @@ requires-dist = [
{ name = "flask", specifier = ">=3.1.0" }, { name = "flask", specifier = ">=3.1.0" },
{ name = "getmac", specifier = ">=0.9.5" }, { name = "getmac", specifier = ">=0.9.5" },
{ name = "pandas", specifier = ">=2.2.3" }, { name = "pandas", specifier = ">=2.2.3" },
{ name = "prometheus-client", specifier = ">=0.21.1" },
] ]
[[package]] [[package]]