File size: 7,761 Bytes
0a8cbc3
d9ea3f9
60da408
c9ba3ae
f311ea6
c9ba3ae
f311ea6
 
 
 
0a8cbc3
 
 
 
f311ea6
 
60da408
0a8cbc3
 
51dfd28
0a8cbc3
f311ea6
0a8cbc3
51dfd28
0d6622c
0a8cbc3
 
 
 
 
 
60da408
0d6622c
0a8cbc3
 
 
d9ea3f9
0a8cbc3
f311ea6
 
0a8cbc3
 
f311ea6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51dfd28
f311ea6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51dfd28
0a8cbc3
 
 
 
 
 
 
 
 
f311ea6
0a8cbc3
0d6622c
0a8cbc3
 
 
 
 
 
 
f311ea6
 
0a8cbc3
 
 
f311ea6
0a8cbc3
 
 
 
 
 
f311ea6
0a8cbc3
 
f311ea6
0a8cbc3
c9ba3ae
60da408
0a8cbc3
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# app.py

# -*- coding: utf-8 -*-
#
# PROJECT:      CognitiveEDA v5.4 - The QuantumLeap Intelligence Platform
#
# DESCRIPTION:  Main application entry point. This definitive version combines UI
#               layout and callback registration within a single, robust script
#               to align with Gradio's context-based API design, resolving all
#               previous startup errors.
#
# SETUP:        $ pip install -r requirements.txt
#
# AUTHOR:       An MCP & PhD Expert in Data & AI Solutions
# VERSION:      5.4 (Definitive Context-Aware Edition)
# LAST-UPDATE:  2023-10-30 (Final architectural correction)

import warnings
import logging
import gradio as gr

# The callback LOGIC is still neatly separated
from ui import callbacks
from core.config import settings

# --- Configuration & Setup ---
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - [%(levelname)s] - (%(filename)s:%(lineno)d) - %(message)s'
)
warnings.filterwarnings('ignore', category=FutureWarning)


