Download app.py from Teoman21/BID: direct link, hf CLI and curl.
- Browser
- Download file 30.4 kB
-
https://huggingface.co/spaces/Teoman21/BID/resolve/main/app.py
- Command line
-
hf download hf://spaces/Teoman21/BID/app.py
-
curl -L -o app.py https://huggingface.co/spaces/Teoman21/BID/resolve/main/app.py
30.4 kB
| """Gradio application wiring for the Business Intelligence dashboard.""" | |
| from __future__ import annotations | |
| import tempfile | |
| from typing import Any, Dict, Iterable, List, Optional, Tuple | |
| # Monkey-patch to fix gradio_client TypeError bug with boolean additionalProperties | |
| # This must be done BEFORE importing gradio | |
| import gradio_client.utils as _gc_utils | |
| _original_json_schema_to_python_type = _gc_utils._json_schema_to_python_type | |
| def _patched_json_schema_to_python_type(schema, defs=None): | |
| """Patched version that handles boolean schema values.""" | |
| # Handle boolean schemas (e.g., additionalProperties: true/false) | |
| if isinstance(schema, bool): | |
| return "Any" if schema else "None" | |
| return _original_json_schema_to_python_type(schema, defs) | |
| _gc_utils._json_schema_to_python_type = _patched_json_schema_to_python_type | |
| import gradio as gr | |
| import pandas as pd | |
| import matplotlib.figure as mpl_fig | |
| from data_processor import ( | |
| DatasetBundle, | |
| dataset_overview, | |
| dataset_preview, | |
| filter_dataframe, | |
| filter_metadata, | |
| load_dataset, | |
| load_sample_dataset, | |
| missing_value_report, | |
| numeric_summary, | |
| categorical_summary, | |
| correlation_matrix, | |
| sample_dataset_options, | |
| ) | |
| from insights import ( | |
| detect_anomalies, | |
| detect_trend, | |
| get_default_insight_columns, | |
| top_bottom_performers, | |
| ) | |
| from visualizations import ( | |
| create_category_plot, | |
| create_correlation_heatmap, | |
| create_distribution_plot, | |
| create_scatter_plot, | |
| create_time_series_plot, | |
| figure_to_png_bytes, | |
| ) | |
| def _format_overview_text(info: Dict[str, Any], source_name: str) -> str: | |
| """Render dataset information as Markdown.""" | |
| lines = [ | |
| f"**Source:** {source_name}", | |
| f"- Rows: {info['Rows']}", | |
| f"- Columns: {info['Columns']}", | |
| f"- Memory Usage: {info['Memory Usage (MB)']} MB", | |
| ] | |
| return "\n".join(lines) | |
| def _empty_dataframe(message: str = "No data available") -> pd.DataFrame: | |
| """Return a placeholder DataFrame for empty displays.""" | |
| return pd.DataFrame({"status": [message]}) | |
| DEFAULT_STATE = { | |
| "dataframe": None, | |
| "filtered_df": None, | |
| "column_types": None, | |
| "filter_meta": None, | |
| "source_name": None, | |
| "current_figure": None, | |
| } | |
| def _ensure_state(state) -> Dict[str, Any]: | |
| """Guarantee a dictionary-based state object.""" | |
| return state or DEFAULT_STATE.copy() | |
| def _current_dataframe(state, filtered: bool = True) -> pd.DataFrame: | |
| """Return the filtered or raw dataframe from state.""" | |
| state = _ensure_state(state) | |
| key = "filtered_df" if filtered else "dataframe" | |
| df = state.get(key) | |
| if isinstance(df, pd.DataFrame): | |
| return df | |
| raise ValueError("Please upload a dataset before performing this action.") | |
| def _finalize_dataset_load(bundle, state): | |
| """Populate shared outputs after a dataset is loaded.""" | |
| df = bundle.dataframe | |
| state = { | |
| "dataframe": df, | |
| "filtered_df": df, | |
| "column_types": { | |
| "numeric": bundle.column_types.numeric, | |
| "categorical": bundle.column_types.categorical, | |
| "datetime": bundle.column_types.datetime, | |
| }, | |
| "filter_meta": filter_metadata(df, bundle.column_types), | |
| "source_name": bundle.source_name, | |
| } | |
| overview = dataset_overview(df) | |
| preview = dataset_preview(df) | |
| status = f"✅ Loaded '{bundle.source_name}' with {df.shape[0]} rows and {df.shape[1]} columns." | |
| info_text = _format_overview_text(overview["info"], bundle.source_name) | |
| dtypes_df = overview["dtypes"] | |
| head_df = preview["head"] | |
| tail_df = preview["tail"] | |
| filter_preview = head_df | |
| row_count = f"Rows displayed: {len(df)}" | |
| return state, status, info_text, dtypes_df, head_df, tail_df, filter_preview, row_count | |
| def _handle_file_upload(file, state): | |
| """Load a dataset from the uploaded file.""" | |
| state = _ensure_state(state) | |
| try: | |
| bundle = load_dataset(file) | |
| except ValueError as exc: | |
| return ( | |
| state, | |
| f"❌ {exc}", | |
| "No dataset loaded.", | |
| _empty_dataframe(), | |
| _empty_dataframe(), | |
| _empty_dataframe(), | |
| _empty_dataframe(), | |
| "Rows displayed: 0", | |
| ) | |
| return _finalize_dataset_load(bundle, state) | |
| def _handle_sample_dataset(selection: Optional[str], state): | |
| """Load one of the bundled sample datasets.""" | |
| state = _ensure_state(state) | |
| if not selection: | |
| message = "Please choose a sample dataset before loading." | |
| empty = _empty_dataframe(message) | |
| return state, f"⚠️ {message}", "No dataset loaded.", empty, empty, empty, empty, "Rows displayed: 0" | |
| try: | |
| bundle = load_sample_dataset(selection) | |
| except ValueError as exc: | |
| empty = _empty_dataframe(str(exc)) | |
| return state, f"❌ {exc}", "No dataset loaded.", empty, empty, empty, empty, "Rows displayed: 0" | |
| return _finalize_dataset_load(bundle, state) | |
| def _populate_column_options( | |
| state, | |
| ): | |
| """Populate dropdown choices based on the uploaded dataset.""" | |
| state = _ensure_state(state) | |
| column_types = state.get("column_types") | |
| if not column_types: | |
| empty_dropdown = gr.update(choices=[], value=None, interactive=False, visible=True) | |
| hidden_checkbox = gr.update(choices=[], value=[], visible=False, interactive=False) | |
| return ( | |
| empty_dropdown, | |
| empty_dropdown, | |
| hidden_checkbox, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| empty_dropdown, | |
| ) | |
| numeric = list(column_types["numeric"]) | |
| categorical = list(column_types["categorical"]) | |
| datetime_cols = list(column_types["datetime"]) | |
| all_columns = list(state["dataframe"].columns) | |
| defaults = { | |
| "numeric": numeric[0] if numeric else None, | |
| "datetime": datetime_cols[0] if datetime_cols else None, | |
| } | |
| def dropdown(values: Iterable[str], default: Optional[str] = None): | |
| choices = list(values) | |
| value = default if default in choices else None | |
| return gr.update( | |
| choices=choices, | |
| value=value, | |
| interactive=bool(choices), | |
| visible=True, | |
| ) | |
| return ( | |
| dropdown(numeric), # numeric filter column | |
| dropdown(datetime_cols), # date filter column | |
| gr.update(choices=[], value=[], visible=False, interactive=False), # categorical values reset | |
| dropdown(categorical), # categorical filter column | |
| dropdown(all_columns, defaults.get("datetime")), # time series date | |
| dropdown(numeric, defaults.get("numeric")), # time series value | |
| dropdown(numeric), # distribution numeric | |
| dropdown(categorical), # category column | |
| dropdown(numeric), # category value | |
| dropdown(numeric), # scatter x | |
| dropdown(numeric), # scatter y | |
| gr.update(choices=all_columns, value=None, interactive=bool(all_columns), visible=True), # scatter color | |
| dropdown(numeric, defaults.get("numeric")), # insight numeric | |
| dropdown(datetime_cols, defaults.get("datetime")), # insight datetime | |
| dropdown(numeric, defaults.get("numeric")), # trend value | |
| dropdown(numeric, defaults.get("numeric")), # anomaly column | |
| ) | |
| def _update_numeric_inputs(column: Optional[str], state) -> Tuple[Any, Any]: | |
| """Update numeric min/max inputs when a column is selected.""" | |
| state = _ensure_state(state) | |
| hidden = gr.update(visible=False, value=None) | |
| if not column or "filter_meta" not in state: | |
| return hidden, hidden | |
| meta = state["filter_meta"]["numeric"].get(column) | |
| if not meta: | |
| return hidden, hidden | |
| minimum = float(meta["min"]) | |
| maximum = float(meta["max"]) | |
| return ( | |
| gr.update(value=minimum, visible=True, interactive=True, label=f"Min ({column})"), | |
| gr.update(value=maximum, visible=True, interactive=True, label=f"Max ({column})"), | |
| ) | |
| def _update_categorical_values(column: Optional[str], state): | |
| """Populate categorical value options for filtering.""" | |
| state = _ensure_state(state) | |
| if not column or "filter_meta" not in state: | |
| return gr.update(visible=False) | |
| values = state["filter_meta"]["categorical"].get(column, []) | |
| return gr.update( | |
| choices=values, | |
| value=values[: min(10, len(values))], | |
| visible=bool(values), | |
| interactive=bool(values), | |
| label=f"Values to include ({column})", | |
| ) | |
| def _update_date_bounds(column: Optional[str], state) -> Tuple[Any, Any]: | |
| """Populate date inputs when a date column is selected.""" | |
| state = _ensure_state(state) | |
| if not column or "filter_meta" not in state: | |
| hidden = gr.update(visible=False, value=None) | |
| return hidden, hidden | |
| meta = state["filter_meta"]["datetime"].get(column) | |
| if not meta: | |
| hidden = gr.update(visible=False, value=None) | |
| return hidden, hidden | |
| start = str(meta["min"]) | |
| end = str(meta["max"]) | |
| return ( | |
| gr.update(value=start, visible=True, label=f"Start date ({column})"), | |
| gr.update(value=end, visible=True, label=f"End date ({column})"), | |
| ) | |
| def _apply_filters( | |
| state, | |
| numeric_column: Optional[str], | |
| numeric_min: Optional[float], | |
| numeric_max: Optional[float], | |
| categorical_column: Optional[str], | |
| categorical_values: Optional[List[str]], | |
| date_column: Optional[str], | |
| start_date: Optional[str], | |
| end_date: Optional[str], | |
| ) -> Tuple[Dict[str, Any], pd.DataFrame, str]: | |
| """Filter the dataset according to user selections.""" | |
| state = _ensure_state(state) | |
| df = _current_dataframe(state, filtered=False) | |
| numeric_filters: Dict[str, Tuple[Optional[float], Optional[float]]] = {} | |
| categorical_filters: Dict[str, List[str]] = {} | |
| date_filters: Dict[str, Tuple[Optional[str], Optional[str]]] = {} | |
| if numeric_column and (numeric_min is not None or numeric_max is not None): | |
| lower = numeric_min | |
| upper = numeric_max | |
| if lower is not None and upper is not None and lower > upper: | |
| lower, upper = upper, lower | |
| numeric_filters[numeric_column] = (lower, upper) | |
| if categorical_column and categorical_values: | |
| categorical_filters[categorical_column] = categorical_values | |
| if date_column and (start_date or end_date): | |
| date_filters[date_column] = (start_date, end_date) | |
| filtered_df = filter_dataframe(df, numeric_filters, categorical_filters, date_filters) | |
| state["filtered_df"] = filtered_df | |
| row_count = f"Rows displayed: {len(filtered_df)}" | |
| preview = filtered_df.head(5) if not filtered_df.empty else _empty_dataframe("No rows match the filters.") | |
| return state, preview, row_count | |
| def _generate_statistics(state) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame, str]: | |
| """Produce summary statistics for the Statistics tab.""" | |
| state = _ensure_state(state) | |
| try: | |
| df = _current_dataframe(state, filtered=False) | |
| except ValueError as exc: | |
| message = str(exc) | |
| empty = _empty_dataframe(message) | |
| return empty, empty, empty, empty, f"⚠️ {message}" | |
| num_summary = numeric_summary(df) | |
| cat_summary = categorical_summary(df) | |
| missing = missing_value_report(df) | |
| corr = correlation_matrix(df) | |
| message = "Statistics generated successfully." | |
| return ( | |
| num_summary if not num_summary.empty else _empty_dataframe("No numeric columns available."), | |
| cat_summary if not cat_summary.empty else _empty_dataframe("No categorical columns available."), | |
| missing if not missing.empty else _empty_dataframe("No missing values detected."), | |
| corr if not corr.empty else _empty_dataframe("Not enough numeric columns for correlation."), | |
| message, | |
| ) | |
| def _generate_chart( | |
| state, | |
| chart_type: str, | |
| ts_date: Optional[str], | |
| ts_value: Optional[str], | |
| ts_agg: str, | |
| dist_column: Optional[str], | |
| dist_type: str, | |
| cat_column: Optional[str], | |
| cat_value: Optional[str], | |
| cat_chart_type: str, | |
| cat_agg: str, | |
| scatter_x: Optional[str], | |
| scatter_y: Optional[str], | |
| scatter_color: Optional[str], | |
| ) -> Tuple[Dict[str, Any], Any, str]: | |
| """Create a visualization based on user selections.""" | |
| state = _ensure_state(state) | |
| try: | |
| df = _current_dataframe(state, filtered=True) | |
| except ValueError as exc: | |
| state["current_figure"] = None | |
| return state, None, f"⚠️ {exc}" | |
| try: | |
| if chart_type == "Time Series": | |
| if not ts_date or not ts_value: | |
| raise ValueError("Select both a date and value column.") | |
| fig = create_time_series_plot(df, ts_date, ts_value, aggregation=ts_agg) | |
| elif chart_type == "Distribution": | |
| if not dist_column: | |
| raise ValueError("Select a numeric column for the distribution plot.") | |
| fig = create_distribution_plot(df, dist_column, plot_type=dist_type) | |
| elif chart_type == "Category": | |
| if not cat_column or not cat_value: | |
| raise ValueError("Select both category and value columns.") | |
| fig = create_category_plot(df, cat_column, cat_value, aggregation=cat_agg, chart_type=cat_chart_type.lower()) | |
| elif chart_type == "Scatter": | |
| if not scatter_x or not scatter_y: | |
| raise ValueError("Select x and y columns for the scatter plot.") | |
| fig = create_scatter_plot(df, scatter_x, scatter_y, color_column=scatter_color) | |
| elif chart_type == "Correlation Heatmap": | |
| fig = create_correlation_heatmap(df) | |
| else: | |
| raise ValueError("Unsupported chart type.") | |
| except ValueError as exc: | |
| state["current_figure"] = None | |
| return state, None, f"⚠️ {exc}" | |
| state["current_figure"] = fig | |
| return state, fig, "✅ Visualization generated. Use 'Export Chart' to download." | |
| def _download_filtered(state) -> str: | |
| """Export the filtered dataset to a temporary CSV file.""" | |
| state = _ensure_state(state) | |
| df = _current_dataframe(state, filtered=True) | |
| if df.empty: | |
| raise ValueError("There are no rows to export. Adjust your filters and try again.") | |
| temp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv", prefix="filtered_", dir=".") | |
| df.to_csv(temp.name, index=False) | |
| temp.close() | |
| return temp.name | |
| def _download_chart(state) -> str: | |
| """Export the most recent chart to PNG.""" | |
| state = _ensure_state(state) | |
| fig = state.get("current_figure") | |
| if fig is None: | |
| raise ValueError("Generate a visualization before exporting.") | |
| buffer = figure_to_png_bytes(fig) | |
| temp = tempfile.NamedTemporaryFile(delete=False, suffix=".png", prefix="chart_", dir=".") | |
| with open(temp.name, "wb") as fp: | |
| fp.write(buffer.read()) | |
| return temp.name | |
| def _generate_insights( | |
| state, | |
| numeric_column: Optional[str], | |
| trend_date_column: Optional[str], | |
| trend_value_column: Optional[str], | |
| anomaly_column: Optional[str], | |
| ) -> Tuple[pd.DataFrame, pd.DataFrame, str, pd.DataFrame, str]: | |
| """Generate top/bottom performers, trends, and anomalies.""" | |
| state = _ensure_state(state) | |
| try: | |
| df = _current_dataframe(state, filtered=True) | |
| except ValueError as exc: | |
| empty = _empty_dataframe(str(exc)) | |
| return empty, empty, f"⚠️ {exc}", empty, f"⚠️ {exc}" | |
| status_messages: List[str] = [] | |
| top_df = bottom_df = _empty_dataframe("Select a numeric column for insights.") | |
| if numeric_column: | |
| try: | |
| performers = top_bottom_performers(df, numeric_column) | |
| top_df = performers["top"] | |
| bottom_df = performers["bottom"] | |
| status_messages.append(f"Top/bottom performers calculated for {numeric_column}.") | |
| except ValueError as exc: | |
| top_df = bottom_df = _empty_dataframe(str(exc)) | |
| status_messages.append(f"⚠️ {exc}") | |
| trend_text = "Select a date and value column to evaluate trend." | |
| if trend_date_column and trend_value_column: | |
| try: | |
| trend_text = detect_trend(df, trend_date_column, trend_value_column) | |
| except ValueError as exc: | |
| trend_text = f"⚠️ {exc}" | |
| anomaly_df = _empty_dataframe("Select a numeric column to detect anomalies.") | |
| if anomaly_column: | |
| anomalies = detect_anomalies(df, anomaly_column) | |
| anomaly_df = anomalies if not anomalies.empty else _empty_dataframe("No significant anomalies detected.") | |
| combined_status = "\n".join(status_messages) if status_messages else "Insights generated." | |
| return top_df, bottom_df, trend_text, anomaly_df, combined_status | |
| def _describe_sample_dataset(selection: Optional[str]) -> str: | |
| """Return a user-friendly description for the selected sample dataset.""" | |
| if not selection: | |
| return "Select a sample dataset to view its description." | |
| descriptions = sample_dataset_options() | |
| description = descriptions.get(selection) | |
| if not description: | |
| return "Sample dataset description unavailable. Ensure the file exists in the `data/` directory." | |
| return f"**{selection}**\n\n{description}" | |
| def create_dashboard(): | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# Business Intelligence Dashboard") | |
| dataset_state = gr.State(DEFAULT_STATE.copy()) | |
| #last_figure_state = gr.State(None) | |
| sample_choices = list(sample_dataset_options().keys()) | |
| with gr.Tab("Data Upload"): | |
| with gr.Row(): | |
| file_input = gr.File(label="Upload CSV or Excel", file_types=[".csv", ".xlsx", ".xls"]) | |
| load_button = gr.Button("Load Data", variant="primary") | |
| gr.Markdown("Or load one of the curated datasets bundled with the project:") | |
| with gr.Row(): | |
| sample_dropdown = gr.Dropdown(label="Sample Dataset", choices=sample_choices, value=None, interactive=bool(sample_choices)) | |
| load_sample_button = gr.Button("Load Sample", variant="secondary", interactive=bool(sample_choices)) | |
| if sample_choices: | |
| sample_description = gr.Markdown("Select a sample dataset to view its description.") | |
| else: | |
| sample_description = gr.Markdown("⚠️ No sample datasets detected in the `data/` folder.") | |
| upload_status = gr.Markdown("No dataset loaded.") | |
| dataset_info = gr.Markdown() | |
| dtypes_table = gr.Dataframe(label="Column Types", interactive=False) | |
| with gr.Row(): | |
| head_table = gr.Dataframe(label="Preview (Head)", interactive=False) | |
| tail_table = gr.Dataframe(label="Preview (Tail)", interactive=False) | |
| with gr.Tab("Statistics"): | |
| stats_status = gr.Markdown() | |
| numeric_table = gr.Dataframe(label="Numeric Summary", interactive=False) | |
| categorical_table = gr.Dataframe(label="Categorical Summary", interactive=False) | |
| missing_table = gr.Dataframe(label="Missing Value Report", interactive=False) | |
| correlation_table = gr.Dataframe(label="Correlation Matrix", interactive=False) | |
| generate_stats_button = gr.Button("Generate Statistics", variant="secondary") | |
| with gr.Tab("Filter & Explore"): | |
| filter_status = gr.Markdown("Rows displayed: 0") | |
| with gr.Accordion("Numeric Filter", open=False): | |
| numeric_column_dropdown = gr.Dropdown(label="Numeric Column", choices=[]) | |
| numeric_min_input = gr.Number(label="Minimum Value", visible=False) | |
| numeric_max_input = gr.Number(label="Maximum Value", visible=False) | |
| with gr.Accordion("Categorical Filter", open=False): | |
| categorical_column_dropdown = gr.Dropdown(label="Category Column", choices=[]) | |
| categorical_values = gr.CheckboxGroup(label="Values", choices=[], visible=False) | |
| with gr.Accordion("Date Filter", open=False): | |
| date_column_dropdown = gr.Dropdown(label="Date Column", choices=[]) | |
| start_date_picker = gr.Textbox(label="Start Date (YYYY-MM-DD)", visible=False) | |
| end_date_picker = gr.Textbox(label="End Date (YYYY-MM-DD)", visible=False) | |
| apply_filters_button = gr.Button("Apply Filters", variant="primary") | |
| filter_preview_table = gr.Dataframe(label="Filtered Preview", interactive=False) | |
| export_filtered_button = gr.Button("Download Filtered Data", variant="secondary") | |
| export_filtered_file = gr.File(label="Filtered CSV", interactive=False) | |
| with gr.Tab("Visualizations"): | |
| viz_status = gr.Markdown() | |
| chart_type = gr.Radio( | |
| label="Chart Type", | |
| choices=["Time Series", "Distribution", "Category", "Scatter", "Correlation Heatmap"], | |
| value="Time Series", | |
| ) | |
| with gr.Column(visible=True) as time_series_controls: | |
| ts_date_column = gr.Dropdown(label="Date Column", choices=[]) | |
| ts_value_column = gr.Dropdown(label="Value Column", choices=[]) | |
| ts_aggregation = gr.Dropdown(label="Aggregation", choices=["sum", "mean", "median", "count"], value="sum") | |
| with gr.Column(visible=False) as distribution_controls: | |
| dist_column = gr.Dropdown(label="Numeric Column", choices=[]) | |
| dist_type = gr.Radio(label="Distribution Type", choices=["histogram", "box"], value="histogram") | |
| with gr.Column(visible=False) as category_controls: | |
| category_column = gr.Dropdown(label="Category Column", choices=[]) | |
| category_value_column = gr.Dropdown(label="Value Column", choices=[]) | |
| category_chart_type = gr.Radio(label="Chart Style", choices=["Bar", "Pie"], value="Bar") | |
| category_aggregation = gr.Dropdown(label="Aggregation", choices=["sum", "mean", "median", "count"], value="sum") | |
| with gr.Column(visible=False) as scatter_controls: | |
| scatter_x_column = gr.Dropdown(label="X Axis", choices=[]) | |
| scatter_y_column = gr.Dropdown(label="Y Axis", choices=[]) | |
| scatter_color_column = gr.Dropdown(label="Color (optional)", choices=[]) | |
| with gr.Row(): | |
| generate_chart_button = gr.Button("Generate Visualization", variant="primary") | |
| export_chart_button = gr.Button("Export Chart (PNG)", variant="secondary") | |
| chart_output = gr.Plot(label="Visualization") | |
| export_chart_file = gr.File(label="Exported Chart", interactive=False) | |
| with gr.Tab("Insights"): | |
| insights_status = gr.Markdown() | |
| insight_numeric_column = gr.Dropdown(label="Numeric Column", choices=[]) | |
| trend_date_column = gr.Dropdown(label="Date Column", choices=[]) | |
| trend_value_column = gr.Dropdown(label="Value Column", choices=[]) | |
| anomaly_column = gr.Dropdown(label="Column for Anomaly Detection", choices=[]) | |
| generate_insights_button = gr.Button("Generate Insights", variant="primary") | |
| top_table = gr.Dataframe(label="Top Performers", interactive=False) | |
| bottom_table = gr.Dataframe(label="Bottom Performers", interactive=False) | |
| trend_output = gr.Markdown() | |
| anomaly_table = gr.Dataframe(label="Potential Anomalies", interactive=False) | |
| # Interactions | |
| load_button.click( | |
| fn=_handle_file_upload, | |
| inputs=[file_input, dataset_state], | |
| outputs=[ | |
| dataset_state, | |
| upload_status, | |
| dataset_info, | |
| dtypes_table, | |
| head_table, | |
| tail_table, | |
| filter_preview_table, | |
| filter_status, | |
| ], | |
| ).then( | |
| fn=_populate_column_options, | |
| inputs=[dataset_state], | |
| outputs=[ | |
| numeric_column_dropdown, | |
| date_column_dropdown, | |
| categorical_values, | |
| categorical_column_dropdown, | |
| ts_date_column, | |
| ts_value_column, | |
| dist_column, | |
| category_column, | |
| category_value_column, | |
| scatter_x_column, | |
| scatter_y_column, | |
| scatter_color_column, | |
| insight_numeric_column, | |
| trend_date_column, | |
| trend_value_column, | |
| anomaly_column, | |
| ], | |
| ).then( | |
| fn=_generate_statistics, | |
| inputs=[dataset_state], | |
| outputs=[ | |
| numeric_table, | |
| categorical_table, | |
| missing_table, | |
| correlation_table, | |
| stats_status, | |
| ], | |
| ) | |
| load_sample_button.click( | |
| fn=_handle_sample_dataset, | |
| inputs=[sample_dropdown, dataset_state], | |
| outputs=[ | |
| dataset_state, | |
| upload_status, | |
| dataset_info, | |
| dtypes_table, | |
| head_table, | |
| tail_table, | |
| filter_preview_table, | |
| filter_status, | |
| ], | |
| ).then( | |
| fn=_populate_column_options, | |
| inputs=[dataset_state], | |
| outputs=[ | |
| numeric_column_dropdown, | |
| date_column_dropdown, | |
| categorical_values, | |
| categorical_column_dropdown, | |
| ts_date_column, | |
| ts_value_column, | |
| dist_column, | |
| category_column, | |
| category_value_column, | |
| scatter_x_column, | |
| scatter_y_column, | |
| scatter_color_column, | |
| insight_numeric_column, | |
| trend_date_column, | |
| trend_value_column, | |
| anomaly_column, | |
| ], | |
| ).then( | |
| fn=_generate_statistics, | |
| inputs=[dataset_state], | |
| outputs=[ | |
| numeric_table, | |
| categorical_table, | |
| missing_table, | |
| correlation_table, | |
| stats_status, | |
| ], | |
| ) | |
| sample_dropdown.change( | |
| fn=_describe_sample_dataset, | |
| inputs=[sample_dropdown], | |
| outputs=[sample_description], | |
| ) | |
| numeric_column_dropdown.change( | |
| fn=_update_numeric_inputs, | |
| inputs=[numeric_column_dropdown, dataset_state], | |
| outputs=[numeric_min_input, numeric_max_input], | |
| ) | |
| categorical_column_dropdown.change( | |
| fn=_update_categorical_values, | |
| inputs=[categorical_column_dropdown, dataset_state], | |
| outputs=[categorical_values], | |
| ) | |
| date_column_dropdown.change( | |
| fn=_update_date_bounds, | |
| inputs=[date_column_dropdown, dataset_state], | |
| outputs=[start_date_picker, end_date_picker], | |
| ) | |
| generate_stats_button.click( | |
| fn=_generate_statistics, | |
| inputs=[dataset_state], | |
| outputs=[numeric_table, categorical_table, missing_table, correlation_table, stats_status], | |
| ) | |
| apply_filters_button.click( | |
| fn=_apply_filters, | |
| inputs=[ | |
| dataset_state, | |
| numeric_column_dropdown, | |
| numeric_min_input, | |
| numeric_max_input, | |
| categorical_column_dropdown, | |
| categorical_values, | |
| date_column_dropdown, | |
| start_date_picker, | |
| end_date_picker, | |
| ], | |
| outputs=[dataset_state, filter_preview_table, filter_status], | |
| ) | |
| export_filtered_button.click( | |
| fn=_download_filtered, | |
| inputs=[dataset_state], | |
| outputs=[export_filtered_file], | |
| ) | |
| def _toggle_controls(selected: str) -> Tuple[Any, Any, Any, Any]: | |
| return ( | |
| gr.update(visible=selected == "Time Series"), | |
| gr.update(visible=selected == "Distribution"), | |
| gr.update(visible=selected == "Category"), | |
| gr.update(visible=selected == "Scatter"), | |
| ) | |
| chart_type.change( | |
| fn=_toggle_controls, | |
| inputs=[chart_type], | |
| outputs=[time_series_controls, distribution_controls, category_controls, scatter_controls], | |
| ) | |
| generate_chart_button.click( | |
| fn=_generate_chart, | |
| inputs=[ | |
| dataset_state, | |
| chart_type, | |
| ts_date_column, | |
| ts_value_column, | |
| ts_aggregation, | |
| dist_column, | |
| dist_type, | |
| category_column, | |
| category_value_column, | |
| category_chart_type, | |
| category_aggregation, | |
| scatter_x_column, | |
| scatter_y_column, | |
| scatter_color_column, | |
| ], | |
| outputs=[dataset_state, chart_output, viz_status], | |
| ) | |
| export_chart_button.click( | |
| fn=_download_chart, | |
| inputs=[dataset_state], | |
| outputs=[export_chart_file], | |
| ) | |
| generate_insights_button.click( | |
| fn=_generate_insights, | |
| inputs=[ | |
| dataset_state, | |
| insight_numeric_column, | |
| trend_date_column, | |
| trend_value_column, | |
| anomaly_column, | |
| ], | |
| outputs=[ | |
| top_table, | |
| bottom_table, | |
| trend_output, | |
| anomaly_table, | |
| insights_status, | |
| ], | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| demo = create_dashboard() | |
| demo.launch(server_name="0.0.0.0", server_port=7860, share=True) | |