-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathapp-core.py
86 lines (73 loc) · 2.9 KB
/
app-core.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
from __future__ import annotations
import plotly.graph_objects as go
from shared import df, plot_timeseries, value_box_server, value_box_ui
from shiny import App, Inputs, Outputs, Session, reactive, ui
from shinywidgets import output_widget, render_plotly
all_models = ["model_1", "model_2", "model_3", "model_4"]
app_ui = ui.page_sidebar(
ui.sidebar(
ui.input_checkbox_group("models", "Models", all_models, selected=all_models)
),
ui.layout_columns(
value_box_ui("model_1", "Model 1"),
value_box_ui("model_2", "Model 2"),
value_box_ui("model_3", "Model 3"),
value_box_ui("model_4", "Model 4"),
fill=False,
id="value-boxes",
),
ui.card(
ui.card_header(
"Model accuracy over time",
ui.input_switch("pause", "Pause updates", value=False, width="auto"),
class_="d-flex justify-content-between",
),
output_widget("plot"),
full_screen=True,
),
title="Model monitoring dashboard",
class_="bslib-page-dashboard",
)
def server(input: Inputs, output: Outputs, session: Session):
# Note that df from shared.py is a reactive calc that gets
# invalidated (approximately) when the database updates
# We can choose to ignore the invalidation by doing an isolated read
@reactive.calc
def maybe_paused_df():
if not input.pause():
return df()
with reactive.isolate():
return df()
# Source the value box module server code for each model
for model in all_models:
value_box_server(model, maybe_paused_df, model)
# Create an empty plotly figure on page load
@render_plotly
def plot():
return go.FigureWidget()
# Update the plotly figure with the latest data
@reactive.effect
def _():
d = maybe_paused_df()
d = d[d["model"].isin(input.models())]
with plot.widget.batch_animate():
fig = plot_timeseries(d)
plot.widget.update(layout=fig.layout, data=fig.data)
# Hacky way to hide/show model value boxes. This is currently the only real
# option you want the value box UI to be statically rendered (thus, reducing
# flicker on update), but also want to hide/show them based on user input.
@reactive.effect
@reactive.event(input.models)
def _():
ui.remove_ui("#value-box-hide") # Remove any previously added style tag
# Construct CSS to hide the value boxes that the user has deselected
css = ""
missing_models = list(set(all_models) - set(input.models()))
for model in missing_models:
i = all_models.index(model) + 1
css += f"#value-boxes > *:nth-child({i}) {{ display: none; }}"
# Add the CSS to the head of the document
if css:
style = ui.tags.style(css, id="value-box-hide")
ui.insert_ui(style, selector="head")
app = App(app_ui, server)