GuglielmoTor commited on
Commit
21988b0
·
verified ·
1 Parent(s): 4f37612

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -159
app.py CHANGED
@@ -3,60 +3,58 @@ import gradio as gr
3
  import pandas as pd
4
  import os
5
  import logging
 
6
  import matplotlib
7
  matplotlib.use('Agg') # Set backend for Matplotlib
8
- import matplotlib.pyplot as plt
9
- import time
10
- from datetime import datetime, timedelta
11
- import numpy as np
12
- from collections import OrderedDict, defaultdict # Added defaultdict
13
- import asyncio
14
 
15
  # --- Module Imports ---
16
  from utils.gradio_utils import get_url_user_token
17
 
18
  # Functions from newly created/refactored modules
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 # Keep this if used by AnalyticsTab
 
 
23
  )
24
- from services.state_manager import process_and_store_bubble_token
25
- from services.sync_logic import sync_all_linkedin_data_orchestrator
26
  from ui.ui_generators import (
27
  display_main_dashboard,
28
- build_analytics_tab_plot_area, # This will be passed to AnalyticsTab
29
- BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON # These will be passed
30
  )
31
- from ui.analytics_plot_generator import update_analytics_plots_figures, create_placeholder_plot # Pass these
32
- from formulas import PLOT_FORMULAS # Keep this if used by AnalyticsTab
33
 
34
- # --- EXISTING CHATBOT MODULE IMPORTS ---
35
- from features.chatbot.chatbot_prompts import get_initial_insight_prompt_and_suggestions # Pass this
36
- from features.chatbot.chatbot_handler import generate_llm_response # Pass this
37
 
38
- # --- NEW AGENTIC PIPELINE IMPORTS ---
39
  try:
40
- from run_agentic_pipeline import run_agentic_pipeline_autonomously
41
- from ui.insights_ui_generator import (
42
- format_single_okr_for_display
43
- )
44
  AGENTIC_MODULES_LOADED = True
45
- except:
46
- logging.error(f"Could not import agentic pipeline modules: {e}. Tabs 3 and 4 will be disabled.")
47
  AGENTIC_MODULES_LOADED = False
48
- def format_single_okr_for_display(okr_data, **kwargs): return "Agentic modules not loaded. OKR display unavailable." # Placeholder
49
-
 
 
 
 
50
 
51
-
52
- # --- IMPORT THE NEW ANALYTICS TAB MODULE ---
53
- from services.analytics_tab_module import AnalyticsTab # Assuming analytics_tab_module.py is in the services directory
54
 
55
  # Configure logging
56
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
57
 
58
  # API Key Setup
59
- os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "False"
60
  user_provided_api_key = os.environ.get("GEMINI_API_KEY")
61
  if user_provided_api_key:
62
  os.environ["GOOGLE_API_KEY"] = user_provided_api_key
@@ -66,45 +64,50 @@ else:
66
 
67
 
68
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
69
- title="LinkedIn Organization Dashboard") as app:
 
70
  token_state = gr.State(value={
71
  "token": None, "client_id": None, "org_urn": None,
72
  "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
73
- "bubble_mentions_df": pd.DataFrame(),
74
- "bubble_follower_stats_df": pd.DataFrame(),
75
- "fetch_count_for_api": 0, "url_user_token_temp_storage": None,
 
76
  "config_date_col_posts": "published_at", "config_date_col_mentions": "date",
77
  "config_date_col_followers": "date", "config_media_type_col": "media_type",
78
  "config_eb_labels_col": "li_eb_label"
79
  })
80
 
81
- # States for existing analytics tab chatbot - these are passed to AnalyticsTab
82
  chat_histories_st = gr.State({})
83
  current_chat_plot_id_st = gr.State(None)
84
- plot_data_for_chatbot_st = gr.State({}) # This will be populated by the analytics module's refresh
85
 
86
- # --- STATES FOR AGENTIC PIPELINE ---
87
- orchestration_raw_results_st = gr.State(None) # Stores the full raw output from the agentic pipeline
88
- key_results_for_selection_st = gr.State([]) # Stores the list of dicts for KR selection (label, id, etc.)
89
- selected_key_result_ids_st = gr.State([]) # Stores the unique_kr_ids selected in the CheckboxGroup
90
 
 
91
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
92
- url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
93
- status_box = gr.Textbox(label="Stato Generale Token LinkedIn", interactive=False, value="Inizializzazione...")
94
- org_urn_display = gr.Textbox(label="URN Organizzazione (Nascosto)", interactive=False, visible=False)
 
 
95
 
 
96
  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)
