dashboard/serve.py

116 lines
4.1 KiB
Python

import os
import dash
import pandas as pd
import plotly.express as px
from plotly.subplots import make_subplots
from dash import html, dcc, Input, Output, dash_table, clientside_callback
from datetime import datetime
from zoneinfo import ZoneInfo # This is for Python 3.9 and later
from flask import request
tz = 'America/Los_Angeles'
local_tz = ZoneInfo(tz) # Change this to your timezone, e.g., 'Europe/London', 'Asia/Tokyo'
# Load your Excel file
file_path = "buildkite_benchmarks.xlsx"
df = pd.read_excel(file_path)
metrics = [col for col in df.columns if df[col].dtype in ['float64', 'int64']]
timestamp = os.path.getmtime(file_path)
last_modified_time = datetime.fromtimestamp(timestamp).astimezone(local_tz)
readable_time = last_modified_time.strftime('%Y-%m-%d %H:%M:%S') + ' ' + tz
# Create a column with the commit link as text (since Plotly hover can't render HTML)
df['commit_link'] = df['commit_url'].apply(lambda x: f"URL: {x}")
# Create a Dash application
app = dash.Dash(__name__)
def create_metric_figure(metric):
# Create a subplot figure for each metric, as there is only one metric per plot here
fig = make_subplots(rows=1, cols=1, shared_xaxes=True)
trace = px.line(
df,
x='build_datetime',
y=metric,
title=f'Trend of {metric}',
text='commit_link' # This is where the hover text comes from
).data[0]
fig.add_trace(trace, row=1, col=1)
fig.update_traces(
mode='markers+lines',
hovertemplate='Time:%{x}<br>Value: %{y}</br><extra></extra>' # Customize hover text
)
fig.update_layout(
height=300, # Fixed height as each metric is in a separate plot now
title_text=f"Performance Metrics for {metric}",
hovermode='closest'
)
return fig
# Dropdown to select the metric for plotting
app.layout = html.Div([
html.H1('Performance Metrics Over Time'),
dcc.Markdown("Click on data point to show commit url.", id='url-click-output'), # Use dcc.Markdown to handle HTML
dcc.Dropdown(
id='metric-select',
options=[{'label': metric, 'value': metric} for metric in metrics],
value=metrics,
multi=True
),
# Generate a plot for each metric, hidden by default
html.Div([dcc.Graph(id=f'graph-{metric}', figure=create_metric_figure(metric))
for metric in metrics]),
dcc.Store(id='plot-visibility'),
html.Div(f"Last Updated: {readable_time}", style={'position': 'fixed', 'bottom': '10px', 'right': '10px'}),
])
clientside_callback(
"""
function(selectedMetrics, ...plotIds) {
// Loop through all plot IDs and set visibility
plotIds.forEach((plotId) => {
const plotElement = document.getElementById(plotId);
if (plotElement) {
plotElement.style.display = selectedMetrics.includes(plotId.replace('graph-', '')) ? 'block' : 'none';
}
});
return null;
}
""",
Output('plot-visibility', 'data'),
Input('metric-select', 'value'),
state=[Input(f'graph-{metric}', 'id') for metric in metrics] # Passing plot IDs as state
)
for metric in metrics:
clientside_callback(
"""
function(clickData) {
if (clickData) {
var url = clickData.points[0].text.split("URL: ")[1];
return `[Open Commit ${url}](${url})`;
}
return "Click on a point to see the URL.";
}
""",
Output('url-click-output', 'children', allow_duplicate=True),
Input(f'graph-{metric}', 'clickData'),
prevent_initial_call=True,
)
# Flask request logging
@app.server.before_request
def log_request():
# This will print the request details before each request is processed.
if request.path != '/_reload-hash':
print(f"Request received at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Request path: {request.path}")
print(f"Request method: {request.method}")
print(f"Request headers: {request.headers}")
print(f"Request body: {request.get_data(as_text=True)}")
if __name__ == '__main__':
app.run_server(debug=True)