GuglielmoTor commited on
Commit
2e2e19a
·
verified ·
1 Parent(s): d575454

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +189 -130
app.py CHANGED
@@ -5,207 +5,266 @@ import os
5
  import logging
6
  import matplotlib
7
  matplotlib.use('Agg') # Set backend for Matplotlib to avoid GUI conflicts with Gradio
8
- # import time # No longer directly used here for profiling
9
- from datetime import datetime, timedelta
10
- # import numpy as np # No longer directly used here
11
- # from collections import OrderedDict, defaultdict # Moved or not needed directly
 
 
12
 
13
  # --- Module Imports ---
14
  from utils.gradio_utils import get_url_user_token
 
 
15
  from config import (
16
  LINKEDIN_CLIENT_ID_ENV_VAR, BUBBLE_APP_NAME_ENV_VAR,
17
- BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR) # PLOT_ID_TO_FORMULA_KEY_MAP moved
 
 
 
 
 
18
  from services.state_manager import process_and_store_bubble_token
19
  from services.sync_logic import sync_all_linkedin_data_orchestrator
20
- from ui.ui_generators import display_main_dashboard # Other UI generators moved or used internally by new modules
21
 
22
- # --- NEW UI MODULE IMPORTS ---
23
- from ui import analytics_tab
24
- from ui import agentic_module
25
 
26
- # --- EXISTING CHATBOT MODULE IMPORTS (used by analytics_tab) ---
27
- # from features.chatbot.chatbot_prompts import get_initial_insight_prompt_and_suggestions # Used in analytics_tab
28
- # from features.chatbot.chatbot_handler import generate_llm_response # Used in analytics_tab
 
 
29
 
30
- # --- AGENTIC PIPELINE IMPORTS (used by agentic_module) ---
31
- # AGENTIC_MODULES_LOADED is handled within agentic_module.py
 
 
 
 
 
 
 
 
 
32
 
33
  # Configure logging
34
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
35
 
36
- # 1. Set Vertex AI usage preference (if applicable)
37
- os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "False"
38
-
39
- # 2. Get your API key
40
  user_provided_api_key = os.environ.get("GEMINI_API_KEY")
41
  if user_provided_api_key:
42
  os.environ["GOOGLE_API_KEY"] = user_provided_api_key
43
  logging.info("GOOGLE_API_KEY environment variable has been set from GEMINI_API_KEY.")
44
  else:
45
- logging.error(f"CRITICAL ERROR: The API key environment variable 'GEMINI_API_KEY' was not found.")
46
 
47
-
48
- # --- Gradio UI Blocks ---
49
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
50
- title="LinkedIn Organization Dashboard") as app:
51
-
52
- # --- Core States ---
53
  token_state = gr.State(value={
54
  "token": None, "client_id": None, "org_urn": None,
55
  "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
56
- "bubble_mentions_df": pd.DataFrame(), "bubble_follower_stats_df": pd.DataFrame(),
 
57
  "fetch_count_for_api": 0, "url_user_token_temp_storage": None,
58
  "config_date_col_posts": "published_at", "config_date_col_mentions": "date",
59
  "config_date_col_followers": "date", "config_media_type_col": "media_type",
60
  "config_eb_labels_col": "li_eb_label"
61
  })
 
 
 
 
 
62
 
63
- # States for analytics tab chatbot (passed to analytics_tab module)
64
- chat_histories_st = gr.State({})
65
- current_chat_plot_id_st = gr.State(None)
66
- plot_data_for_chatbot_st = gr.State({}) # Populated by analytics_tab.handle_refresh_analytics_graphs
67
- active_panel_action_state = gr.State(None) # For insights/formula panel
68
- explored_plot_id_state = gr.State(None) # For explore plot view
69
 