97
 
98
- def initial_load_sequence(url_token, org_urn_val, current_state):
99
- status_msg, new_state, btn_update = process_and_store_bubble_token(url_token, org_urn_val, current_state)
 
 
100
  dashboard_content = display_main_dashboard(new_state)
101
- return status_msg, new_state, btn_update, dashboard_content
102
 
103
- # --- Instantiate the AnalyticsTab module ---
104
- analytics_icons = {
105
- 'bomb': BOMB_ICON, 'explore': EXPLORE_ICON,
106
- 'formula': FORMULA_ICON, 'active': ACTIVE_ICON
107
- }
108
  analytics_tab_instance = AnalyticsTab(
109
  token_state=token_state,
110
  chat_histories_st=chat_histories_st,
@@ -121,56 +124,50 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
121
  )
122
 
123
  with gr.Tabs() as tabs:
124
- with gr.TabItem("1️⃣ Dashboard & Sync", id="tab_dashboard_sync"):
125
- gr.Markdown("Il sistema controlla i dati esistenti da Bubble. 'Sincronizza' si attiva se sono necessari nuovi dati.")
126
- sync_data_btn = gr.Button("🔄 Sincronizza Dati LinkedIn", variant="primary", visible=False, interactive=False)
127
- sync_status_html_output = gr.HTML("<p style='text-align:center;'>Stato sincronizzazione...</p>")
128
  dashboard_display_html = gr.HTML("<p style='text-align:center;'>Caricamento dashboard...</p>")
129
 
130
- # --- Use the AnalyticsTab module to create Tab 2 ---
131
  analytics_tab_instance.create_tab_ui()
132
 
133
- # --- Tab 3: Agentic Analysis Report ---
134
  with gr.TabItem("3️⃣ Agentic Analysis Report", id="tab_agentic_report", visible=AGENTIC_MODULES_LOADED):
135
- gr.Markdown("## 🤖 Comprehensive Analysis Report (AI Generated)")
136
- agentic_pipeline_status_md = gr.Markdown("Stato Pipeline AI (filtro 'Sempre'): In attesa...", visible=True)
137
- gr.Markdown("Questo report è generato da un agente AI con filtro 'Sempre' sui dati disponibili. Rivedi criticamente.")
138
- agentic_report_display_md = gr.Markdown("La pipeline AI si avvierà automaticamente dopo il caricamento iniziale dei dati o dopo una sincronizzazione.")
139
  if not AGENTIC_MODULES_LOADED:
140
- gr.Markdown("🔴 **Error:** Agentic pipeline modules could not be loaded. This tab is disabled.")
141
 
142
- # --- Tab 4: Agentic OKRs & Tasks ---
143
  with gr.TabItem("4️⃣ Agentic OKRs & Tasks", id="tab_agentic_okrs", visible=AGENTIC_MODULES_LOADED):
144
- gr.Markdown("## 🎯 AI Generated OKRs and Actionable Tasks (filtro 'Sempre')")
145
- gr.Markdown("Basato sull'analisi AI (filtro 'Sempre'), l'agente ha proposto i seguenti OKR e task. Seleziona i Key Results per dettagli.")
146
  if not AGENTIC_MODULES_LOADED:
147
- gr.Markdown("🔴 **Error:** Agentic pipeline modules could not be loaded. This tab is disabled.")
148
  with gr.Row():
149
  with gr.Column(scale=1):
150
- gr.Markdown("### Suggested Key Results (da analisi 'Sempre')")
151
  key_results_cbg = gr.CheckboxGroup(label="Select Key Results", choices=[], value=[], interactive=True)
152
  with gr.Column(scale=3):
153
  gr.Markdown("### Detailed OKRs and Tasks for Selected Key Results")
154
- okr_detail_display_md = gr.Markdown("I dettagli OKR appariranno qui dopo l'esecuzione della pipeline AI.")
155
 
 
156
  def update_okr_display_on_selection(selected_kr_unique_ids: list, raw_orchestration_results: dict, all_krs_for_selection: list):
