Remove type hints for state to bypass Gradio schema error
Browse files
app.py
CHANGED
|
@@ -39,7 +39,7 @@ from visualizations import (
|
|
| 39 |
)
|
| 40 |
|
| 41 |
|
| 42 |
-
|
| 43 |
|
| 44 |
|
| 45 |
def _format_overview_text(info: Dict[str, Any], source_name: str) -> str:
|
|
@@ -58,12 +58,12 @@ def _empty_dataframe(message: str = "No data available") -> pd.DataFrame:
|
|
| 58 |
return pd.DataFrame({"status": [message]})
|
| 59 |
|
| 60 |
|
| 61 |
-
def _ensure_state(state
|
| 62 |
"""Guarantee a dictionary-based state object."""
|
| 63 |
return state or {}
|
| 64 |
|
| 65 |
|
| 66 |
-
def _current_dataframe(state
|
| 67 |
"""Return the filtered or raw dataframe from state."""
|
| 68 |
key = "filtered_df" if filtered else "dataframe"
|
| 69 |
df = state.get(key)
|
|
@@ -72,7 +72,7 @@ def _current_dataframe(state: DatasetState, filtered: bool = True) -> pd.DataFra
|
|
| 72 |
raise ValueError("Please upload a dataset before performing this action.")
|
| 73 |
|
| 74 |
|
| 75 |
-
def _finalize_dataset_load(bundle: DatasetBundle, state
|
| 76 |
"""Populate shared outputs after a dataset is loaded."""
|
| 77 |
df = bundle.dataframe
|
| 78 |
state = {
|
|
@@ -97,7 +97,7 @@ def _finalize_dataset_load(bundle: DatasetBundle, state: DatasetState) -> Tuple[
|
|
| 97 |
return state, status, info_text, dtypes_df, head_df, tail_df, filter_preview, row_count
|
| 98 |
|
| 99 |
|
| 100 |
-
def _handle_file_upload(file, state
|
| 101 |
"""Load a dataset from the uploaded file."""
|
| 102 |
state = _ensure_state(state)
|
| 103 |
try:
|
|
@@ -117,7 +117,7 @@ def _handle_file_upload(file, state: Optional[DatasetState]) -> Tuple[DatasetSta
|
|
| 117 |
return _finalize_dataset_load(bundle, state)
|
| 118 |
|
| 119 |
|
| 120 |
-
def _handle_sample_dataset(selection: Optional[str], state
|
| 121 |
"""Load one of the bundled sample datasets."""
|
| 122 |
state = _ensure_state(state)
|
| 123 |
if not selection:
|
|
@@ -135,7 +135,7 @@ def _handle_sample_dataset(selection: Optional[str], state: Optional[DatasetStat
|
|
| 135 |
|
| 136 |
|
| 137 |
def _populate_column_options(
|
| 138 |
-
state
|
| 139 |
) -> Tuple[Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any]:
|
| 140 |
"""Populate dropdown choices based on the uploaded dataset."""
|
| 141 |
state = _ensure_state(state)
|
|
@@ -198,7 +198,7 @@ def _populate_column_options(
|
|
| 198 |
)
|
| 199 |
|
| 200 |
|
| 201 |
-
def _update_numeric_inputs(column: Optional[str], state
|
| 202 |
"""Update numeric min/max inputs when a column is selected."""
|
| 203 |
state = _ensure_state(state)
|
| 204 |
hidden = gr.update(visible=False, value=None)
|
|
@@ -215,7 +215,7 @@ def _update_numeric_inputs(column: Optional[str], state: Optional[DatasetState])
|
|
| 215 |
)
|
| 216 |
|
| 217 |
|
| 218 |
-
def _update_categorical_values(column: Optional[str], state
|
| 219 |
"""Populate categorical value options for filtering."""
|
| 220 |
state = _ensure_state(state)
|
| 221 |
if not column or "filter_meta" not in state:
|
|
@@ -230,7 +230,7 @@ def _update_categorical_values(column: Optional[str], state: Optional[DatasetSta
|
|
| 230 |
)
|
| 231 |
|
| 232 |
|
| 233 |
-
def _update_date_bounds(column: Optional[str], state
|
| 234 |
"""Populate date inputs when a date column is selected."""
|
| 235 |
state = _ensure_state(state)
|
| 236 |
if not column or "filter_meta" not in state:
|
|
@@ -249,7 +249,7 @@ def _update_date_bounds(column: Optional[str], state: Optional[DatasetState]) ->
|
|
| 249 |
|
| 250 |
|
| 251 |
def _apply_filters(
|
| 252 |
-
state
|
| 253 |
numeric_column: Optional[str],
|
| 254 |
numeric_min: Optional[float],
|
| 255 |
numeric_max: Optional[float],
|
|
@@ -258,7 +258,7 @@ def _apply_filters(
|
|
| 258 |
date_column: Optional[str],
|
| 259 |
start_date: Optional[str],
|
| 260 |
end_date: Optional[str],
|
| 261 |
-
) -> Tuple[
|
| 262 |
"""Filter the dataset according to user selections."""
|
| 263 |
state = _ensure_state(state)
|
| 264 |
df = _current_dataframe(state, filtered=False)
|
|
@@ -288,7 +288,7 @@ def _apply_filters(
|
|
| 288 |
return state, preview, row_count
|
| 289 |
|
| 290 |
|
| 291 |
-
def _generate_statistics(state
|
| 292 |
"""Produce summary statistics for the Statistics tab."""
|
| 293 |
state = _ensure_state(state)
|
| 294 |
try:
|
|
@@ -313,7 +313,7 @@ def _generate_statistics(state: Optional[DatasetState]) -> Tuple[pd.DataFrame, p
|
|
| 313 |
|
| 314 |
|
| 315 |
def _generate_chart(
|
| 316 |
-
state
|
| 317 |
chart_type: str,
|
| 318 |
ts_date: Optional[str],
|
| 319 |
ts_value: Optional[str],
|
|
@@ -362,7 +362,7 @@ def _generate_chart(
|
|
| 362 |
return fig, fig, "Visualization generated."
|
| 363 |
|
| 364 |
|
| 365 |
-
def _download_filtered(state
|
| 366 |
"""Export the filtered dataset to a temporary CSV file."""
|
| 367 |
state = _ensure_state(state)
|
| 368 |
df = _current_dataframe(state, filtered=True)
|
|
@@ -387,7 +387,7 @@ def _download_chart(fig: Optional[go.Figure]) -> str:
|
|
| 387 |
|
| 388 |
|
| 389 |
def _generate_insights(
|
| 390 |
-
state
|
| 391 |
numeric_column: Optional[str],
|
| 392 |
trend_date_column: Optional[str],
|
| 393 |
trend_value_column: Optional[str],
|
|
|
|
| 39 |
)
|
| 40 |
|
| 41 |
|
| 42 |
+
|
| 43 |
|
| 44 |
|
| 45 |
def _format_overview_text(info: Dict[str, Any], source_name: str) -> str:
|
|
|
|
| 58 |
return pd.DataFrame({"status": [message]})
|
| 59 |
|
| 60 |
|
| 61 |
+
def _ensure_state(state) -> Dict[str, Any]:
|
| 62 |
"""Guarantee a dictionary-based state object."""
|
| 63 |
return state or {}
|
| 64 |
|
| 65 |
|
| 66 |
+
def _current_dataframe(state, filtered: bool = True) -> pd.DataFrame:
|
| 67 |
"""Return the filtered or raw dataframe from state."""
|
| 68 |
key = "filtered_df" if filtered else "dataframe"
|
| 69 |
df = state.get(key)
|
|
|
|
| 72 |
raise ValueError("Please upload a dataset before performing this action.")
|
| 73 |
|
| 74 |
|
| 75 |
+
def _finalize_dataset_load(bundle: DatasetBundle, state) -> Tuple[Dict[str, Any], str, str, pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame, str]:
|
| 76 |
"""Populate shared outputs after a dataset is loaded."""
|
| 77 |
df = bundle.dataframe
|
| 78 |
state = {
|
|
|
|
| 97 |
return state, status, info_text, dtypes_df, head_df, tail_df, filter_preview, row_count
|
| 98 |
|
| 99 |
|
| 100 |
+
def _handle_file_upload(file, state) -> Tuple[Dict[str, Any], str, str, pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame, str]:
|
| 101 |
"""Load a dataset from the uploaded file."""
|
| 102 |
state = _ensure_state(state)
|
| 103 |
try:
|
|
|
|
| 117 |
return _finalize_dataset_load(bundle, state)
|
| 118 |
|
| 119 |
|
| 120 |
+
def _handle_sample_dataset(selection: Optional[str], state) -> Tuple[Dict[str, Any], str, str, pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame, str]:
|
| 121 |
"""Load one of the bundled sample datasets."""
|
| 122 |
state = _ensure_state(state)
|
| 123 |
if not selection:
|
|
|
|
| 135 |
|
| 136 |
|
| 137 |
def _populate_column_options(
|
| 138 |
+
state,
|
| 139 |
) -> Tuple[Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any]:
|
| 140 |
"""Populate dropdown choices based on the uploaded dataset."""
|
| 141 |
state = _ensure_state(state)
|
|
|
|
| 198 |
)
|
| 199 |
|
| 200 |
|
| 201 |
+
def _update_numeric_inputs(column: Optional[str], state) -> Tuple[Any, Any]:
|
| 202 |
"""Update numeric min/max inputs when a column is selected."""
|
| 203 |
state = _ensure_state(state)
|
| 204 |
hidden = gr.update(visible=False, value=None)
|
|
|
|
| 215 |
)
|
| 216 |
|
| 217 |
|
| 218 |
+
def _update_categorical_values(column: Optional[str], state):
|
| 219 |
"""Populate categorical value options for filtering."""
|
| 220 |
state = _ensure_state(state)
|
| 221 |
if not column or "filter_meta" not in state:
|
|
|
|
| 230 |
)
|
| 231 |
|
| 232 |
|
| 233 |
+
def _update_date_bounds(column: Optional[str], state) -> Tuple[Any, Any]:
|
| 234 |
"""Populate date inputs when a date column is selected."""
|
| 235 |
state = _ensure_state(state)
|
| 236 |
if not column or "filter_meta" not in state:
|
|
|
|
| 249 |
|
| 250 |
|
| 251 |
def _apply_filters(
|
| 252 |
+
state,
|
| 253 |
numeric_column: Optional[str],
|
| 254 |
numeric_min: Optional[float],
|
| 255 |
numeric_max: Optional[float],
|
|
|
|
| 258 |
date_column: Optional[str],
|
| 259 |
start_date: Optional[str],
|
| 260 |
end_date: Optional[str],
|
| 261 |
+
) -> Tuple[Dict[str, Any], pd.DataFrame, str]:
|
| 262 |
"""Filter the dataset according to user selections."""
|
| 263 |
state = _ensure_state(state)
|
| 264 |
df = _current_dataframe(state, filtered=False)
|
|
|
|
| 288 |
return state, preview, row_count
|
| 289 |
|
| 290 |
|
| 291 |
+
def _generate_statistics(state) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame, str]:
|
| 292 |
"""Produce summary statistics for the Statistics tab."""
|
| 293 |
state = _ensure_state(state)
|
| 294 |
try:
|
|
|
|
| 313 |
|
| 314 |
|
| 315 |
def _generate_chart(
|
| 316 |
+
state,
|
| 317 |
chart_type: str,
|
| 318 |
ts_date: Optional[str],
|
| 319 |
ts_value: Optional[str],
|
|
|
|
| 362 |
return fig, fig, "Visualization generated."
|
| 363 |
|
| 364 |
|
| 365 |
+
def _download_filtered(state) -> str:
|
| 366 |
"""Export the filtered dataset to a temporary CSV file."""
|
| 367 |
state = _ensure_state(state)
|
| 368 |
df = _current_dataframe(state, filtered=True)
|
|
|
|
| 387 |
|
| 388 |
|
| 389 |
def _generate_insights(
|
| 390 |
+
state,
|
| 391 |
numeric_column: Optional[str],
|
| 392 |
trend_date_column: Optional[str],
|
| 393 |
trend_value_column: Optional[str],
|