70
- # States for Agentic Pipeline (passed to agentic_module)
71
- orchestration_raw_results_st = gr.State(None)
72
- key_results_for_selection_st = gr.State([]) # Stores the list of dicts for choices
73
- selected_key_result_ids_st = gr.State([]) # Stores the selected unique_kr_ids
74
 
75
- # --- Top Level UI ---
76
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
77
  url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
78
  status_box = gr.Textbox(label="Stato Generale Token LinkedIn", interactive=False, value="Inizializzazione...")
79
  org_urn_display = gr.Textbox(label="URN Organizzazione (Nascosto)", interactive=False, visible=False)
80
 
81
- # Load URL parameters on app load
 
82
  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)
83
 
84
- # --- Tabs ---
85
  with gr.Tabs() as tabs:
86
- # --- Tab 1: Dashboard & Sync ---
87
  with gr.TabItem("1️⃣ Dashboard & Sync", id="tab_dashboard_sync"):
88
- gr.Markdown("Il sistema controlla i dati esistenti da Bubble. 'Sincronizza' si attiva se sono necessari nuovi dati.")
89
- sync_data_btn = gr.Button("🔄 Sincronizza Dati LinkedIn", variant="primary", visible=False, interactive=False)
90
- sync_status_html_output = gr.HTML("<p style='text-align:center;'>Stato sincronizzazione...</p>")
91
- dashboard_display_html = gr.HTML("<p style='text-align:center;'>Caricamento dashboard...</p>")
92
 
93
- # --- Tab 2: Analisi Grafici ---
94
  with gr.TabItem("2️⃣ Analisi Grafici", id="tab_analytics"):
95
- # Build UI and wire internal events within analytics_tab module
96
- (apply_filter_btn_analytics, date_filter_selector_analytics,
97
- custom_start_date_picker_analytics, custom_end_date_picker_analytics,
98
- analytics_status_md_ref, # Reference to the status markdown in analytics tab
99
- analytics_refresh_outputs_components, # list of components for refresh handler output
100
- analytics_refresh_outputs_plus_states # list of components + states for refresh handler output
101
- ) = analytics_tab.build_and_wire_tab(
102
- token_state, chat_histories_st, current_chat_plot_id_st,
103
- plot_data_for_chatbot_st, active_panel_action_state, explored_plot_id_state
104
- )
105
-
106
- # --- Tabs 3 & 4: Agentic Pipeline ---
107
- # build_and_wire_tabs will create TabItems internally
108
- agentic_pipeline_output_components = agentic_module.build_and_wire_tabs(
109
- orchestration_raw_results_st,
110
- key_results_for_selection_st,
111
- selected_key_result_ids_st
112
- )
113
 
 
 
 
114
 
115
- # --- Event Chaining & Orchestration ---
 
 
 
 
116
 
117
- # Initial Load Sequence (Simplified: direct calls, complex logic in handlers)
118
- def initial_load_sequence_wrapper(url_token, org_urn_val, current_state):
119
- # This function is primarily for the first tab's initial state.
120
- status_msg, new_state, btn_update = process_and_store_bubble_token(url_token, org_urn_val, current_state)
121
- dashboard_content = display_main_dashboard(new_state) # From ui_generators
122
- return status_msg, new_state, btn_update, dashboard_content
 
 
 
 
 
 
 
 
 
 
123
 
124
- # Outputs for the agentic pipeline handler
125
- # Order: report_display, key_results_cbg, okr_detail_display,
126
- # orchestration_raw_results_st, selected_key_result_ids_st, key_results_for_selection_st,
127
- # agentic_pipeline_status_md
128
- agentic_pipeline_full_outputs_list = agentic_pipeline_output_components[:3] + \
129
- [orchestration_raw_results_st, selected_key_result_ids_st, key_results_for_selection_st] + \
130
- [agentic_pipeline_output_components[3]]
131
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
- initial_load_event = org_urn_display.change(
134
- fn=initial_load_sequence_wrapper,
135
  inputs=[url_user_token_display, org_urn_display, token_state],
136
- outputs=[status_box, token_state, sync_data_btn, dashboard_display_html],
137
- show_progress="full"
 
 
 
 
 
138
  )