157
  if not raw_orchestration_results or not AGENTIC_MODULES_LOADED:
158
- return gr.update(value="Nessun dato dalla pipeline AI o moduli non caricati.")
159
-
160
- actionable_okrs_dict = raw_orchestration_results.get("actionable_okrs_and_tasks")
161
  if not actionable_okrs_dict or not isinstance(actionable_okrs_dict.get("okrs"), list):
162
- return gr.update(value="Nessun OKR trovato nei risultati della pipeline.")
163
 
164
  okrs_list = actionable_okrs_dict["okrs"]
165
- # Ensure all_krs_for_selection is a list of dicts with expected keys
166
- if not all_krs_for_selection or not isinstance(all_krs_for_selection, list) or \
167
- not all(isinstance(kr, dict) and 'unique_kr_id' in kr and 'okr_index' in kr and 'kr_index' in kr for kr in all_krs_for_selection):
168
- logging.error("all_krs_for_selection is not in the expected format.")
169
  return gr.update(value="Errore interno: formato dati KR non valido.")
170
 
171
-
172
  kr_id_to_indices = {kr_info['unique_kr_id']: (kr_info['okr_index'], kr_info['kr_index']) for kr_info in all_krs_for_selection}
173
-
174
  selected_krs_by_okr_idx = defaultdict(list)
175
  if selected_kr_unique_ids:
176
  for kr_unique_id in selected_kr_unique_ids:
@@ -179,39 +176,26 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
179
  selected_krs_by_okr_idx[okr_idx].append(kr_idx)
180
 
181
  output_md_parts = []
182
- if not okrs_list:
183
- output_md_parts.append("Nessun OKR generato.")
184
- else:
185
- for okr_idx, okr_data in enumerate(okrs_list):
186
- accepted_indices_for_this_okr = selected_krs_by_okr_idx.get(okr_idx)
187
- # If specific KRs are selected, only show OKRs that have at least one of the selected KRs
188
- # OR if no KRs are selected at all, show all OKRs.
189
- if selected_kr_unique_ids: # User has made a selection
190
- if accepted_indices_for_this_okr is not None: # This OKR has some of the selected KRs
191
- output_md_parts.append(format_single_okr_for_display(okr_data, accepted_kr_indices=accepted_indices_for_this_okr, okr_main_index=okr_idx))
192
- else: # No KRs selected, show all OKRs with all their KRs
193
- output_md_parts.append(format_single_okr_for_display(okr_data, accepted_kr_indices=None, okr_main_index=okr_idx))
194
-
195
- if not output_md_parts and selected_kr_unique_ids:
196
- final_md = "Nessun OKR corrisponde alla selezione corrente o i KR selezionati non hanno task dettagliati."
197
- elif not output_md_parts and not selected_kr_unique_ids: # Should be covered by "Nessun OKR generato."
198
- final_md = "Nessun OKR generato."
199
- else:
200
- final_md = "\n\n---\n\n".join(output_md_parts)
201
  return gr.update(value=final_md)
202
 
203
  if AGENTIC_MODULES_LOADED:
204
  key_results_cbg.change(
205
  fn=update_okr_display_on_selection,
206
  inputs=[key_results_cbg, orchestration_raw_results_st, key_results_for_selection_st],
207
- outputs=[okr_detail_display_md],
208
- api_name="update_okr_display_on_selection_module"
209
  )
210
 
211
-
212
- # Define the output list for the agentic pipeline callbacks
213
- # Order: Report MD, KR CBG, OKR Detail MD, RawResults State, SelectedKRIDs State, KRList State, Status MD
214
- agentic_pipeline_outputs_list = [
215
  agentic_report_display_md,
216
  key_results_cbg,
217
  okr_detail_display_md,
@@ -220,16 +204,16 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
220
  key_results_for_selection_st,
221
  agentic_pipeline_status_md
222
  ]
223
- agentic_pipeline_inputs = [token_state] # Input for the autonomous run
224
 
225
- # --- Event Handling ---
226
  initial_load_event = org_urn_display.change(
227
- fn=initial_load_sequence,
228
  inputs=[url_user_token_display, org_urn_display, token_state],
229
- outputs=[status_box, token_state, sync_data_btn, dashboard_display_html],
230
  show_progress="full"
231
  )
