File size: 10,618 Bytes
b560569
575b933
b0464a9
87a87e7
21988b0
791c130
8add36b
f7fc39b
575b933
826a2a1
2e2e19a
8add36b
575b933
21988b0
 
 
 
 
2e2e19a
945226d
9aa3bfe
 
945226d
21988b0
 
dd307a7
 
8add36b
21988b0
dd307a7
 
21988b0
8add36b
bde3d91
 
21988b0
 
2601f1c
dd307a7
bb1bc48
dd307a7
21988b0
 
 
5a483f8
21988b0
8add36b
ddc6277
21988b0
ddc6277
 
 
 
bde3d91
ddc6277
8add36b
21988b0
 
8add36b
ddc6277
21988b0
bde3d91
 
4699b09
 
 
 
 
 
 
 
 
 
 
 
 
 
bde3d91
 
 
4699b09
bde3d91
dd307a7
 
 
 
 
 
 
 
 
8853c45
 
4699b09
ddc6277
6b39ad4
5cc5759
4699b09
 
77179e2
8add36b
dd307a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9aa3bfe
 
 
dd307a7
9aa3bfe
 
 
 
 
 
 
 
dd307a7
 
 
 
8add36b
bde3d91
dd307a7
 
 
 
 
 
 
 
 
1644cc1
2e2e19a
dd307a7
 
 
 
 
 
 
 
 
 
9aa3bfe
dd307a7
9aa3bfe
dd307a7
 
 
 
 
 
 
 
3c8e890
dd307a7
3c8e890
 
dd307a7
00de3b2
adb3bbe
dd307a7
 
 
 
 
 
8add36b
dd307a7
 
 
 
 
 
 
 
21988b0
dd307a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
import gradio as gr
import pandas as pd
import os
import logging
from collections import defaultdict
import matplotlib
matplotlib.use('Agg') # Set backend for Matplotlib

# --- Module Imports ---
from utils.gradio_utils import get_url_user_token

# Functions from newly created/refactored modules
from config import (
    PLOT_ID_TO_FORMULA_KEY_MAP,
    LINKEDIN_CLIENT_ID_ENV_VAR,
    BUBBLE_APP_NAME_ENV_VAR,
    BUBBLE_API_KEY_PRIVATE_ENV_VAR,
    BUBBLE_API_ENDPOINT_ENV_VAR
)

# analytics_tab_module is now imported locally inside ui/ui_enhancements.py's build_main_app_ui
# from services.analytics_tab_module import AnalyticsTab # REMOVED global import here

# UPDATED: Using the new data loading function from the refactored state manager
from services.state_manager import load_data_from_bubble

# Import UI generator functions (these are now passed to build_main_app_ui)
from ui.ui_generators import (
    build_analytics_tab_plot_area,
    build_home_tab_ui,
    create_enhanced_report_tab,
    BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON
)
# NEW: Import the new OKR UI functions
from ui.okr_ui_generator import create_enhanced_okr_tab, format_okrs_for_enhanced_display, get_initial_okr_display
from ui.analytics_plot_generator import update_analytics_plots_figures, create_placeholder_plot
from formulas import PLOT_FORMULAS

# NEW: Import UI enhancements from the new module
from ui.ui_main_page_enhancements import build_main_app_ui, update_report_display_enhanced

# --- CHATBOT MODULE IMPORTS ---
from features.chatbot.chatbot_prompts import get_initial_insight_prompt_and_suggestions
from features.chatbot.chatbot_handler import generate_llm_response

# --- AGENTIC PIPELINE (DISPLAY ONLY) IMPORTS ---
try:
    # This is the main function called on initial load to populate the agentic tabs
    from run_agentic_pipeline import load_and_display_agentic_results
    # This function is now called when a new report is selected from the dropdown
    from services.report_data_handler import fetch_and_reconstruct_data_from_bubble
    # UI formatting functions
    from ui.insights_ui_generator import (
        format_report_for_display, # This will now return header HTML and body Markdown
    )
    AGENTIC_MODULES_LOADED = True
