-
Notifications
You must be signed in to change notification settings - Fork 6
/
dashboard.py
159 lines (129 loc) · 4.36 KB
/
dashboard.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""
A dashboard to visualize metrics and alerts.
Run locally with:
$ streamlit run dashboard.py
"""
import pandas as pd
import plotly.graph_objs as go
import streamlit as st
from dotenv import load_dotenv
from plotly.subplots import make_subplots
from anomstack.config import specs
from anomstack.jinja.render import render
from anomstack.sql.read import read_sql
load_dotenv()
st.set_page_config(layout="wide")
def plot_time_series(df, metric_name) -> go.Figure:
"""
Plot a time series with metric value and metric score.
"""
# Create a figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])
# Add traces/lines for metric_value, metric_score, and metric_alert
fig.add_trace(
go.Scatter(x=df["metric_timestamp"], y=df["metric_value"], name="Metric Value"),
secondary_y=False,
)
fig.add_trace(
go.Scatter(
x=df["metric_timestamp"],
y=df["metric_score"],
name="Metric Score",
line=dict(dash="dot"),
),
secondary_y=True,
)
alert_df = df[df["metric_alert"] == 1]
if not alert_df.empty:
fig.add_trace(
go.Scatter(
x=alert_df["metric_timestamp"],
y=alert_df["metric_alert"],
mode="markers",
name="Metric Alert",
marker=dict(color="red", size=5),
),
secondary_y=True,
)
change_df = df[df["metric_change"] == 1]
if not change_df.empty:
fig.add_trace(
go.Scatter(
x=change_df["metric_timestamp"],
y=change_df["metric_change"],
mode="markers",
name="Metric Change",
marker=dict(color="orange", size=5),
),
secondary_y=True,
)
# Update x-axis and y-axes to remove gridlines, set the y-axis range
# for metric score, and format as percentage
fig.update_xaxes(showgrid=False, zeroline=False)
fig.update_yaxes(showgrid=False, zeroline=False, secondary_y=False)
fig.update_yaxes(
showgrid=False,
zeroline=False,
range=[0, 1.1],
tickformat=".0%",
secondary_y=True,
)
# Set x-axis title
fig.update_xaxes(title_text="Timestamp")
# Set y-axes titles
fig.update_yaxes(title_text="Metric Value", secondary_y=False)
fig.update_yaxes(title_text="Metric Score", secondary_y=True)
# Move legend to the top of the plot
fig.update_layout(
title_text=f"{metric_name} (n={len(df)})",
hovermode="x",
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1),
autosize=True,
)
return fig
@st.cache_data(ttl=60)
def get_data(sql: str, db: str) -> pd.DataFrame:
"""
Get data from the database.
"""
df = read_sql(sql, db=db)
return df
# Streamlit app
custom_css = """<style>a {text-decoration: none;}</style>"""
st.markdown(custom_css, unsafe_allow_html=True)
st.title("[Anomstack](https://github.com/andrewm4894/anomstack) Metrics Visualization")
# get metric batches
metric_batches = list(specs.keys())
# inputs
last_n = st.sidebar.number_input("Last N:", min_value=1, value=5000)
batch_selection = st.sidebar.selectbox("Metric Batch:", metric_batches)
# get data
sql = render("dashboard_sql", specs[batch_selection], params={"alert_max_n": last_n})
db = specs[batch_selection]["db"]
df = get_data(sql, db)
# data based inputs
metric_names = ["ALL"]
unique_metrics = sorted(
list(df[df["metric_batch"] == batch_selection]["metric_name"].unique())
)
metric_names.extend(unique_metrics)
metric_selection = st.sidebar.selectbox("Metric Name:", metric_names)
# filter data and plot
if metric_selection == "ALL":
for metric in unique_metrics:
filtered_df = df[
(df["metric_batch"] == batch_selection) & (df["metric_name"] == metric)
].sort_values(by="metric_timestamp")
# plot
fig = plot_time_series(filtered_df, metric)
st.plotly_chart(fig, use_container_width=True)
else:
filtered_df = df[
(df["metric_batch"] == batch_selection)
& (df["metric_name"] == metric_selection)
].sort_values(by="metric_timestamp")
# plot
fig = plot_time_series(filtered_df, metric_selection)
st.plotly_chart(fig, use_container_width=True)
with st.expander("Show SQL Query"):
st.text(sql)