232
 
 
233
  initial_load_event.then(
234
  fn=analytics_tab_instance._refresh_analytics_graphs_ui,
235
  inputs=[
@@ -241,65 +225,23 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
241
  ],
242
  outputs=analytics_tab_instance.graph_refresh_outputs_list,
243
  show_progress="full"
 
244
  ).then(
245
- fn=run_agentic_pipeline_autonomously, # Generator function
246
- inputs=[token_state, orchestration_raw_results_st, selected_key_result_ids_st, key_results_for_selection_st],
247
- outputs=agentic_pipeline_outputs_list,
248
- show_progress="minimal" # Use minimal for generators that yield status
249
- )
250
-
251
- sync_event_part1 = sync_data_btn.click(
252
- fn=sync_all_linkedin_data_orchestrator,
253
- inputs=[token_state],
254
- outputs=[sync_status_html_output, token_state],
255
- show_progress="full"
256
- )
257
- sync_event_part2 = sync_event_part1.then(
258
- fn=process_and_store_bubble_token,
259
- inputs=[url_user_token_display, org_urn_display, token_state],
260
- outputs=[status_box, token_state, sync_data_btn],
261
- show_progress=False
262
- )
263
- sync_event_part2.then(
264
- fn=run_agentic_pipeline_autonomously, # Generator function
265
  inputs=[token_state, orchestration_raw_results_st, selected_key_result_ids_st, key_results_for_selection_st],
266
- outputs=agentic_pipeline_outputs_list,
267
  show_progress="minimal"
268
  )
269
- sync_event_part3 = sync_event_part2.then(
270
- fn=display_main_dashboard,
271
- inputs=[token_state],
272
- outputs=[dashboard_display_html],
273
- show_progress=False
274
- )
275
- sync_event_graphs_after_sync = sync_event_part3.then(
276
- fn=analytics_tab_instance._refresh_analytics_graphs_ui,
277
- inputs=[
278
- token_state,
279
- analytics_tab_instance.date_filter_selector,
280
- analytics_tab_instance.custom_start_date_picker,
281
- analytics_tab_instance.custom_end_date_picker,
282
- chat_histories_st
283
- ],
284
- outputs=analytics_tab_instance.graph_refresh_outputs_list,
285
- show_progress="full"
286
- )
287
 
288
  if __name__ == "__main__":
 
289
  if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR):
290
- logging.warning(f"ATTENZIONE: '{LINKEDIN_CLIENT_ID_ENV_VAR}' non impostata.")
291
  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]):
292
- logging.warning("ATTENZIONE: Una o più variabili d'ambiente Bubble (BUBBLE_APP_NAME, BUBBLE_API_KEY_PRIVATE, BUBBLE_API_ENDPOINT) non sono impostate.")
293
  if not AGENTIC_MODULES_LOADED:
294
- logging.warning("CRITICAL: Agentic pipeline modules failed to load. Tabs 3 and 4 (Agentic Report & OKRs) will be non-functional.")
295
- if not os.environ.get("GEMINI_API_KEY"): # Check GEMINI_API_KEY directly as GOOGLE_API_KEY is derived
296
- logging.warning("ATTENZIONE: 'GEMINI_API_KEY' non impostata. Questo è necessario per le funzionalità AI, incluse le tab agentiche e il chatbot dei grafici.")
297
-
298
- try:
299
- logging.info(f"Gradio version: {gr.__version__}")
300
- logging.info(f"Pandas version: {pd.__version__}")
301
- logging.info(f"Matplotlib version: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
302
- except Exception as e:
303
- logging.warning(f"Could not log library versions: {e}")
304
 
305
  app.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)), debug=True)
 
3
  import pandas as pd
4
  import os
5
  import logging
6
+ from collections import defaultdict
7
  import matplotlib
8
  matplotlib.use('Agg') # Set backend for Matplotlib
 
 
 
 
 
 
9
 
10
  # --- Module Imports ---
11
  from utils.gradio_utils import get_url_user_token
12
 
13
  # Functions from newly created/refactored modules