except ImportError as e:
    logging.error(f"Could not import agentic pipeline display modules: {e}. Tabs 3 and 4 will be disabled.")
    AGENTIC_MODULES_LOADED = False
    # Placeholder functions to prevent app from crashing if imports fail
    def load_and_display_agentic_results(*args, **kwargs):
        # NOTE: This return signature MUST match agentic_display_outputs
        # Adjusted return values for the new split report display components and the new OKR HTML
        empty_header_html = """
        <div class="report-title">πŸ“Š Comprehensive Analysis Report</div>
        <div class="report-subtitle">AI-Generated Insights from Your LinkedIn Data</div>
        <div class="status-badge">Generated from Bubble.io</div>
        """
        empty_body_markdown = """
        <div class="empty-state">
            <div class="empty-state-icon">πŸ“„</div>
            <div class="empty-state-title">No Report Selected</div>
            <div class="empty-state-description">
                Please select a report from the library above to view its detailed analysis and insights.
            </div>
        </div>
        """
        # The load_and_display_agentic_results function returns many values.
        # Ensure the placeholder returns the correct number of gr.update components
        # matching the `outputs` in the .then() call later.
        return (
            gr.update(value="Modules not loaded."), # agentic_pipeline_status_md (0)
            gr.update(choices=[], value=None),       # report_selector_dd (1)
            gr.update(choices=[], value=[]),         # key_results_cbg (2) - KEPT HIDDEN for compatibility
            gr.update(value="Modules not loaded."),  # okr_detail_display_md (3) - KEPT HIDDEN for compatibility
            None,                                    # orchestration_raw_results_st (4)
            [],                                      # selected_key_result_ids_st (5) - KEPT HIDDEN for compatibility
            [],                                      # key_results_for_selection_st (6) - KEPT HIDDEN for compatibility
            gr.update(value=empty_header_html),      # report_header_html_display (7)
            gr.update(value=empty_body_markdown),    # report_body_markdown_display (8)
            {},                                      # reconstruction_cache_st (9)
            gr.update(value=get_initial_okr_display()), # NEW: enhanced_okr_display_html (10)
            gr.update(value={}) # NEW: actionable_okrs_data_st (11)
        )
    def fetch_and_reconstruct_data_from_bubble(*args, **kwargs):
        return None, {}
    def format_report_for_display(report_data):
        # Placeholder for when modules are not loaded, returns structure matching the new design
        return {'header_html': '<h1>Agentic modules not loaded.</h1>', 'body_markdown': 'Report display unavailable.'}


# --- Initial data load sequence function (remains in app.py as it uses service functions) ---
def initial_data_load_sequence(url_token, org_urn_val, current_state):
    """
    Handles the initial data loading from Bubble.
    No longer generates dashboard HTML as the Home tab is now static.
    """
    status_msg, new_state = load_data_from_bubble(url_token, org_urn_val, current_state)
    # Add status icons based on success/failure
    if "successfully" in status_msg.lower() or "loaded" in status_msg.lower():
        status_msg = f"βœ… {status_msg}"
    elif "error" in status_msg.lower() or "failed" in status_msg.lower():
        status_msg = f"❌ {status_msg}"
    else:
        status_msg = f"πŸ”„ {status_msg}"
    return status_msg, new_state

# Build the main UI using the function from ui_enhancements
(app, url_user_token_display, org_urn_display, status_box,
 token_state, reconstruction_cache_st, enhanced_okr_display_html,
 tabs, report_selector_dd, agentic_display_outputs,
 analytics_tab_instance, chat_histories_st_returned,
 current_chat_plot_id_st_returned, plot_data_for_chatbot_st_returned, # Receive these returned states
 format_report_for_display_func_passed) = \
    build_main_app_ui(
        PLOT_ID_TO_FORMULA_KEY_MAP=PLOT_ID_TO_FORMULA_KEY_MAP,
        PLOT_FORMULAS=PLOT_FORMULAS,
        BOMB_ICON=BOMB_ICON, EXPLORE_ICON=EXPLORE_ICON, FORMULA_ICON=FORMULA_ICON, ACTIVE_ICON=ACTIVE_ICON,
        build_analytics_tab_plot_area_func=build_analytics_tab_plot_area,
        update_analytics_plots_figures_func=update_analytics_plots_figures,
        create_placeholder_plot_func=create_placeholder_plot,
        get_initial_insight_prompt_and_suggestions_func=get_initial_insight_prompt_and_suggestions,
        generate_llm_response_func=generate_llm_response,
        build_home_tab_ui_func=build_home_tab_ui,
        create_enhanced_report_tab_func=create_enhanced_report_tab,
        create_enhanced_okr_tab_func=create_enhanced_okr_tab,
        format_report_for_display_func=format_report_for_display # Pass the imported function
    )