139
-
140
- # After initial load, refresh analytics graphs
141
  initial_load_event.then(
142
- fn=analytics_tab.handle_refresh_analytics_graphs,
143
- inputs=[token_state, date_filter_selector_analytics, custom_start_date_picker_analytics, custom_end_date_picker_analytics, chat_histories_st],
144
- outputs=analytics_refresh_outputs_plus_states, # Use the list from analytics_tab
145
- show_progress="full"
146
- ).then( # Then run agentic pipeline
147
- fn=agentic_module.handle_run_agentic_pipeline,
148
- inputs=[token_state, orchestration_raw_results_st, key_results_for_selection_st, selected_key_result_ids_st], # Pass states
149
- outputs=agentic_pipeline_full_outputs_list,
150
- show_progress="minimal"
 
151
  )
 
 
 
 
 
 
 
 
 
152
 
153
- # Sync Data Event Chain
154
- sync_event_part1 = sync_data_btn.click(
155
- fn=sync_all_linkedin_data_orchestrator,
 
 
 
 
 
 
156
  inputs=[token_state],
157
- outputs=[sync_status_html_output, token_state], # token_state is updated here
158
- show_progress="full"
159
  )
160
 
161
- # After sync, re-process token and update dashboard display (Tab 1)
162
  sync_event_part2 = sync_event_part1.then(
163
- fn=process_and_store_bubble_token, # This updates token_state again
164
- inputs=[url_user_token_display, org_urn_display, token_state], # Pass the updated token_state
165
- outputs=[status_box, token_state, sync_data_btn], # token_state updated again
166
- show_progress=False
167
- )
168
-
169
- # After token processing, re-run agentic pipeline with potentially new data
170
- sync_event_part2.then(
171
- fn=agentic_module.handle_run_agentic_pipeline,
172
- inputs=[token_state, orchestration_raw_results_st, key_results_for_selection_st, selected_key_result_ids_st], # Pass the latest token_state
173
- outputs=agentic_pipeline_full_outputs_list,
174
- show_progress="minimal"
175
  )
176
 
177
- # Then, update the main dashboard display on Tab 1
178
- sync_event_part3 = sync_event_part2.then(
179
- fn=display_main_dashboard,
180
- inputs=[token_state], # Use the latest token_state
181
- outputs=[dashboard_display_html],
182
- show_progress=False
 
 
 
 
 
 
 
 
183
  )
184
 
185
- # Finally, refresh analytics graphs on Tab 2
186
- sync_event_graphs_after_sync = sync_event_part3.then(
187
- fn=analytics_tab.handle_refresh_analytics_graphs,
188
- inputs=[token_state, date_filter_selector_analytics, custom_start_date_picker_analytics, custom_end_date_picker_analytics, chat_histories_st],
189
- outputs=analytics_refresh_outputs_plus_states, # Use the list from analytics_tab
190
- show_progress="full"
 
 
 
 
 
191
  )
192
 
193
- # --- App Launch ---
 
194
  if __name__ == "__main__":
195
- if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR): logging.warning(f"ATTENZIONE: '{LINKEDIN_CLIENT_ID_ENV_VAR}' non impostata.")
 
 
196
  if not all(os.environ.get(var) for var in [BUBBLE_APP_NAME_ENV_VAR, BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR]):
197
  logging.warning("ATTENZIONE: Variabili Bubble non impostate.")
198
 
