axiom-coding-agent-setup 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/agent-browser/SKILL.md +55 -0
- package/.agents/skills/developing-with-streamlit/SKILL.md +220 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-chat-ui/SKILL.md +195 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/SKILL.md +233 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/packaged-components.md +212 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/state-sync.md +149 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/theme-css-variables.md +243 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/troubleshooting.md +121 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-dashboards/SKILL.md +147 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-multipage-apps/SKILL.md +218 -0
- package/.agents/skills/developing-with-streamlit/skills/choosing-streamlit-selection-widgets/SKILL.md +139 -0
- package/.agents/skills/developing-with-streamlit/skills/connecting-streamlit-to-snowflake/SKILL.md +188 -0
- package/.agents/skills/developing-with-streamlit/skills/creating-streamlit-themes/SKILL.md +486 -0
- package/.agents/skills/developing-with-streamlit/skills/displaying-streamlit-data/SKILL.md +199 -0
- package/.agents/skills/developing-with-streamlit/skills/improving-streamlit-design/SKILL.md +191 -0
- package/.agents/skills/developing-with-streamlit/skills/optimizing-streamlit-performance/SKILL.md +323 -0
- package/.agents/skills/developing-with-streamlit/skills/organizing-streamlit-code/SKILL.md +91 -0
- package/.agents/skills/developing-with-streamlit/skills/setting-up-streamlit-environment/SKILL.md +128 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-cli/SKILL.md +165 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-custom-components/SKILL.md +170 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-layouts/SKILL.md +229 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-markdown/SKILL.md +207 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-session-state/SKILL.md +144 -0
- package/.agents/skills/developing-with-streamlit/templates/.gitattributes +3 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/README.md +188 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/streamlit_app.py +365 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/streamlit_app.py +461 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/streamlit_app.py +527 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/streamlit_app.py +307 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/streamlit_app.py +426 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/streamlit_app.py +463 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/streamlit_app.py +252 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/streamlit_app.py +342 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/streamlit_app.py +384 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/README.md +128 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/dracula.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/github.toml +37 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/minimal.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/nord.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/snowflake.toml +42 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/solarized-light.toml +38 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/spotify.toml +34 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/stripe.toml +35 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_shared/streamlit_app.py +336 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_templates/pyproject.toml.tmpl +12 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/.streamlit/config.toml +37 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/manage.py +332 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/.streamlit/config.toml +42 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/.streamlit/config.toml +38 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/.streamlit/config.toml +34 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/.streamlit/config.toml +35 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/streamlit_app.py +337 -0
- package/.agents/skills/fastapi/SKILL.md +436 -0
- package/.agents/skills/fastapi/references/dependencies.md +142 -0
- package/.agents/skills/fastapi/references/other-tools.md +76 -0
- package/.agents/skills/fastapi/references/streaming.md +105 -0
- package/.agents/skills/frontend-design/LICENSE.txt +177 -0
- package/.agents/skills/frontend-design/SKILL.md +42 -0
- package/.agents/skills/gradio/SKILL.md +84 -0
- package/.agents/skills/gradio/references/api-signatures.md +115 -0
- package/.agents/skills/gradio/references/event-listeners.md +130 -0
- package/.agents/skills/gradio/references/examples.md +613 -0
- package/.agents/skills/ui-ux-pro-max/SKILL.md +659 -0
- package/.agents/skills/ui-ux-pro-max/data +1 -0
- package/.agents/skills/ui-ux-pro-max/scripts +1 -0
- package/.env.axiom +8 -0
- package/AGENTS.md +2 -1
- package/README.md +19 -3
- package/bin/cli.js +11 -1
- package/opencode.json +7 -4
- package/package.json +1 -1
- package/skills-lock.json +36 -0
- package/.agents/skills/n8n-patterns/SKILL.md +0 -272
package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/streamlit_app.py
ADDED
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Metrics Dashboard Template
|
|
3
|
+
|
|
4
|
+
A comprehensive metrics dashboard demonstrating:
|
|
5
|
+
- Time series visualization with Altair (line, area, bar, point charts)
|
|
6
|
+
- Metric cards with chart/table toggle and popover filters
|
|
7
|
+
- Time range filtering (1M, 6M, 1Y, QTD, YTD, All)
|
|
8
|
+
- Line options (Daily, 7-day MA)
|
|
9
|
+
|
|
10
|
+
This template uses synthetic data. Replace the generate_*_data() functions
|
|
11
|
+
with your own data sources (e.g., Snowflake queries, APIs, etc.)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from datetime import date, timedelta
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pandas as pd
|
|
17
|
+
import streamlit as st
|
|
18
|
+
import altair as alt
|
|
19
|
+
|
|
20
|
+
st.set_page_config(
|
|
21
|
+
page_title="Metrics Dashboard",
|
|
22
|
+
page_icon=":material/monitoring:",
|
|
23
|
+
layout="wide",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# =============================================================================
|
|
28
|
+
# Constants
|
|
29
|
+
# =============================================================================
|
|
30
|
+
|
|
31
|
+
TIME_RANGES = ["1M", "6M", "1Y", "QTD", "YTD", "All"]
|
|
32
|
+
CHART_HEIGHT = 300
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# =============================================================================
|
|
36
|
+
# Synthetic Data Generation (Replace with your data source)
|
|
37
|
+
# =============================================================================
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def generate_metric_data(
|
|
41
|
+
metric_name: str,
|
|
42
|
+
start_date: date,
|
|
43
|
+
end_date: date,
|
|
44
|
+
base_value: float = 1000,
|
|
45
|
+
growth_rate: float = 0.001,
|
|
46
|
+
noise_factor: float = 0.1,
|
|
47
|
+
) -> pd.DataFrame:
|
|
48
|
+
"""Generate synthetic time series data for a metric.
|
|
49
|
+
|
|
50
|
+
Replace this function with your actual data source, e.g.:
|
|
51
|
+
- Snowflake query
|
|
52
|
+
- API call
|
|
53
|
+
- Database query
|
|
54
|
+
"""
|
|
55
|
+
np.random.seed(hash(metric_name) % 2**32)
|
|
56
|
+
|
|
57
|
+
dates = pd.date_range(start=start_date, end=end_date, freq="D")
|
|
58
|
+
n_days = len(dates)
|
|
59
|
+
|
|
60
|
+
# Base trend with growth
|
|
61
|
+
trend = base_value * (1 + growth_rate) ** np.arange(n_days)
|
|
62
|
+
|
|
63
|
+
# Add weekly seasonality (lower on weekends)
|
|
64
|
+
day_of_week = dates.dayofweek
|
|
65
|
+
seasonality = np.where(day_of_week >= 5, 0.7, 1.0)
|
|
66
|
+
trend = trend * seasonality
|
|
67
|
+
|
|
68
|
+
# Add noise
|
|
69
|
+
noise = np.random.normal(1, noise_factor, n_days)
|
|
70
|
+
values = trend * noise
|
|
71
|
+
|
|
72
|
+
# Calculate rolling averages
|
|
73
|
+
df = pd.DataFrame({
|
|
74
|
+
"ds": dates,
|
|
75
|
+
"daily_value": values,
|
|
76
|
+
})
|
|
77
|
+
df["value_7d_ma"] = df["daily_value"].rolling(7, min_periods=1).mean()
|
|
78
|
+
|
|
79
|
+
return df
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@st.cache_data(ttl=3600)
|
|
83
|
+
def load_all_metrics() -> dict[str, pd.DataFrame]:
|
|
84
|
+
"""Load all metrics data. Replace with your data loading logic."""
|
|
85
|
+
end_date = date.today() - timedelta(days=1)
|
|
86
|
+
start_date = end_date - timedelta(days=730) # 2 years of data
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
"users": generate_metric_data("users", start_date, end_date, base_value=5000, growth_rate=0.002),
|
|
90
|
+
"sessions": generate_metric_data("sessions", start_date, end_date, base_value=15000, growth_rate=0.003),
|
|
91
|
+
"revenue": generate_metric_data("revenue", start_date, end_date, base_value=50000, growth_rate=0.001),
|
|
92
|
+
"conversions": generate_metric_data("conversions", start_date, end_date, base_value=500, growth_rate=0.0015),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# =============================================================================
|
|
97
|
+
# Chart Utilities
|
|
98
|
+
# =============================================================================
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def filter_by_time_range(df: pd.DataFrame, x_col: str, time_range: str) -> pd.DataFrame:
|
|
102
|
+
"""Filter dataframe by time range."""
|
|
103
|
+
if time_range == "All" or df.empty:
|
|
104
|
+
return df
|
|
105
|
+
|
|
106
|
+
df = df.copy()
|
|
107
|
+
df[x_col] = pd.to_datetime(df[x_col])
|
|
108
|
+
max_date = df[x_col].max()
|
|
109
|
+
|
|
110
|
+
if time_range == "1M":
|
|
111
|
+
min_date = max_date - timedelta(days=30)
|
|
112
|
+
elif time_range == "6M":
|
|
113
|
+
min_date = max_date - timedelta(days=180)
|
|
114
|
+
elif time_range == "1Y":
|
|
115
|
+
min_date = max_date - timedelta(days=365)
|
|
116
|
+
elif time_range == "QTD":
|
|
117
|
+
quarter_month = ((max_date.month - 1) // 3) * 3 + 1
|
|
118
|
+
min_date = pd.Timestamp(date(max_date.year, quarter_month, 1))
|
|
119
|
+
elif time_range == "YTD":
|
|
120
|
+
min_date = pd.Timestamp(date(max_date.year, 1, 1))
|
|
121
|
+
else:
|
|
122
|
+
return df
|
|
123
|
+
|
|
124
|
+
return df[df[x_col] >= min_date]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def render_line_chart(
|
|
128
|
+
df: pd.DataFrame,
|
|
129
|
+
x_col: str,
|
|
130
|
+
y_cols: list[str],
|
|
131
|
+
labels: list[str],
|
|
132
|
+
height: int = CHART_HEIGHT,
|
|
133
|
+
) -> alt.Chart:
|
|
134
|
+
"""Render a multi-line chart."""
|
|
135
|
+
# Melt for Altair
|
|
136
|
+
melted = df.melt(
|
|
137
|
+
id_vars=[x_col],
|
|
138
|
+
value_vars=y_cols,
|
|
139
|
+
var_name="series",
|
|
140
|
+
value_name="value",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Map to labels
|
|
144
|
+
label_map = dict(zip(y_cols, labels))
|
|
145
|
+
melted["series"] = melted["series"].map(label_map)
|
|
146
|
+
|
|
147
|
+
chart = (
|
|
148
|
+
alt.Chart(melted)
|
|
149
|
+
.mark_line()
|
|
150
|
+
.encode(
|
|
151
|
+
x=alt.X(f"{x_col}:T", title=None),
|
|
152
|
+
y=alt.Y("value:Q", title=None, scale=alt.Scale(zero=False)),
|
|
153
|
+
color=alt.Color("series:N", title=None, legend=alt.Legend(orient="bottom")),
|
|
154
|
+
strokeDash=alt.condition(
|
|
155
|
+
alt.datum.series == "7-day MA",
|
|
156
|
+
alt.value([5, 5]),
|
|
157
|
+
alt.value([0]),
|
|
158
|
+
),
|
|
159
|
+
tooltip=[
|
|
160
|
+
alt.Tooltip(f"{x_col}:T", title="Date", format="%Y-%m-%d"),
|
|
161
|
+
alt.Tooltip("series:N", title="Series"),
|
|
162
|
+
alt.Tooltip("value:Q", title="Value", format=",.0f"),
|
|
163
|
+
],
|
|
164
|
+
)
|
|
165
|
+
.properties(height=height)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return chart
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def render_area_chart(
|
|
172
|
+
df: pd.DataFrame,
|
|
173
|
+
x_col: str,
|
|
174
|
+
y_cols: list[str],
|
|
175
|
+
labels: list[str],
|
|
176
|
+
height: int = CHART_HEIGHT,
|
|
177
|
+
) -> alt.Chart:
|
|
178
|
+
"""Render a stacked area chart."""
|
|
179
|
+
melted = df.melt(
|
|
180
|
+
id_vars=[x_col],
|
|
181
|
+
value_vars=y_cols,
|
|
182
|
+
var_name="series",
|
|
183
|
+
value_name="value",
|
|
184
|
+
)
|
|
185
|
+
label_map = dict(zip(y_cols, labels))
|
|
186
|
+
melted["series"] = melted["series"].map(label_map)
|
|
187
|
+
|
|
188
|
+
chart = (
|
|
189
|
+
alt.Chart(melted)
|
|
190
|
+
.mark_area(opacity=0.6, line=True)
|
|
191
|
+
.encode(
|
|
192
|
+
x=alt.X(f"{x_col}:T", title=None),
|
|
193
|
+
y=alt.Y("value:Q", title=None, scale=alt.Scale(zero=False)),
|
|
194
|
+
color=alt.Color("series:N", title=None, legend=alt.Legend(orient="bottom")),
|
|
195
|
+
tooltip=[
|
|
196
|
+
alt.Tooltip(f"{x_col}:T", title="Date", format="%Y-%m-%d"),
|
|
197
|
+
alt.Tooltip("series:N", title="Series"),
|
|
198
|
+
alt.Tooltip("value:Q", title="Value", format=",.0f"),
|
|
199
|
+
],
|
|
200
|
+
)
|
|
201
|
+
.properties(height=height)
|
|
202
|
+
)
|
|
203
|
+
return chart
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def render_bar_chart(
|
|
207
|
+
df: pd.DataFrame,
|
|
208
|
+
x_col: str,
|
|
209
|
+
y_cols: list[str],
|
|
210
|
+
labels: list[str],
|
|
211
|
+
height: int = CHART_HEIGHT,
|
|
212
|
+
) -> alt.Chart:
|
|
213
|
+
"""Render a bar chart (weekly aggregation for readability)."""
|
|
214
|
+
df = df.copy()
|
|
215
|
+
df[x_col] = pd.to_datetime(df[x_col])
|
|
216
|
+
df["week"] = df[x_col].dt.to_period("W").dt.start_time
|
|
217
|
+
|
|
218
|
+
# Aggregate by week
|
|
219
|
+
agg_df = df.groupby("week")[y_cols].mean().reset_index()
|
|
220
|
+
|
|
221
|
+
melted = agg_df.melt(
|
|
222
|
+
id_vars=["week"],
|
|
223
|
+
value_vars=y_cols,
|
|
224
|
+
var_name="series",
|
|
225
|
+
value_name="value",
|
|
226
|
+
)
|
|
227
|
+
label_map = dict(zip(y_cols, labels))
|
|
228
|
+
melted["series"] = melted["series"].map(label_map)
|
|
229
|
+
|
|
230
|
+
chart = (
|
|
231
|
+
alt.Chart(melted)
|
|
232
|
+
.mark_bar(opacity=0.8)
|
|
233
|
+
.encode(
|
|
234
|
+
x=alt.X("week:T", title=None),
|
|
235
|
+
y=alt.Y("value:Q", title=None, scale=alt.Scale(zero=False)),
|
|
236
|
+
color=alt.Color("series:N", title=None, legend=alt.Legend(orient="bottom")),
|
|
237
|
+
xOffset="series:N",
|
|
238
|
+
tooltip=[
|
|
239
|
+
alt.Tooltip("week:T", title="Week", format="%Y-%m-%d"),
|
|
240
|
+
alt.Tooltip("series:N", title="Series"),
|
|
241
|
+
alt.Tooltip("value:Q", title="Value", format=",.0f"),
|
|
242
|
+
],
|
|
243
|
+
)
|
|
244
|
+
.properties(height=height)
|
|
245
|
+
)
|
|
246
|
+
return chart
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def render_point_chart(
|
|
250
|
+
df: pd.DataFrame,
|
|
251
|
+
x_col: str,
|
|
252
|
+
y_cols: list[str],
|
|
253
|
+
labels: list[str],
|
|
254
|
+
height: int = CHART_HEIGHT,
|
|
255
|
+
) -> alt.Chart:
|
|
256
|
+
"""Render a scatter/point chart with trend line."""
|
|
257
|
+
melted = df.melt(
|
|
258
|
+
id_vars=[x_col],
|
|
259
|
+
value_vars=y_cols,
|
|
260
|
+
var_name="series",
|
|
261
|
+
value_name="value",
|
|
262
|
+
)
|
|
263
|
+
label_map = dict(zip(y_cols, labels))
|
|
264
|
+
melted["series"] = melted["series"].map(label_map)
|
|
265
|
+
|
|
266
|
+
points = (
|
|
267
|
+
alt.Chart(melted)
|
|
268
|
+
.mark_point(opacity=0.5, size=20)
|
|
269
|
+
.encode(
|
|
270
|
+
x=alt.X(f"{x_col}:T", title=None),
|
|
271
|
+
y=alt.Y("value:Q", title=None, scale=alt.Scale(zero=False)),
|
|
272
|
+
color=alt.Color("series:N", title=None, legend=alt.Legend(orient="bottom")),
|
|
273
|
+
tooltip=[
|
|
274
|
+
alt.Tooltip(f"{x_col}:T", title="Date", format="%Y-%m-%d"),
|
|
275
|
+
alt.Tooltip("series:N", title="Series"),
|
|
276
|
+
alt.Tooltip("value:Q", title="Value", format=",.0f"),
|
|
277
|
+
],
|
|
278
|
+
)
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Add trend line for 7-day MA only
|
|
282
|
+
trend = (
|
|
283
|
+
alt.Chart(melted[melted["series"] == "7-day MA"])
|
|
284
|
+
.mark_line(strokeDash=[5, 5], strokeWidth=2)
|
|
285
|
+
.encode(
|
|
286
|
+
x=alt.X(f"{x_col}:T"),
|
|
287
|
+
y=alt.Y("value:Q"),
|
|
288
|
+
color=alt.Color("series:N"),
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
return (points + trend).properties(height=height)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
# =============================================================================
|
|
296
|
+
# Metric Card Component
|
|
297
|
+
# =============================================================================
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def metric_card(
|
|
301
|
+
title: str,
|
|
302
|
+
df: pd.DataFrame,
|
|
303
|
+
key_prefix: str,
|
|
304
|
+
chart_type: str = "line",
|
|
305
|
+
):
|
|
306
|
+
"""Display a metric card with chart/table toggle and popover filters.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
title: Card title
|
|
310
|
+
df: DataFrame with ds, daily_value, value_7d_ma columns
|
|
311
|
+
key_prefix: Unique prefix for widget keys
|
|
312
|
+
chart_type: One of "line", "area", "bar", "point"
|
|
313
|
+
"""
|
|
314
|
+
chart_renderers = {
|
|
315
|
+
"line": render_line_chart,
|
|
316
|
+
"area": render_area_chart,
|
|
317
|
+
"bar": render_bar_chart,
|
|
318
|
+
"point": render_point_chart,
|
|
319
|
+
}
|
|
320
|
+
render_chart = chart_renderers.get(chart_type, render_line_chart)
|
|
321
|
+
|
|
322
|
+
with st.container(border=True):
|
|
323
|
+
# Header row with title, view toggle, and filters
|
|
324
|
+
with st.container(
|
|
325
|
+
horizontal=True,
|
|
326
|
+
horizontal_alignment="distribute",
|
|
327
|
+
vertical_alignment="center",
|
|
328
|
+
):
|
|
329
|
+
st.markdown(f"**{title}**")
|
|
330
|
+
|
|
331
|
+
view_mode = st.segmented_control(
|
|
332
|
+
"View",
|
|
333
|
+
options=[":material/show_chart:", ":material/table:"],
|
|
334
|
+
default=":material/show_chart:",
|
|
335
|
+
key=f"{key_prefix}_view",
|
|
336
|
+
label_visibility="collapsed",
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
with st.popover("Filters", type="tertiary"):
|
|
340
|
+
line_options = st.pills(
|
|
341
|
+
"Lines",
|
|
342
|
+
options=["Daily", "7-day MA"],
|
|
343
|
+
default=["Daily", "7-day MA"],
|
|
344
|
+
selection_mode="multi",
|
|
345
|
+
key=f"{key_prefix}_lines",
|
|
346
|
+
)
|
|
347
|
+
time_range = st.segmented_control(
|
|
348
|
+
"Time range",
|
|
349
|
+
options=TIME_RANGES,
|
|
350
|
+
default="All",
|
|
351
|
+
key=f"{key_prefix}_time",
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
# Apply filters
|
|
355
|
+
line_options = line_options or ["7-day MA"]
|
|
356
|
+
filtered_df = filter_by_time_range(df, "ds", time_range)
|
|
357
|
+
|
|
358
|
+
# Determine which columns to show
|
|
359
|
+
y_cols = []
|
|
360
|
+
labels = []
|
|
361
|
+
if "Daily" in line_options:
|
|
362
|
+
y_cols.append("daily_value")
|
|
363
|
+
labels.append("Daily")
|
|
364
|
+
if "7-day MA" in line_options:
|
|
365
|
+
y_cols.append("value_7d_ma")
|
|
366
|
+
labels.append("7-day MA")
|
|
367
|
+
|
|
368
|
+
# Render view
|
|
369
|
+
if "table" in (view_mode or ""):
|
|
370
|
+
st.dataframe(
|
|
371
|
+
filtered_df,
|
|
372
|
+
height=CHART_HEIGHT,
|
|
373
|
+
hide_index=True,
|
|
374
|
+
)
|
|
375
|
+
else:
|
|
376
|
+
if y_cols:
|
|
377
|
+
st.altair_chart(
|
|
378
|
+
render_chart(filtered_df, "ds", y_cols, labels),
|
|
379
|
+
)
|
|
380
|
+
else:
|
|
381
|
+
st.info("Select at least one line option.")
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# =============================================================================
|
|
385
|
+
# Page Header Component
|
|
386
|
+
# =============================================================================
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def render_page_header(title: str):
|
|
390
|
+
"""Render page header with title and reset button."""
|
|
391
|
+
with st.container(
|
|
392
|
+
horizontal=True, horizontal_alignment="distribute", vertical_alignment="center"
|
|
393
|
+
):
|
|
394
|
+
st.markdown(title)
|
|
395
|
+
if st.button(":material/restart_alt: Reset", type="tertiary"):
|
|
396
|
+
st.session_state.clear()
|
|
397
|
+
st.rerun()
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
# =============================================================================
|
|
401
|
+
# Page Layout
|
|
402
|
+
# =============================================================================
|
|
403
|
+
|
|
404
|
+
# Load data (cached)
|
|
405
|
+
metrics_data = load_all_metrics()
|
|
406
|
+
|
|
407
|
+
# Page header
|
|
408
|
+
render_page_header("# :material/monitoring: Metrics Dashboard")
|
|
409
|
+
|
|
410
|
+
# Row 1: Users and Sessions
|
|
411
|
+
row1 = st.columns(2)
|
|
412
|
+
|
|
413
|
+
with row1[0]:
|
|
414
|
+
metric_card("Active Users", metrics_data["users"], "users", chart_type="line")
|
|
415
|
+
|
|
416
|
+
with row1[1]:
|
|
417
|
+
metric_card("Sessions", metrics_data["sessions"], "sessions", chart_type="area")
|
|
418
|
+
|
|
419
|
+
# Row 2: Revenue and Conversions
|
|
420
|
+
row2 = st.columns(2)
|
|
421
|
+
|
|
422
|
+
with row2[0]:
|
|
423
|
+
metric_card("Revenue", metrics_data["revenue"], "revenue", chart_type="bar")
|
|
424
|
+
|
|
425
|
+
with row2[1]:
|
|
426
|
+
metric_card("Conversions", metrics_data["conversions"], "conversions", chart_type="point")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Copy this file to secrets.toml and fill in your credentials
|
|
2
|
+
# NEVER commit secrets.toml to version control — it contains sensitive
|
|
3
|
+
# credentials that would be exposed in your repository.
|
|
4
|
+
#
|
|
5
|
+
# To find your account and host values, run:
|
|
6
|
+
# snow connection list
|
|
7
|
+
# Look for the "account" and "host" fields in your connection config.
|
|
8
|
+
|
|
9
|
+
[connections.snowflake]
|
|
10
|
+
account = "<YOUR_ACCOUNT>" # e.g., "ORGNAME-ACCTNAME" from `snow connection list`
|
|
11
|
+
host = "<YOUR_HOST>" # e.g., "myaccount.snowflakecomputing.com" (required for private link / internal accounts)
|
|
12
|
+
user = "<YOUR_USER>"
|
|
13
|
+
authenticator = "externalbrowser" # Opens browser for SSO login
|
|
14
|
+
warehouse = "<YOUR_WAREHOUSE>"
|
|
15
|
+
database = "<YOUR_DATABASE>"
|
|
16
|
+
schema = "<YOUR_SCHEMA>"
|
|
17
|
+
|
|
18
|
+
# Alternative: use password instead of SSO (less secure)
|
|
19
|
+
# password = "<YOUR_PASSWORD>"
|
|
20
|
+
# Remove the authenticator line above if using password
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "dashboard-metrics-snowflake"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "A metrics dashboard template using Snowflake for data storage and retrieval"
|
|
5
|
+
requires-python = ">=3.11"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"altair>=5.5.0",
|
|
8
|
+
"pandas>=2.2.3",
|
|
9
|
+
"snowflake-connector-python>=3.3.0",
|
|
10
|
+
"streamlit[snowflake]>=1.54.0",
|
|
11
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
definition_version: 2
|
|
2
|
+
entities:
|
|
3
|
+
DASHBOARD_METRICS_SNOWFLAKE:
|
|
4
|
+
type: streamlit
|
|
5
|
+
identifier:
|
|
6
|
+
name: DASHBOARD_METRICS_SNOWFLAKE
|
|
7
|
+
database: <FROM_CONNECTION> # Use: snow connection list
|
|
8
|
+
schema: <FROM_CONNECTION>
|
|
9
|
+
query_warehouse: <FROM_CONNECTION>
|
|
10
|
+
runtime_name: SYSTEM$ST_CONTAINER_RUNTIME_PY3_11
|
|
11
|
+
# List available: SHOW EXTERNAL ACCESS INTEGRATIONS;
|
|
12
|
+
# Common: PYPI_ACCESS_INTEGRATION (verify exists)
|
|
13
|
+
external_access_integrations:
|
|
14
|
+
- <YOUR_PYPI_INTEGRATION>
|
|
15
|
+
main_file: streamlit_app.py
|
|
16
|
+
artifacts:
|
|
17
|
+
- streamlit_app.py
|
|
18
|
+
- pyproject.toml
|