14
  from config import (
15
+ PLOT_ID_TO_FORMULA_KEY_MAP,
16
+ LINKEDIN_CLIENT_ID_ENV_VAR,
17
+ BUBBLE_APP_NAME_ENV_VAR,
18
+ BUBBLE_API_KEY_PRIVATE_ENV_VAR,
19
+ BUBBLE_API_ENDPOINT_ENV_VAR
20
  )
21
+ # UPDATED: Using the new data loading function from the refactored state manager
22
+ from services.state_manager import load_data_from_bubble
23
  from ui.ui_generators import (
24
  display_main_dashboard,
25
+ build_analytics_tab_plot_area,
26
+ BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON
27
  )
28
+ from ui.analytics_plot_generator import update_analytics_plots_figures, create_placeholder_plot
29
+ from formulas import PLOT_FORMULAS
30
 
31
+ # --- CHATBOT MODULE IMPORTS ---
32
+ from features.chatbot.chatbot_prompts import get_initial_insight_prompt_and_suggestions
33
+ from features.chatbot.chatbot_handler import generate_llm_response
34
 
35
+ # --- AGENTIC PIPELINE (DISPLAY ONLY) IMPORTS ---
36
  try:
37
+ # UPDATED: Using the new display function to show pre-computed results
38
+ from run_agentic_pipeline import load_and_display_agentic_results
39
+ from ui.insights_ui_generator import format_single_okr_for_display
 
40
  AGENTIC_MODULES_LOADED = True
41
+ except ImportError as e:
42
+ logging.error(f"Could not import agentic pipeline display modules: {e}. Tabs 3 and 4 will be disabled.")
43
  AGENTIC_MODULES_LOADED = False
44
+ # Placeholder for the new function name if imports fail
45
+ def load_and_display_agentic_results(*args, **kwargs):
46
+ # This tuple matches the expected number of outputs for the event handler
47
+ return "Modules not loaded.", "Modules not loaded.", "Modules not loaded.", None, [], [], "Error"
48
+ def format_single_okr_for_display(okr_data, **kwargs):
49
+ return "Agentic modules not loaded. OKR display unavailable."
50
 
51
+ # --- ANALYTICS TAB MODULE IMPORT ---
52
+ from services.analytics_tab_module import AnalyticsTab
 
53
 
54
  # Configure logging
55
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
56
 
57
  # API Key Setup
 
58
  user_provided_api_key = os.environ.get("GEMINI_API_KEY")
59
  if user_provided_api_key:
60
  os.environ["GOOGLE_API_KEY"] = user_provided_api_key
 
64
 
65
 
66
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
67
+ title="LinkedIn Organization Dashboard") as app:
68
+ # --- STATE MANAGEMENT ---
69
  token_state = gr.State(value={
70
  "token": None, "client_id": None, "org_urn": None,
71
  "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
72
+ "bubble_mentions_df": pd.DataFrame(), "bubble_follower_stats_df": pd.DataFrame(),
73
+ "bubble_agentic_analysis_data": pd.DataFrame(), # To store agentic results from Bubble
74
+ "url_user_token_temp_storage": None,
75
+ # Config values remain useful for display components
76
  "config_date_col_posts": "published_at", "config_date_col_mentions": "date",
77
  "config_date_col_followers": "date", "config_media_type_col": "media_type",
78
  "config_eb_labels_col": "li_eb_label"
79
  })
80
 
81
+ # States for analytics tab chatbot
82
  chat_histories_st = gr.State({})
83
  current_chat_plot_id_st = gr.State(None)
84
+ plot_data_for_chatbot_st = gr.State({})
85
 
86
+ # States for agentic results display
87
+ orchestration_raw_results_st = gr.State(None) # Stores reconstructed report/OKR dict from Bubble
88
+ key_results_for_selection_st = gr.State([]) # Stores list of dicts for KR selection
89
+ selected_key_result_ids_st = gr.State([]) # Stores unique_kr_ids selected by the user
90
 
91
+ # --- UI LAYOUT ---
92
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
93
+ # Hidden components to receive URL parameters
94
+ url_user_token_display = gr.Textbox(label="User Token (Hidden)", interactive=False, visible=False)
95
+ org_urn_display = gr.Textbox(label="Org URN (Hidden)", interactive=False, visible=False)
96
+ # General status display
97
+ status_box = gr.Textbox(label="Status", interactive=False, value="Initializing...")
98
 