199
- # AGENTIC_MODULES_LOADED is now checked within agentic_module.py, log from there if needed.
200
- # We can add a check here based on the import success if desired for app startup.
201
- if not agentic_module.AGENTIC_MODULES_LOADED: # Check the flag from the module
202
- logging.warning("CRITICAL: Agentic pipeline modules failed to load. Tabs 3 and 4 will be non-functional.")
203
- if not os.environ.get("GEMINI_API_KEY") and agentic_module.AGENTIC_MODULES_LOADED:
204
  logging.warning("ATTENZIONE: 'GEMINI_API_KEY' non impostata. La pipeline AI per le tab 3 e 4 potrebbe non funzionare.")
205
 
206
- try:
207
  logging.info(f"Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
208
- except ImportError:
209
  logging.warning("Matplotlib non trovato.")
210
 
211
  app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
 
5
  import logging
6
  import matplotlib
7
  matplotlib.use('Agg') # Set backend for Matplotlib to avoid GUI conflicts with Gradio
8
+ # import matplotlib.pyplot as plt # Not directly used in app.py anymore
9
+ import time # For profiling if needed
10
+ # from datetime import datetime, timedelta # Not directly used in app.py
11
+ # import numpy as np # Not directly used in app.py
12
+ # from collections import OrderedDict, defaultdict # Not directly used in app.py
13
+ import asyncio # For async operations
14
 
15
  # --- Module Imports ---
16
  from utils.gradio_utils import get_url_user_token
17
+
18
+ # Configuration (assuming these exist and are correctly defined)
19
  from config import (
20
  LINKEDIN_CLIENT_ID_ENV_VAR, BUBBLE_APP_NAME_ENV_VAR,
21
+ BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR,
22
+ # PLOT_ID_TO_FORMULA_KEY_MAP # Used in analytics_handlers
23
+ )
24
+ # from formulas import PLOT_FORMULAS # Used in analytics_handlers
25
+
26
+ # Services (assuming these exist and are correctly defined)
27
  from services.state_manager import process_and_store_bubble_token
28
  from services.sync_logic import sync_all_linkedin_data_orchestrator
 
29
 
30
+ # UI Generators (assuming these exist and are correctly defined)
31
+ from ui.ui_generators import display_main_dashboard #, build_analytics_tab_plot_area, BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON
 
32
 
33
+ # Tab Setup UI functions
34
+ from ui.dashboard_sync_tab import setup_dashboard_sync_tab
35
+ from ui.analytics_tab_setup import setup_analytics_tab
36
+ from ui.agentic_report_tab import setup_agentic_report_tab
37
+ from ui.agentic_okrs_tab import setup_agentic_okrs_tab
38
 
39
+ # Handler Classes
40
+ from handlers.dashboard_sync_handlers import DashboardSyncHandlers
41
+ from handlers.analytics_handlers import AnalyticsHandlers
42
+ from handlers.agentic_handlers import AgenticHandlers, AGENTIC_MODULES_LOADED as AGENTIC_HANDLERS_MODULES_LOADED
43
+
44
+ # Check consistency of AGENTIC_MODULES_LOADED
45
+ # This flag is crucial for conditional UI rendering and functionality.
46
+ # The one from AgenticHandlers is based on its own try-except for its specific imports.
47
+ # We might need a global one if app.py itself tries to import agentic modules directly.
48
+ # For now, using the one from AgenticHandlers as it's most relevant to agentic functionality.
49
+ APP_AGENTIC_MODULES_LOADED = AGENTIC_HANDLERS_MODULES_LOADED
50
 
51
  # Configure logging
52
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
53
 
54
+ # API Key Setup
55
+ os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "False"
 
 
56
  user_provided_api_key = os.environ.get("GEMINI_API_KEY")
57
  if user_provided_api_key:
58
  os.environ["GOOGLE_API_KEY"] = user_provided_api_key
59
  logging.info("GOOGLE_API_KEY environment variable has been set from GEMINI_API_KEY.")
60
  else:
61
+ logging.error("CRITICAL ERROR: The API key environment variable 'GEMINI_API_KEY' was not found.")
62
 
63
+ # --- Main Gradio Application ---
 
64
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
65
+ title="LinkedIn Organization Dashboard") as app:
66
+
67
+ # --- Global States ---
68
  token_state = gr.State(value={
69
  "token": None, "client_id": None, "org_urn": None,
70
  "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
71
+ "bubble_mentions_df": pd.DataFrame(),
72
+ "bubble_follower_stats_df": pd.DataFrame(),
73
  "fetch_count_for_api": 0, "url_user_token_temp_storage": None,
74
  "config_date_col_posts": "published_at", "config_date_col_mentions": "date",
75
  "config_date_col_followers": "date", "config_media_type_col": "media_type",
76
  "config_eb_labels_col": "li_eb_label"
77
  })
