From 4111f100d9063f26a55c7d003905c6137d3b88f6 Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Sun, 22 Feb 2026 11:10:32 +0200 Subject: [PATCH 1/2] analytics: Add /analytics endpoint Signed-off-by: Denys Fedoryshchenko --- api/main.py | 18 +- api/static/__init__.py | 6 + api/static/css/__init__.py | 6 + api/static/js/__init__.py | 6 + api/templates/analytics.html | 744 +++++++++++++++++++++++++++++++++++ 5 files changed, 778 insertions(+), 2 deletions(-) create mode 100644 api/static/__init__.py create mode 100644 api/static/css/__init__.py create mode 100644 api/static/js/__init__.py create mode 100644 api/templates/analytics.html diff --git a/api/main.py b/api/main.py index b766c30c..47347e65 100644 --- a/api/main.py +++ b/api/main.py @@ -1124,8 +1124,6 @@ async def get_telemetry_stats(request: Request): }) pipeline.append({'$sort': {'total': -1}}) - results = await db.aggregate(TelemetryEvent, pipeline) - results = await db.aggregate(TelemetryEvent, pipeline) return JSONResponse(content=jsonable_encoder([ { @@ -1842,6 +1840,22 @@ async def manage(): return PlainTextResponse(file.read(), headers=hdr) +@app.get('/analytics') +async def analytics_page(): + """Serve pipeline analytics dashboard with telemetry data""" + metrics.add('http_requests_total', 1) + root_dir = os.path.dirname(os.path.abspath(__file__)) + analytics_path = os.path.join(root_dir, 'templates', 'analytics.html') + with open(analytics_path, 'r', encoding='utf-8') as file: + hdr = { + 'Content-Type': 'text/html', + 'Cache-Control': 'no-cache, no-store, must-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0' + } + return PlainTextResponse(file.read(), headers=hdr) + + @app.get('/stats') async def stats_page(): """Serve simple HTML page to view infrastructure statistics""" diff --git a/api/static/__init__.py b/api/static/__init__.py new file mode 100644 index 00000000..b4480656 --- /dev/null +++ b/api/static/__init__.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2026 Collabora Limited +# Author: Denys Fedoryshchenko + +"""Static files package for KernelCI API viewer""" diff --git a/api/static/css/__init__.py b/api/static/css/__init__.py new file mode 100644 index 00000000..2c7a7dfa --- /dev/null +++ b/api/static/css/__init__.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2026 Collabora Limited +# Author: Denys Fedoryshchenko + +"""CSS files for KernelCI API viewer""" diff --git a/api/static/js/__init__.py b/api/static/js/__init__.py new file mode 100644 index 00000000..efa2956d --- /dev/null +++ b/api/static/js/__init__.py @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Copyright (C) 2026 Collabora Limited +# Author: Denys Fedoryshchenko + +"""JavaScript files for KernelCI API viewer""" diff --git a/api/templates/analytics.html b/api/templates/analytics.html new file mode 100644 index 00000000..2e5ba849 --- /dev/null +++ b/api/templates/analytics.html @@ -0,0 +1,744 @@ + + + + KernelCI Pipeline Analytics + + + + + + + + + + + + +
+ +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+ + +
+
+
+
-
+
Total Events
+
+
+
+
+
-
+
Pass
+
+
+
+
+
-
+
Fail
+
+
+
+
+
-
+
Infra Errors
+
+
+
+
+
-
+
Incomplete
+
+
+
+
+
-
+
Skip
+
+
+
+ + +
+
+
+
Anomaly Detection
+
+

Loading anomalies...

+
+
+
+
+ + +
+
+
+
Results Distribution
+
+ +
+
+
+
+
+
Events by Kind
+
+ +
+
+
+
+ + +
+
+
+
Stats by Runtime
+
+ + + + + + + + + + + + +
RuntimeTotalPassFailInfra ErrHealth
+
+
+
+
+
+
Stats by Tree
+
+ + + + + + + + + + + + +
TreeTotalPassFailInfra ErrHealth
+
+
+
+
+ + +
+
+
+
Stats by Device Type
+
+ + + + + + + + + + + + +
Device TypeTotalPassFailInfra ErrHealth
+
+
+
+
+
+
Recent Errors
+
+ + + + + + + + + + + +
TimeKindRuntimeError TypeMessage
+
+
+
+
+ + +
+
+
+
Runtime Health Overview
+
+ +
+
+
+
+
+ + + + + + + From 435365b7902ae01df39b792e107b035d2991e099 Mon Sep 17 00:00:00 2001 From: Denys Fedoryshchenko Date: Sun, 22 Feb 2026 11:46:34 +0200 Subject: [PATCH 2/2] analytics: Show event details Signed-off-by: Denys Fedoryshchenko --- api/templates/analytics.html | 290 ++++++++++++++++++++++++++++++++--- 1 file changed, 272 insertions(+), 18 deletions(-) diff --git a/api/templates/analytics.html b/api/templates/analytics.html index 2e5ba849..71383ae1 100644 --- a/api/templates/analytics.html +++ b/api/templates/analytics.html @@ -64,6 +64,43 @@ .nav-links a { color: rgba(255,255,255,0.8); text-decoration: none; margin-left: 16px; } .nav-links a:hover { color: white; } .chart-container { position: relative; height: 300px; } + .anomaly-alert { cursor: pointer; } + .anomaly-alert:hover { opacity: 0.9; } + .stats-clickable tr { cursor: pointer; } + .stats-clickable tr:hover { background-color: #e9ecef; } + .detail-panel { + background: white; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0,0,0,0.15); + padding: 24px; + margin-bottom: 24px; + border-left: 4px solid #667eea; + } + .detail-panel .detail-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 16px; + } + .detail-panel .detail-header h5 { margin: 0; } + .event-card { + background: #f8f9fa; + border-radius: 8px; + padding: 14px; + margin-bottom: 10px; + border: 1px solid #e9ecef; + } + .event-card:hover { border-color: #667eea; } + .event-field { margin-bottom: 4px; } + .event-field .field-name { + font-weight: 600; + color: #6c757d; + font-size: 0.8rem; + text-transform: uppercase; + } + .event-field .field-value { font-size: 0.9rem; } + .node-link { color: #667eea; text-decoration: underline; cursor: pointer; } + .node-link:hover { color: #764ba2; } @@ -311,6 +348,26 @@
Runtime Health Overview
+ + +