File size: 12,497 Bytes
b560569
575b933
b0464a9
87a87e7
21988b0
791c130
8add36b
f7fc39b
575b933
826a2a1
2e2e19a
8add36b
575b933
21988b0
 
 
 
 
2e2e19a
945226d
 
 
21988b0
 
dd307a7
 
8add36b
21988b0
dd307a7
 
21988b0
8add36b
bde3d91
 
21988b0
 
2601f1c
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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
575b933
8add36b
dd307a7
 
 
 
 
 
 
 
 
 
 
 
8add36b
bde3d91
dd307a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1644cc1
2e2e19a
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
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
)

from services.analytics_tab_module import AnalyticsTab

# 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

# Instantiate AnalyticsTab (needs to be done before building UI if its methods are called)
analytics_icons = {'bomb': BOMB_ICON, 'explore': EXPLORE_ICON, 'formula': FORMULA_ICON, 'active': ACTIVE_ICON}
analytics_tab_instance = AnalyticsTab(
    # These states are created within build_main_app_ui, but AnalyticsTab needs references.
    # We will set them after build_main_app_ui returns them. For now, pass placeholders.
    token_state=None, # Will be updated after build_main_app_ui
    chat_histories_st=None, # Will be updated after build_main_app_ui
    current_chat_plot_id_st=None, # Will be updated after build_main_app_ui
    plot_data_for_chatbot_st=None, # Will be updated after build_main_app_ui
    plot_id_to_formula_map=PLOT_ID_TO_FORMULA_KEY_MAP,
    plot_formulas_data=PLOT_FORMULAS,
    icons=analytics_icons,
    fn_build_plot_area=build_analytics_tab_plot_area,
    fn_update_plot_figures=update_analytics_plots_figures,
    fn_create_placeholder_plot=create_placeholder_plot,
    fn_get_initial_insight=get_initial_insight_prompt_and_suggestions,
    fn_generate_llm_response=generate_llm_response
)


# 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_returned, chat_histories_st_returned, format_report_for_display_func_passed) = \
    build_main_app_ui(
        analytics_tab_instance=analytics_tab_instance, # Pass the instantiated object
        AGENTIC_MODULES_LOADED=AGENTIC_MODULES_LOADED,
        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
    )

# Now, update the analytics_tab_instance with the actual state components from the built UI
# This is crucial because analytics_tab_instance was instantiated before the gr.State components existed.
analytics_tab_instance.token_state = token_state
analytics_tab_instance.chat_histories_st = chat_histories_st_returned
analytics_tab_instance.current_chat_plot_id_st = agentic_display_outputs[9] # This is current_chat_plot_id_st
analytics_tab_instance.plot_data_for_chatbot_st = agentic_display_outputs[11] # This is actionable_okrs_data_st, which is incorrect. Should be plot_data_for_chatbot_st

# Corrected update for analytics_tab_instance state components.
# Need to know the exact index of each state in agentic_display_outputs or pass them directly.
# Given the `build_main_app_ui` returns specific components, let's use those directly.
# The original code for `chat_histories_st`, `current_chat_plot_id_st`, `plot_data_for_chatbot_st`
# are defined within `build_main_app_ui`'s scope.
# The returned `chat_histories_st_returned` and the other states are the correct references.
# So, no need to access them via `agentic_display_outputs` for the analytics_tab_instance.
# The parameters `chat_histories_st_returned` (which is `chat_histories_st` inside `build_main_app_ui`)
# and others are the correct references.

# 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, # Using the corrected method name (assuming this exists)
    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], # Using returned chat_histories_st
    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
    )