78
+
79
+ # States for analytics tab chatbot
80
+ chat_histories_st = gr.State({}) # Stores chat histories for each plot_id {plot_id: [{"role":"user",...}]}
81
+ current_chat_plot_id_st = gr.State(None) # ID of the plot currently active in chat
82
+ plot_data_for_chatbot_st = gr.State({}) # Stores summaries for plots {plot_id: "summary text"}
83
 
84
+ # States for analytics tab panel management
85
+ active_panel_action_state = gr.State(None) # Tracks current active panel e.g. {"plot_id": "X", "type": "insights"}
86
+ explored_plot_id_state = gr.State(None) # Tracks if a plot is being "explored" (others hidden)
 
 
 
87
 
88
+ # States for Agentic Pipeline
89
+ orchestration_raw_results_st = gr.State(None) # Stores raw output from run_full_analytics_orchestration
90
+ key_results_for_selection_st = gr.State([]) # Stores list of dicts for KR checkbox group choices
91
+ selected_key_result_ids_st = gr.State([]) # Stores list of selected KR unique IDs from checkbox group (though CBG value is source of truth)
92
 
93
+ # --- Hidden Components for URL Params ---
94
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
95
  url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
96
  status_box = gr.Textbox(label="Stato Generale Token LinkedIn", interactive=False, value="Inizializzazione...")
97
  org_urn_display = gr.Textbox(label="URN Organizzazione (Nascosto)", interactive=False, visible=False)
98
 
99
+ # --- Load URL parameters ---
100
+ # This runs on app load to fetch params from the URL query string
101
  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)
102
 
103
+ # --- UI Setup for Tabs ---
104
  with gr.Tabs() as tabs:
 
105
  with gr.TabItem("1️⃣ Dashboard & Sync", id="tab_dashboard_sync"):
106
+ dashboard_sync_components = setup_dashboard_sync_tab()
 
 
 
107
 
 
108
  with gr.TabItem("2️⃣ Analisi Grafici", id="tab_analytics"):
109
+ analytics_components = setup_analytics_tab() # This returns a dict of all components in the tab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ # Agentic tabs are conditional
112
+ agentic_report_components = {}
113
+ agentic_okrs_components = {}
114
 
115
+ with gr.TabItem("3️⃣ Agentic Analysis Report", id="tab_agentic_report", visible=APP_AGENTIC_MODULES_LOADED):
116
+ agentic_report_components = setup_agentic_report_tab(APP_AGENTIC_MODULES_LOADED)
117
+
118
+ with gr.TabItem("4️⃣ Agentic OKRs & Tasks", id="tab_agentic_okrs", visible=APP_AGENTIC_MODULES_LOADED):
119
+ agentic_okrs_components = setup_agentic_okrs_tab(APP_AGENTIC_MODULES_LOADED)
120
 
121
+ # --- Initialize Handlers ---
122
+ dashboard_sync_handler = DashboardSyncHandlers(
123
+ dashboard_components, token_state, url_user_token_display, org_urn_display, status_box
124
+ )
125
+
126
+ analytics_handler = AnalyticsHandlers(
127
+ analytics_components, token_state, chat_histories_st, current_chat_plot_id_st,
128
+ plot_data_for_chatbot_st, active_panel_action_state, explored_plot_id_state
129
+ )
130
+
131
+ agentic_handler = None
132
+ if APP_AGENTIC_MODULES_LOADED:
133
+ agentic_handler = AgenticHandlers(
134
+ agentic_report_components, agentic_okrs_components, token_state,
135
+ orchestration_raw_results_st, key_results_for_selection_st, selected_key_result_ids_st
136
+ )
137
 