def main():
    """
    Primary function to build, wire up, and launch the Gradio application.
    """
    logging.info(f"Starting {settings.APP_TITLE}")

    # The 'with' block creates the Gradio context. All UI and events will be defined here.
    with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="indigo"), title=settings.APP_TITLE) as demo:
        
        # ======================================================================
        # 1. DEFINE THE UI LAYOUT DIRECTLY WITHIN THE MAIN SCRIPT
        # This is the most robust pattern and resolves all context-related errors.
        # ======================================================================
        
        # State object to hold the DataAnalyzer instance
        state_analyzer = gr.State()

        # --- Header ---
        gr.Markdown(f"<h1>{settings.APP_TITLE}</h1>")
        gr.Markdown("A world-class data discovery platform that provides a complete suite of EDA tools and intelligently unlocks specialized analysis modules.")

        # --- Input Row ---
        with gr.Row():
            upload_button = gr.File(label="1. Upload Data File (CSV, Excel)", file_types=[".csv", ".xlsx"], scale=3)
            analyze_button = gr.Button("✨ Generate Intelligence Report", variant="primary", scale=1)

        # --- Main Tabs ---
        with gr.Tabs():
            with gr.Tab("πŸ€– AI-Powered Strategy Report", id="tab_ai"):
                ai_report_output = gr.Markdown("### Your AI-generated report will appear here after analysis...")
            
            with gr.Tab("πŸ“‹ Data Profile", id="tab_profile"):
                with gr.Accordion("Missing Values Report", open=False):
                    profile_missing_df = gr.DataFrame()
                with gr.Accordion("Numeric Features Summary", open=True):
                    profile_numeric_df = gr.DataFrame()
                with gr.Accordion("Categorical Features Summary", open=True):
                    profile_categorical_df = gr.DataFrame()

            with gr.Tab("πŸ“Š Overview Visuals", id="tab_overview"):
                with gr.Row():
                    plot_types = gr.Plot()
                    plot_missing = gr.Plot()
                plot_correlation = gr.Plot()
                
            with gr.Tab("🎨 Interactive Explorer", id="tab_explorer"):
                 gr.Markdown("### Univariate Analysis")
                 with gr.Row():
                     dd_hist_col = gr.Dropdown(label="Select Column for Histogram", interactive=True)
                     plot_histogram = gr.Plot()
                 gr.Markdown("### Bivariate Analysis")
                 with gr.Row():
                     with gr.Column(scale=1):
                         dd_scatter_x = gr.Dropdown(label="X-Axis (Numeric)", interactive=True)
                         dd_scatter_y = gr.Dropdown(label="Y-Axis (Numeric)", interactive=True)
                         dd_scatter_color = gr.Dropdown(label="Color By (Optional)", interactive=True)
                     with gr.Column(scale=2):
                         plot_scatter = gr.Plot()
            
            with gr.Tab("🧩 Clustering (K-Means)", id="tab_cluster", visible=False) as tab_cluster:
                with gr.Row():
                    with gr.Column(scale=1):
                        num_clusters = gr.Slider(minimum=2, maximum=10, value=3, step=1, label="Number of Clusters (K)", interactive=True)
                        md_cluster_summary = gr.Markdown()
                    with gr.Column(scale=2):
                        plot_cluster = gr.Plot()
                plot_elbow = gr.Plot()
                
            tab_timeseries = gr.Tab("βŒ› Time-Series Analysis", id="tab_timeseries", visible=False)
            tab_text = gr.Tab("πŸ“ Text Analysis", id="tab_text", visible=False)

        # --- Collect all components into a dictionary for easy access ---
        components = {
            "state_analyzer": state_analyzer, "upload_button": upload_button, "analyze_button": analyze_button,
            "ai_report_output": ai_report_output, "profile_missing_df": profile_missing_df,
            "profile_numeric_df": profile_numeric_df, "profile_categorical_df": profile_categorical_df,
            "plot_types": plot_types, "plot_missing": plot_missing, "plot_correlation": plot_correlation,
            "dd_hist_col": dd_hist_col, "plot_histogram": plot_histogram, "dd_scatter_x": dd_scatter_x,
            "dd_scatter_y": dd_scatter_y, "dd_scatter_color": dd_scatter_color, "plot_scatter": plot_scatter,
            "tab_timeseries": tab_timeseries, "tab_text": tab_text, "tab_cluster": tab_cluster,
            "num_clusters": num_clusters, "md_cluster_summary": md_cluster_summary,
            "plot_cluster": plot_cluster, "plot_elbow": plot_elbow,
        }

        # ======================================================================
        # 2. REGISTER EVENT HANDLERS
        # Now that components is a guaranteed dictionary, this will work.
        # ======================================================================

        # --- Primary Analysis Chain ---
        analysis_complete_event = components["analyze_button"].click(
            fn=callbacks.run_initial_analysis,
            inputs=[components["upload_button"]],
            outputs=[components["state_analyzer"]]
        )
        analysis_complete_event.then(
            fn=callbacks.generate_reports_and_visuals,
            inputs=[components["state_analyzer"]],
            outputs=components
        )

        # --- Interactive Explorer Callbacks ---
        components["dd_hist_col"].change(
            fn=callbacks.create_histogram,
            inputs=[components["state_analyzer"], components["dd_hist_col"]],
            outputs=[components["plot_histogram"]]
        )
        scatter_inputs = [
            components["state_analyzer"], components["dd_scatter_x"], 
            components["dd_scatter_y"], components["dd_scatter_color"]
        ]
        for dropdown in [components["dd_scatter_x"], components["dd_scatter_y"], components["dd_scatter_color"]]:
            dropdown.change(
                fn=callbacks.create_scatterplot, inputs=scatter_inputs, outputs=[components["plot_scatter"]]
            )

        # --- Specialized Module Callbacks ---
        components["num_clusters"].change(
            fn=callbacks.update_clustering,
            inputs=[components["state_analyzer"], components["num_clusters"]],
            outputs=[components["plot_cluster"], components["plot_elbow"], components["md_cluster_summary"]]
        )
    
    # 3. Launch the application server
    demo.launch(debug=False, server_name="0.0.0.0")


# --- Application Entry Point ---
if __name__ == "__main__":
    main()