99
+ # Load URL parameters on page load
100
  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)
101
 
102
+ # UPDATED: Simplified initial data loading sequence
103
+ def initial_data_load_sequence(url_token, org_urn_val, current_state):
104
+ # This function now only loads data from Bubble and updates the main dashboard display
105
+ status_msg, new_state = load_data_from_bubble(url_token, org_urn_val, current_state)
106
  dashboard_content = display_main_dashboard(new_state)
107
+ return status_msg, new_state, dashboard_content
108
 
109
+ # Instantiate the AnalyticsTab module (no changes needed here)
110
+ analytics_icons = {'bomb': BOMB_ICON, 'explore': EXPLORE_ICON, 'formula': FORMULA_ICON, 'active': ACTIVE_ICON}
 
 
 
111
  analytics_tab_instance = AnalyticsTab(
112
  token_state=token_state,
113
  chat_histories_st=chat_histories_st,
 
124
  )
125
 
126
  with gr.Tabs() as tabs:
127
+ with gr.TabItem("1️⃣ Dashboard", id="tab_dashboard"):
128
+ # REMOVED: Sync button and related UI components. This tab is now just for the main dashboard.
129
+ gr.Markdown("I dati visualizzati in questo pannello sono caricati direttamente da Bubble.io.")
 
130
  dashboard_display_html = gr.HTML("<p style='text-align:center;'>Caricamento dashboard...</p>")
131
 
132
+ # Use the AnalyticsTab module to create Tab 2
133
  analytics_tab_instance.create_tab_ui()
134
 
135
+ # Tab 3: Agentic Analysis Report
136
  with gr.TabItem("3️⃣ Agentic Analysis Report", id="tab_agentic_report", visible=AGENTIC_MODULES_LOADED):
137
+ gr.Markdown("## 🤖 Comprehensive Analysis Report (from Bubble.io)")
138
+ agentic_pipeline_status_md = gr.Markdown("Status: Loading report data...", visible=True)
139
+ gr.Markdown("Questo report è stato pre-generato da un agente AI e caricato da Bubble.io.")
140
+ agentic_report_display_md = gr.Markdown("The AI-generated report will be displayed here once loaded.")
141
  if not AGENTIC_MODULES_LOADED:
142
+ gr.Markdown("🔴 **Error:** Agentic pipeline display modules could not be loaded. This tab is disabled.")
143
 
144
+ # Tab 4: Agentic OKRs & Tasks
145
  with gr.TabItem("4️⃣ Agentic OKRs & Tasks", id="tab_agentic_okrs", visible=AGENTIC_MODULES_LOADED):
146
+ gr.Markdown("## 🎯 AI Generated OKRs and Actionable Tasks (from Bubble.io)")
147
+ gr.Markdown("Basato sull'analisi AI pre-generata, l'agente ha proposto i seguenti OKR. Seleziona i Key Results per dettagli.")
148
  if not AGENTIC_MODULES_LOADED:
149
+ gr.Markdown("🔴 **Error:** Agentic pipeline display modules could not be loaded. This tab is disabled.")
150
  with gr.Row():
151
  with gr.Column(scale=1):
152
+ gr.Markdown("### Suggested Key Results")
153
  key_results_cbg = gr.CheckboxGroup(label="Select Key Results", choices=[], value=[], interactive=True)
154
  with gr.Column(scale=3):
155
  gr.Markdown("### Detailed OKRs and Tasks for Selected Key Results")
156
+ okr_detail_display_md = gr.Markdown("I dettagli OKR appariranno qui dopo il caricamento dei dati.")
157
 
158
+ # This handler logic for the CheckboxGroup remains the same, as it operates on loaded data.
159
  def update_okr_display_on_selection(selected_kr_unique_ids: list, raw_orchestration_results: dict, all_krs_for_selection: list):
160
  if not raw_orchestration_results or not AGENTIC_MODULES_LOADED:
161
+ return gr.update(value="Nessun dato di analisi caricato o moduli non disponibili.")
162
+ actionable_okrs_dict = raw_orchestration_results.get("actionable_okrs")
 
163
  if not actionable_okrs_dict or not isinstance(actionable_okrs_dict.get("okrs"), list):