138
+ # --- Setup Event Handlers from Handler Classes ---
139
+ # Dashboard/Sync handlers are mostly involved in chained events below
140
+ analytics_handler.setup_event_handlers() # Sets up internal events for analytics tab
141
+
142
+ if APP_AGENTIC_MODULES_LOADED and agentic_handler:
143
+ agentic_handler.setup_event_handlers() # Sets up internal events for agentic tabs (e.g., KR selection)
 
144
 
145
+ # --- Chained Event Logic (Initial Load & Sync) ---
146
+
147
+ # Outputs for the agentic pipeline run
148
+ # Ensure these components exist even if the tab is not visible, or handle None gracefully.
149
+ agentic_pipeline_outputs_list = [
150
+ agentic_report_components.get("agentic_report_display_md", gr.update()),
151
+ agentic_okrs_components.get("key_results_cbg", gr.update()),
152
+ agentic_okrs_components.get("okr_detail_display_md", gr.update()),
153
+ orchestration_raw_results_st,
154
+ selected_key_result_ids_st, # This state is primarily driven by CBG, but pipeline might reset it
155
+ key_results_for_selection_st,
156
+ agentic_report_components.get("agentic_pipeline_status_md", gr.update())
157
+ ]
158
+
159
+ # Initial Load Sequence:
160
+ # 1. Get URL token (app.load)
161
+ # 2. Process token, update dashboard (initial_load_sequence from dashboard_sync_handler)
162
+ # 3. Refresh analytics graphs (analytics_handler.refresh_analytics_graphs_ui)
163
+ # 4. Run agentic pipeline (agentic_handler.run_agentic_pipeline_autonomously_on_update)
164
 
165
+ initial_load_event = org_urn_display.change( # Triggers after app.load populates org_urn_display
166
+ fn=dashboard_sync_handler.initial_load_sequence,
167
  inputs=[url_user_token_display, org_urn_display, token_state],
168
+ outputs=[
169
+ status_box,
170
+ token_state,
171
+ dashboard_sync_components['sync_data_btn'],
172
+ dashboard_sync_components['dashboard_display_html']
173
+ ],
174
+ show_progress="full" # For the initial data processing part
175
  )
176
+
177
+ # Chain analytics refresh after initial load
178
  initial_load_event.then(
179
+ fn=analytics_handler.refresh_analytics_graphs_ui,
180
+ inputs=[
181
+ token_state,
182
+ analytics_components['date_filter_selector'],
183
+ analytics_components['custom_start_date_picker'],
184
+ analytics_components['custom_end_date_picker']
185
+ # chat_histories_st is accessed via self.chat_histories_st in the handler
186
+ ],
187
+ outputs=analytics_handler._get_graph_refresh_outputs_list(), # Get the list of output components
188
+ show_progress="full" # For graph generation
189
  )
190
+
191
+ # Chain agentic pipeline run after initial analytics refresh (if modules loaded)
192
+ if APP_AGENTIC_MODULES_LOADED and agentic_handler:
193
+ initial_load_event.then( # Chaining from initial_load_event ensures it uses the updated token_state
194
+ fn=agentic_handler.run_agentic_pipeline_autonomously_on_update,
195
+ inputs=[token_state], # Depends on the updated token_state
196
+ outputs=agentic_pipeline_outputs_list,
197
+ show_progress="minimal" # For agentic pipeline
198
+ )
199
 