# Event handlers (re-establishing them now that components are defined)
app.load(fn=get_url_user_token, inputs=None, outputs=[url_user_token_display, org_urn_display], api_name="get_url_params", show_progress=False)

if AGENTIC_MODULES_LOADED:
    report_selector_dd.change(
        fn=lambda sr_id, c_state: update_report_display_enhanced(sr_id, c_state, format_report_for_display),
        inputs=[report_selector_dd, token_state],
        outputs=[agentic_display_outputs[7], agentic_display_outputs[8]], # report_header_html_display, report_body_markdown_display
        show_progress="minimal"
    )

# Initial load sequence with enhanced status updates
initial_load_event = org_urn_display.change(
    fn=initial_data_load_sequence,
    inputs=[url_user_token_display, org_urn_display, token_state],
    outputs=[status_box, token_state],
    show_progress="full"
)

# Chain the loading events
initial_load_event.then(
    fn=analytics_tab_instance.refresh_analytics_graphs_ui,
    inputs=[token_state, analytics_tab_instance.date_filter_selector, analytics_tab_instance.custom_start_date_picker,
            analytics_tab_instance.custom_end_date_picker, chat_histories_st_returned],
    outputs=analytics_tab_instance.graph_refresh_outputs_list,
    show_progress="full"
).then(
    fn=load_and_display_agentic_results,
    inputs=[token_state, reconstruction_cache_st],
    outputs=agentic_display_outputs,
    show_progress="minimal"
).then(
    fn=format_okrs_for_enhanced_display,
    inputs=[reconstruction_cache_st],
    outputs=[enhanced_okr_display_html],
    show_progress="minimal"
)

if __name__ == "__main__":
    # Enhanced startup logging
    print("πŸš€ Starting LinkedIn Organization Dashboard...")
    
    # Environment variable checks with better logging
    missing_vars = []
    
    if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR):
        missing_vars.append(LINKEDIN_CLIENT_ID_ENV_VAR)
        logging.warning(f"⚠️  WARNING: '{LINKEDIN_CLIENT_ID_ENV_VAR}' is not set.")
    
    bubble_vars = [BUBBLE_APP_NAME_ENV_VAR, BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR]
    if not all(os.environ.get(var) for var in bubble_vars):
        missing_vars.extend([var for var in bubble_vars if not os.environ.get(var)])
        logging.warning("⚠️  WARNING: One or more Bubble environment variables are not set.")
    
    if not os.environ.get("GEMINI_API_KEY"):
        missing_vars.append("GEMINI_API_KEY")
        logging.warning("⚠️  WARNING: 'GEMINI_API_KEY' is not set.")
    
    if not AGENTIC_MODULES_LOADED:
        logging.warning("πŸ”΄ CRITICAL: Agentic modules failed to load.")
    
    if missing_vars:
        print(f"⚠️  Missing environment variables: {', '.join(missing_vars)}")
        print("πŸ”§ Please set these variables for full functionality.")
    else:
        print("βœ… All environment variables are properly configured.")
    
    print("🌐 Launching dashboard on http://0.0.0.0:7860")
    print("🎯 Dashboard features:")
    print("   β€’ πŸ“Š Advanced LinkedIn Analytics")
    print("   β€’ πŸ€– AI-Powered Insights")
    print("   β€’ 🎯 OKR Generation & Tracking") 
    print("   β€’ ☁️  Bubble.io Integration")
    
    app.launch(
        server_name="0.0.0.0", 
        server_port=int(os.environ.get("PORT", 7860)), 
        debug=True,
        show_error=True,
        show_tips=True,
        enable_queue=True
    )