hopefully fixed flickering on hf_spaces that does not show locally
Browse files
app.py
CHANGED
|
@@ -94,6 +94,14 @@ _CHART_TYPES = [
|
|
| 94 |
]
|
| 95 |
|
| 96 |
_PALETTE_NAMES = ["Set2", "Dark2", "Set1", "Paired", "Pastel1", "Pastel2", "Accent"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
|
| 99 |
# ---------------------------------------------------------------------------
|
|
@@ -111,6 +119,34 @@ def _load_demo(path: Path) -> pd.DataFrame:
|
|
| 111 |
return pd.read_csv(path)
|
| 112 |
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
@st.cache_data(show_spinner=False)
|
| 115 |
def _clean_pipeline(_raw_hash, raw_df, date_col, y_cols, dup_action, missing_action):
|
| 116 |
cleaned, report = clean_dataframe(raw_df, date_col, list(y_cols),
|
|
@@ -720,6 +756,8 @@ with st.sidebar:
|
|
| 720 |
["(none)"] + list(_DEMO_FILES.keys()),
|
| 721 |
key="demo_select",
|
| 722 |
)
|
|
|
|
|
|
|
| 723 |
|
| 724 |
# Load data
|
| 725 |
def _on_new_data(df: pd.DataFrame) -> None:
|
|
@@ -921,13 +959,6 @@ with st.sidebar:
|
|
| 921 |
"(natural-language data filtering)."
|
| 922 |
)
|
| 923 |
|
| 924 |
-
# Reset button
|
| 925 |
-
st.divider()
|
| 926 |
-
if st.button("Reset all"):
|
| 927 |
-
for k in list(st.session_state.keys()):
|
| 928 |
-
del st.session_state[k]
|
| 929 |
-
st.rerun()
|
| 930 |
-
|
| 931 |
st.divider()
|
| 932 |
st.caption(
|
| 933 |
"**Privacy:** All processing is in-memory. "
|
|
@@ -1037,31 +1068,42 @@ else:
|
|
| 1037 |
_data_quality_fragment(report)
|
| 1038 |
|
| 1039 |
# ---------------------------------------------------------------------------
|
| 1040 |
-
#
|
| 1041 |
# ---------------------------------------------------------------------------
|
| 1042 |
-
|
| 1043 |
-
"
|
| 1044 |
-
|
| 1045 |
-
|
| 1046 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1047 |
|
| 1048 |
# ===================================================================
|
| 1049 |
# Tab A β Single Series
|
| 1050 |
# ===================================================================
|
| 1051 |
-
|
| 1052 |
_single_chart_fragment(working_df, date_col, y_cols, freq_info, style_dict)
|
| 1053 |
_single_insights_fragment(freq_info, date_col)
|
| 1054 |
|
| 1055 |
# ===================================================================
|
| 1056 |
# Tab B β Few Series (Panel)
|
| 1057 |
# ===================================================================
|
| 1058 |
-
|
| 1059 |
_panel_chart_fragment(working_df, date_col, y_cols, style_dict)
|
| 1060 |
_panel_insights_fragment(working_df, date_col, freq_info)
|
| 1061 |
|
| 1062 |
# ===================================================================
|
| 1063 |
# Tab C β Many Series (Spaghetti)
|
| 1064 |
# ===================================================================
|
| 1065 |
-
|
| 1066 |
_spaghetti_chart_fragment(working_df, date_col, y_cols, style_dict)
|
| 1067 |
_spaghetti_insights_fragment(working_df, date_col, freq_info)
|
|
|
|
| 94 |
]
|
| 95 |
|
| 96 |
_PALETTE_NAMES = ["Set2", "Dark2", "Set1", "Paired", "Pastel1", "Pastel2", "Accent"]
|
| 97 |
+
_VIEW_SPECS = [
|
| 98 |
+
("Single Series", "single"),
|
| 99 |
+
("Few Series (Panel)", "panel"),
|
| 100 |
+
("Many Series (Spaghetti)", "spaghetti"),
|
| 101 |
+
]
|
| 102 |
+
_VIEW_LABELS = [label for label, _ in _VIEW_SPECS]
|
| 103 |
+
_VIEW_SLUG_BY_LABEL = dict(_VIEW_SPECS)
|
| 104 |
+
_VIEW_LABEL_BY_SLUG = {slug: label for label, slug in _VIEW_SPECS}
|
| 105 |
|
| 106 |
|
| 107 |
# ---------------------------------------------------------------------------
|
|
|
|
| 119 |
return pd.read_csv(path)
|
| 120 |
|
| 121 |
|
| 122 |
+
def _scalar_query_param(value):
|
| 123 |
+
"""Return the first item for multi-valued query params."""
|
| 124 |
+
if isinstance(value, list):
|
| 125 |
+
return value[0] if value else None
|
| 126 |
+
return value
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def _initial_view_label() -> str:
|
| 130 |
+
"""Resolve initial view from query params when available."""
|
| 131 |
+
requested = _scalar_query_param(st.query_params.get("view"))
|
| 132 |
+
return _VIEW_LABEL_BY_SLUG.get(requested, _VIEW_LABELS[0])
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def _reset_all_state() -> None:
|
| 136 |
+
"""Clear all session/query state and rerun."""
|
| 137 |
+
for key in list(st.session_state.keys()):
|
| 138 |
+
del st.session_state[key]
|
| 139 |
+
st.query_params.clear()
|
| 140 |
+
st.rerun()
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def _sync_view_query_param() -> None:
|
| 144 |
+
"""Write current active view to URL query params."""
|
| 145 |
+
active = st.session_state.get("active_view")
|
| 146 |
+
if active in _VIEW_SLUG_BY_LABEL:
|
| 147 |
+
st.query_params["view"] = _VIEW_SLUG_BY_LABEL[active]
|
| 148 |
+
|
| 149 |
+
|
| 150 |
@st.cache_data(show_spinner=False)
|
| 151 |
def _clean_pipeline(_raw_hash, raw_df, date_col, y_cols, dup_action, missing_action):
|
| 152 |
cleaned, report = clean_dataframe(raw_df, date_col, list(y_cols),
|
|
|
|
| 756 |
["(none)"] + list(_DEMO_FILES.keys()),
|
| 757 |
key="demo_select",
|
| 758 |
)
|
| 759 |
+
if st.button("Reset all", key="reset_sidebar", use_container_width=True):
|
| 760 |
+
_reset_all_state()
|
| 761 |
|
| 762 |
# Load data
|
| 763 |
def _on_new_data(df: pd.DataFrame) -> None:
|
|
|
|
| 959 |
"(natural-language data filtering)."
|
| 960 |
)
|
| 961 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 962 |
st.divider()
|
| 963 |
st.caption(
|
| 964 |
"**Privacy:** All processing is in-memory. "
|
|
|
|
| 1068 |
_data_quality_fragment(report)
|
| 1069 |
|
| 1070 |
# ---------------------------------------------------------------------------
|
| 1071 |
+
# View selector
|
| 1072 |
# ---------------------------------------------------------------------------
|
| 1073 |
+
if "active_view" not in st.session_state:
|
| 1074 |
+
st.session_state["active_view"] = _initial_view_label()
|
| 1075 |
+
|
| 1076 |
+
view_col, reset_col = st.columns([6, 1])
|
| 1077 |
+
with view_col:
|
| 1078 |
+
active_view = st.radio(
|
| 1079 |
+
"View",
|
| 1080 |
+
_VIEW_LABELS,
|
| 1081 |
+
key="active_view",
|
| 1082 |
+
horizontal=True,
|
| 1083 |
+
label_visibility="collapsed",
|
| 1084 |
+
on_change=_sync_view_query_param,
|
| 1085 |
+
)
|
| 1086 |
+
with reset_col:
|
| 1087 |
+
if st.button("Reset all", key="reset_main", use_container_width=True):
|
| 1088 |
+
_reset_all_state()
|
| 1089 |
|
| 1090 |
# ===================================================================
|
| 1091 |
# Tab A β Single Series
|
| 1092 |
# ===================================================================
|
| 1093 |
+
if active_view == "Single Series":
|
| 1094 |
_single_chart_fragment(working_df, date_col, y_cols, freq_info, style_dict)
|
| 1095 |
_single_insights_fragment(freq_info, date_col)
|
| 1096 |
|
| 1097 |
# ===================================================================
|
| 1098 |
# Tab B β Few Series (Panel)
|
| 1099 |
# ===================================================================
|
| 1100 |
+
elif active_view == "Few Series (Panel)":
|
| 1101 |
_panel_chart_fragment(working_df, date_col, y_cols, style_dict)
|
| 1102 |
_panel_insights_fragment(working_df, date_col, freq_info)
|
| 1103 |
|
| 1104 |
# ===================================================================
|
| 1105 |
# Tab C β Many Series (Spaghetti)
|
| 1106 |
# ===================================================================
|
| 1107 |
+
else:
|
| 1108 |
_spaghetti_chart_fragment(working_df, date_col, y_cols, style_dict)
|
| 1109 |
_spaghetti_insights_fragment(working_df, date_col, freq_info)
|