200
+ # Sync Data Sequence:
201
+ # 1. sync_all_linkedin_data_orchestrator
202
+ # 2. process_and_store_bubble_token (to update state based on sync results)
203
+ # 3. display_main_dashboard
204
+ # 4. refresh_analytics_graphs_ui
205
+ # 5. run_agentic_pipeline_autonomously_on_update
206
+
207
+ sync_event_part1 = dashboard_sync_components['sync_data_btn'].click(
208
+ fn=sync_all_linkedin_data_orchestrator, # From services.sync_logic
209
  inputs=[token_state],
210
+ outputs=[dashboard_sync_components['sync_status_html_output'], token_state],
211
+ show_progress="full" # For the main sync operation
212
  )
213
 
 
214
  sync_event_part2 = sync_event_part1.then(
215
+ fn=process_and_store_bubble_token, # From services.state_manager
216
+ inputs=[url_user_token_display, org_urn_display, token_state], # Uses the updated token_state from part1
217
+ outputs=[status_box, token_state, dashboard_sync_components['sync_data_btn']],
218
+ show_progress=False # Quick state update
 
 
 
 
 
 
 
 
219
  )
220
 
221
+ # Chain agentic pipeline run after sync and token processing (if modules loaded)
222
+ if APP_AGENTIC_MODULES_LOADED and agentic_handler:
223
+ sync_event_part2.then(
224
+ fn=agentic_handler.run_agentic_pipeline_autonomously_on_update,
225
+ inputs=[token_state], # Uses the token_state updated by process_and_store_bubble_token
226
+ outputs=agentic_pipeline_outputs_list,
227
+ show_progress="minimal"
228
+ )
229
+
230
+ sync_event_part3 = sync_event_part2.then( # Continues from token processing
231
+ fn=display_main_dashboard, # From ui.ui_generators
232
+ inputs=[token_state], # Uses the updated token_state
233
+ outputs=[dashboard_sync_components['dashboard_display_html']],
234
+ show_progress=False # Quick UI update
235
  )
236
 
237
+ # Chain analytics refresh after dashboard update post-sync
238
+ sync_event_part3.then(
239
+ fn=analytics_handler.refresh_analytics_graphs_ui,
240
+ inputs=[
241
+ token_state,
242
+ analytics_components['date_filter_selector'],
243
+ analytics_components['custom_start_date_picker'],
244
+ analytics_components['custom_end_date_picker']
245
+ ],
246
+ outputs=analytics_handler._get_graph_refresh_outputs_list(),
247
+ show_progress="full" # For graph generation after sync
248
  )
249
 
250
+
251
+ # --- Launch ---
252
  if __name__ == "__main__":
253
+ # Environment variable checks (optional but good practice)
254
+ if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR):
255
+ logging.warning(f"ATTENZIONE: '{LINKEDIN_CLIENT_ID_ENV_VAR}' non impostata.")
256
  if not all(os.environ.get(var) for var in [BUBBLE_APP_NAME_ENV_VAR, BUBBLE_API_KEY_PRIVATE_ENV_VAR, BUBBLE_API_ENDPOINT_ENV_VAR]):
257
  logging.warning("ATTENZIONE: Variabili Bubble non impostate.")
258
 
259
+ if not APP_AGENTIC_MODULES_LOADED:
260
+ logging.warning("CRITICAL: Agentic pipeline modules failed to load. Agentic tabs (3 and 4) will be non-functional or hidden.")
261
+
262
+ if not os.environ.get("GEMINI_API_KEY") and APP_AGENTIC_MODULES_LOADED:
 
263
  logging.warning("ATTENZIONE: 'GEMINI_API_KEY' non impostata. La pipeline AI per le tab 3 e 4 potrebbe non funzionare.")
264
 
265
+ try:
266
  logging.info(f"Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
267
+ except ImportError:
268
  logging.warning("Matplotlib non trovato.")
269
 
270
  app.launch(server_name="0.0.0.0", server_port=7860, debug=True)