mgbam commited on
Commit
d1943e0
·
verified ·
1 Parent(s): fb92793

Create ui/callbacks.py

Browse files
Files changed (1) hide show
  1. ui/callbacks.py +100 -0
ui/callbacks.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ui/callbacks.py
2
+ import gradio as gr
3
+ import pandas as pd
4
+ import logging
5
+ from threading import Thread
6
+
7
+ from core.analyzer import DataAnalyzer
8
+ from core.llm import GeminiNarrativeGenerator
9
+ from core.config import settings
10
+ from core.exceptions import APIKeyMissingError, DataProcessingError
11
+ from modules.clustering import perform_clustering
12
+ # ... other module imports
13
+
14
+ def register_callbacks(components):
15
+ """Binds event handlers to the UI components."""
16
+
17
+ # --- Main Analysis Trigger ---
18
+ def run_full_analysis(file_obj):
19
+ # 1. Input Validation
20
+ if file_obj is None:
21
+ raise gr.Error("No file uploaded. Please upload a CSV or Excel file.")
22
+ if not settings.GOOGLE_API_KEY:
23
+ raise APIKeyMissingError("CRITICAL: GOOGLE_API_KEY not found in .env file.")
24
+
25
+ try:
26
+ # 2. Data Loading & Pre-processing
27
+ logging.info(f"Processing uploaded file: {file_obj.name}")
28
+ df = pd.read_csv(file_obj.name) if file_obj.name.endswith('.csv') else pd.read_excel(file_obj.name)
29
+ if len(df) > settings.MAX_UI_ROWS:
30
+ df = df.sample(n=settings.MAX_UI_ROWS, random_state=42)
31
+
32
+ # 3. Core Analysis
33
+ analyzer = DataAnalyzer(df)
34
+ meta = analyzer.metadata
35
+ missing_df, num_df, cat_df = analyzer.get_profiling_reports()
36
+ fig_types, fig_missing, fig_corr = analyzer.get_overview_visuals()
37
+
38
+ # 4. Asynchronous AI Narrative Generation
39
+ ai_report_queue = [""] # Use a mutable list to pass string by reference
40
+ def generate_ai_report_threaded(analyzer_instance):
41
+ narrative_generator = GeminiNarrativeGenerator(api_key=settings.GOOGLE_API_KEY)
42
+ ai_report_queue[0] = narrative_generator.generate_narrative(analyzer_instance)
43
+
44
+ thread = Thread(target=generate_ai_report_threaded, args=(analyzer,))
45
+ thread.start()
46
+
47
+ # 5. Prepare Initial UI Updates (Instantaneous)
48
+ updates = {
49
+ components["state_analyzer"]: analyzer,
50
+ components["ai_report_output"]: "⏳ Generating AI-powered report... This may take a moment.",
51
+ components["profile_missing_df"]: gr.update(value=missing_df),
52
+ components["profile_numeric_df"]: gr.update(value=num_df),
53
+ components["profile_categorical_df"]: gr.update(value=cat_df),
54
+ components["plot_types"]: gr.update(value=fig_types),
55
+ components["plot_missing"]: gr.update(value=fig_missing),
56
+ components["plot_correlation"]: gr.update(value=fig_corr),
57
+ # ... update dropdowns and visibility ...
58
+ components["dd_hist_col"]: gr.update(choices=meta['numeric_cols'], value=meta['numeric_cols'][0] if meta['numeric_cols'] else None),
59
+ components["dd_scatter_x"]: gr.update(choices=meta['numeric_cols'], value=meta['numeric_cols'][0] if meta['numeric_cols'] else None),
60
+ components["dd_scatter_y"]: gr.update(choices=meta['numeric_cols'], value=meta['numeric_cols'][1] if len(meta['numeric_cols']) > 1 else None),
61
+ components["dd_scatter_color"]: gr.update(choices=meta['columns']),
62
+ components["tab_timeseries"]: gr.update(visible=bool(meta['datetime_cols'])),
63
+ components["tab_text"]: gr.update(visible=bool(meta['text_cols'])),
64
+ components["tab_cluster"]: gr.update(visible=len(meta['numeric_cols']) > 1),
65
+ }
66
+ yield updates
67
+
68
+ # 6. Final UI Update (When AI report is ready)
69
+ thread.join() # Wait for the AI thread to finish
70
+ updates[components["ai_report_output"]] = ai_report_queue[0]
71
+ yield updates
72
+
73
+ except (DataProcessingError, APIKeyMissingError) as e:
74
+ logging.error(f"User-facing error: {e}", exc_info=True)
75
+ raise gr.Error(str(e))
76
+ except Exception as e:
77
+ logging.error(f"A critical unhandled error occurred: {e}", exc_info=True)
78
+ raise gr.Error(f"Analysis Failed! An unexpected error occurred: {str(e)}")
79
+
80
+
81
+ # Bind the main function
82
+ components["analyze_button"].click(
83
+ fn=run_full_analysis,
84
+ inputs=[components["upload_button"]],
85
+ outputs=list(components.values())
86
+ )
87
+
88
+ # --- Clustering Tab Callback ---
89
+ def update_clustering(analyzer, k):
90
+ if not analyzer: return gr.update(), gr.update(), gr.update()
91
+ fig_cluster, fig_elbow, summary = perform_clustering(analyzer.df, analyzer.metadata['numeric_cols'], k)
92
+ return fig_cluster, fig_elbow, summary
93
+
94
+ components["num_clusters"].change(
95
+ fn=update_clustering,
96
+ inputs=[components["state_analyzer"], components["num_clusters"]],
97
+ outputs=[components["plot_cluster"], components["plot_elbow"], components["md_cluster_summary"]]
98
+ )
99
+
100
+ # ... Register other callbacks for histogram, scatter, etc. in a similar fashion ...