164
+ return gr.update(value="Nessun OKR trovato nei dati di analisi caricati.")
165
 
166
  okrs_list = actionable_okrs_dict["okrs"]
167
+ if not all_krs_for_selection or not isinstance(all_krs_for_selection, list):
 
 
 
168
  return gr.update(value="Errore interno: formato dati KR non valido.")
169
 
 
170
  kr_id_to_indices = {kr_info['unique_kr_id']: (kr_info['okr_index'], kr_info['kr_index']) for kr_info in all_krs_for_selection}
 
171
  selected_krs_by_okr_idx = defaultdict(list)
172
  if selected_kr_unique_ids:
173
  for kr_unique_id in selected_kr_unique_ids:
 
176
  selected_krs_by_okr_idx[okr_idx].append(kr_idx)
177
 
178
  output_md_parts = []
179
+ for okr_idx, okr_data in enumerate(okrs_list):
180
+ if not selected_kr_unique_ids: # Show all if nothing is selected
181
+ output_md_parts.append(format_single_okr_for_display(okr_data, accepted_kr_indices=None, okr_main_index=okr_idx))
182
+ elif okr_idx in selected_krs_by_okr_idx: # Show only OKRs that have a selected KR
183
+ accepted_indices = selected_krs_by_okr_idx.get(okr_idx)
184
+ output_md_parts.append(format_single_okr_for_display(okr_data, accepted_kr_indices=accepted_indices, okr_main_index=okr_idx))
185
+
186
+ final_md = "\n\n---\n\n".join(output_md_parts) if output_md_parts else "Nessun OKR corrisponde alla selezione corrente."
 
 
 
 
 
 
 
 
 
 
 
187
  return gr.update(value=final_md)
188
 
189
  if AGENTIC_MODULES_LOADED:
190
  key_results_cbg.change(
191
  fn=update_okr_display_on_selection,
192
  inputs=[key_results_cbg, orchestration_raw_results_st, key_results_for_selection_st],
193
+ outputs=[okr_detail_display_md]
 
194
  )
195
 
196
+ # --- EVENT HANDLING (SIMPLIFIED) ---
197
+ # Define the output list for loading agentic results
198
+ agentic_display_outputs = [
 
199
  agentic_report_display_md,
200
  key_results_cbg,
201
  okr_detail_display_md,
 
204
  key_results_for_selection_st,
205
  agentic_pipeline_status_md
206
  ]
 
207
 
208
+ # This is the main event chain that runs when the app loads
209
  initial_load_event = org_urn_display.change(
210
+ fn=initial_data_load_sequence,
211
  inputs=[url_user_token_display, org_urn_display, token_state],
212
+ outputs=[status_box, token_state, dashboard_display_html],
213
  show_progress="full"
214
  )
215
 
216
+ # After initial data is loaded, refresh the analytics graphs
217
  initial_load_event.then(
218
  fn=analytics_tab_instance._refresh_analytics_graphs_ui,
219
  inputs=[
 
225
  ],
226
  outputs=analytics_tab_instance.graph_refresh_outputs_list,
227
  show_progress="full"
228
+ # Then, load and display the pre-computed agentic results
229
  ).then(
230
+ fn=load_and_display_agentic_results, # UPDATED function call
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  inputs=[token_state, orchestration_raw_results_st, selected_key_result_ids_st, key_results_for_selection_st],
232
+ outputs=agentic_display_outputs,
233
  show_progress="minimal"
234
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
 
236
  if __name__ == "__main__":
237
+ # Environment variable checks remain important
238
  if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR):
239
+ logging.warning(f"WARNING: '{LINKEDIN_CLIENT_ID_ENV_VAR}' is not set.")
240
  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]):
241
+ logging.warning("WARNING: One or more Bubble environment variables are not set.")
242
  if not AGENTIC_MODULES_LOADED:
243
+ logging.warning("CRITICAL: Agentic pipeline display modules failed to load. Tabs 3 and 4 will be non-functional.")
244
+ if not os.environ.get("GEMINI_API_KEY"):
245
+ logging.warning("WARNING: 'GEMINI_API_KEY' is not set. This may be needed for chatbot features.")
 
 
 
 
 
 
 
246
 
247
  app.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)), debug=True)