GuglielmoTor commited on
Commit
7aa6c73
·
verified ·
1 Parent(s): 810c600

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +325 -88
app.py CHANGED
@@ -9,7 +9,7 @@ import matplotlib.pyplot as plt
9
  import time # For profiling if needed
10
  from datetime import datetime, timedelta # Added timedelta
11
  import numpy as np
12
- from collections import OrderedDict # To maintain section order
13
  import asyncio # For async operations
14
 
15
  # --- Module Imports ---
@@ -36,6 +36,24 @@ from chatbot_prompts import get_initial_insight_prompt_and_suggestions # MODIFIE
36
  from chatbot_handler import generate_llm_response
37
  # --- END EXISTING CHATBOT MODULE IMPORTS ---
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  # Configure logging
40
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
41
 
@@ -58,6 +76,14 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
58
  current_chat_plot_id_st = gr.State(None)
59
  plot_data_for_chatbot_st = gr.State({})
60
 
 
 
 
 
 
 
 
 
61
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
62
  url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
63
  status_box = gr.Textbox(label="Stato Generale Token LinkedIn", interactive=False, value="Inizializzazione...")
@@ -68,6 +94,10 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
68
  def initial_load_sequence(url_token, org_urn_val, current_state):
69
  status_msg, new_state, btn_update = process_and_store_bubble_token(url_token, org_urn_val, current_state)
70
  dashboard_content = display_main_dashboard(new_state)
 
 
 
 
71
  return status_msg, new_state, btn_update, dashboard_content
72
 
73
  with gr.Tabs() as tabs:
@@ -247,7 +277,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
247
  for _ in plot_configs:
248
  generated_panel_vis_updates.append(gr.update(visible=True))
249
  generated_explore_btn_updates.append(gr.update(value=EXPLORE_ICON))
250
-
251
  if action_type == "insights": new_current_chat_plot_id = None
252
 
253
  else: # Toggling ON a new action or switching actions
@@ -307,7 +337,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
307
  ]
308
 
309
  final_updates.extend(generated_panel_vis_updates)
310
- final_updates.extend(generated_bomb_btn_updates)
311
  final_updates.extend(generated_formula_btn_updates)
312
  final_updates.extend(generated_explore_btn_updates)
313
  final_updates.extend(section_title_vis_updates)
@@ -400,13 +430,12 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
400
  action_col_upd = gr.update(visible=False)
401
  new_active_panel_state_upd = None
402
  formula_hint_upd = gr.update(visible=False)
403
- for _ in plot_configs:
404
- bomb_btns_updates.append(gr.update(value=BOMB_ICON))
405
- formula_btns_updates.append(gr.update(value=FORMULA_ICON))
406
- else:
407
- for _ in plot_configs:
408
- bomb_btns_updates.append(gr.update())
409
- formula_btns_updates.append(gr.update())
410
 
411
  final_explore_updates = [
412
  new_explored_id_to_set, action_col_upd, new_active_panel_state_upd, formula_hint_upd
@@ -432,7 +461,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
432
 
433
  action_panel_outputs_list = _base_action_panel_ui_outputs + _action_panel_state_outputs
434
  action_panel_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("panel_component", gr.update()) for pc in plot_configs])
435
- action_panel_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("bomb_button", gr.update()) for pc in plot_configs])
436
  action_panel_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("formula_button", gr.update()) for pc in plot_configs])
437
  action_panel_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("explore_button", gr.update()) for pc in plot_configs])
438
  action_panel_outputs_list.extend([section_titles_map.get(s_name, gr.update()) for s_name in unique_ordered_sections])
@@ -441,7 +470,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
441
  explore_outputs_list = _explore_base_outputs
442
  explore_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("panel_component", gr.update()) for pc in plot_configs])
443
  explore_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("explore_button", gr.update()) for pc in plot_configs])
444
- explore_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("bomb_button", gr.update()) for pc in plot_configs])
445
  explore_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("formula_button", gr.update()) for pc in plot_configs])
446
  explore_outputs_list.extend([section_titles_map.get(s_name, gr.update()) for s_name in unique_ordered_sections])
447
 
@@ -462,13 +491,12 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
462
  if ui_obj.get("formula_button"):
463
  ui_obj["formula_button"].click(fn=create_panel_action_handler(plot_id, "formula"), inputs=action_click_inputs, outputs=action_panel_outputs_list, api_name=f"action_formula_{plot_id}")
464
  if ui_obj.get("explore_button"):
465
- # Original lambda was not async, ensure it matches handle_explore_click signature and type
466
  ui_obj["explore_button"].click(
467
  fn=lambda current_explored_val, current_active_panel_val, p_id=plot_id: handle_explore_click(p_id, current_explored_val, current_active_panel_val),
468
  inputs=explore_click_inputs,
469
  outputs=explore_outputs_list,
470
  api_name=f"action_explore_{plot_id}"
471
- ) # if handle_explore_click becomes async, this needs 'await' or be wrapped
472
  else: logging.warning(f"UI object for plot_id '{plot_id}' not found for click handlers.")
473
 
474
 
@@ -481,79 +509,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
481
  insights_suggestion_2_btn.click(fn=handle_suggested_question_click, inputs=[insights_suggestion_2_btn] + suggestion_click_inputs_base, outputs=chat_submission_outputs, api_name="click_suggestion_2")
482
  insights_suggestion_3_btn.click(fn=handle_suggested_question_click, inputs=[insights_suggestion_3_btn] + suggestion_click_inputs_base, outputs=chat_submission_outputs, api_name="click_suggestion_3")
483
 
484
-
485
- def refresh_all_analytics_ui_elements(current_token_state_val, date_filter_val, custom_start_val, custom_end_val, current_chat_histories_val):
486
- # This function remains synchronous as per original
487
- logging.info("Refreshing all analytics UI elements and resetting actions/chat.")
488
- plot_gen_results = update_analytics_plots_figures(current_token_state_val, date_filter_val, custom_start_val, custom_end_val, plot_configs)
489
- status_msg, gen_figs, new_summaries = plot_gen_results[0], plot_gen_results[1:-1], plot_gen_results[-1]
490
-
491
- all_updates = [status_msg]
492
- all_updates.extend(gen_figs if len(gen_figs) == len(plot_configs) else [create_placeholder_plot("Error", f"Fig missing {i}") for i in range(len(plot_configs))])
493
-
494
- all_updates.extend([
495
- gr.update(visible=False),
496
- gr.update(value=[], visible=False),
497
- gr.update(value="", visible=False),
498
- gr.update(visible=False),
499
- gr.update(value="S1"), gr.update(value="S2"), gr.update(value="S3"),
500
- gr.update(value="Formula details here.", visible=False),
501
- gr.update(visible=False)
502
- ])
503
-
504
- all_updates.extend([
505
- None,
506
- None,
507
- {},
508
- new_summaries
509
- ])
510
-
511
- for _ in plot_configs:
512
- all_updates.extend([
513
- gr.update(value=BOMB_ICON),
514
- gr.update(value=FORMULA_ICON),
515
- gr.update(value=EXPLORE_ICON),
516
- gr.update(visible=True)
517
- ])
518
-
519
- all_updates.append(None)
520
- all_updates.extend([gr.update(visible=True)] * num_unique_sections)
521
-
522
- logging.info(f"Prepared {len(all_updates)} updates for analytics refresh. Expected {15 + 5*len(plot_configs) + num_unique_sections}.")
523
- return all_updates
524
-
525
- apply_filter_and_sync_outputs_list = [analytics_status_md]
526
- apply_filter_and_sync_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("plot_component", gr.update()) for pc in plot_configs])
527
-
528
- _ui_resets_for_filter = [
529
- global_actions_column_ui, insights_chatbot_ui, insights_chat_input_ui,
530
- insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
531
- formula_display_markdown_ui, formula_close_hint_md
532
- ]
533
- apply_filter_and_sync_outputs_list.extend(_ui_resets_for_filter)
534
-
535
- _state_resets_for_filter = [active_panel_action_state, current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st]
536
- apply_filter_and_sync_outputs_list.extend(_state_resets_for_filter)
537
-
538
- for pc in plot_configs:
539
- pid = pc["id"]
540
- apply_filter_and_sync_outputs_list.extend([
541
- plot_ui_objects.get(pid, {}).get("bomb_button", gr.update()),
542
- plot_ui_objects.get(pid, {}).get("formula_button", gr.update()),
543
- plot_ui_objects.get(pid, {}).get("explore_button", gr.update()),
544
- plot_ui_objects.get(pid, {}).get("panel_component", gr.update())
545
- ])
546
-
547
- apply_filter_and_sync_outputs_list.append(explored_plot_id_state)
548
-
549
- apply_filter_and_sync_outputs_list.extend([section_titles_map.get(s_name, gr.update()) for s_name in unique_ordered_sections])
550
-
551
- apply_filter_btn.click(
552
- fn=refresh_all_analytics_ui_elements,
553
- inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
554
- outputs=apply_filter_and_sync_outputs_list, show_progress="full"
555
- )
556
-
557
  with gr.TabItem("3️⃣ Menzioni", id="tab_mentions"):
558
  refresh_mentions_display_btn = gr.Button("🔄 Aggiorna Visualizzazione Menzioni", variant="secondary")
559
  mentions_html = gr.HTML("Dati menzioni...")
@@ -579,13 +534,290 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
579
  show_progress="full"
580
  )
581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
 
583
  # --- Sync Events (at the end of the app's 'with gr.Blocks()' context) ---
584
  sync_event_part1 = sync_data_btn.click(fn=sync_all_linkedin_data_orchestrator, inputs=[token_state], outputs=[sync_status_html_output, token_state], show_progress="full")
585
  sync_event_part2 = sync_event_part1.then(fn=process_and_store_bubble_token, inputs=[url_user_token_display, org_urn_display, token_state], outputs=[status_box, token_state, sync_data_btn], show_progress=False)
586
  sync_event_part3 = sync_event_part2.then(fn=display_main_dashboard, inputs=[token_state], outputs=[dashboard_display_html], show_progress=False)
587
  sync_event_final = sync_event_part3.then(
588
- fn=refresh_all_analytics_ui_elements, # This is synchronous
589
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
590
  outputs=apply_filter_and_sync_outputs_list,
591
  show_progress="full"
@@ -597,6 +829,11 @@ if __name__ == "__main__":
597
  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]):
598
  logging.warning("ATTENZIONE: Variabili Bubble non impostate.")
599
 
 
 
 
 
 
600
  try: logging.info(f"Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
601
  except ImportError: logging.warning("Matplotlib non trovato.")
602
 
 
9
  import time # For profiling if needed
10
  from datetime import datetime, timedelta # Added timedelta
11
  import numpy as np
12
+ from collections import OrderedDict, defaultdict # To maintain section order and for OKR processing
13
  import asyncio # For async operations
14
 
15
  # --- Module Imports ---
 
36
  from chatbot_handler import generate_llm_response
37
  # --- END EXISTING CHATBOT MODULE IMPORTS ---
38
 
39
+ # --- NEW AGENTIC PIPELINE IMPORTS ---
40
+ try:
41
+ from run_agentic_pipeline import run_full_analytics_orchestration
42
+ from insights_ui_generator import (
43
+ format_report_to_markdown,
44
+ extract_key_results_for_selection,
45
+ format_single_okr_for_display
46
+ )
47
+ AGENTIC_MODULES_LOADED = True
48
+ except ImportError as e:
49
+ logging.error(f"Could not import agentic pipeline modules: {e}. Tabs 5 and 6 will be disabled.")
50
+ AGENTIC_MODULES_LOADED = False
51
+ # Define placeholder functions if modules are not loaded to avoid NameErrors
52
+ async def run_full_analytics_orchestration(*args, **kwargs): return None
53
+ def format_report_to_markdown(report_string): return "Agentic modules not loaded. Report unavailable."
54
+ def extract_key_results_for_selection(okrs_dict): return []
55
+ def format_single_okr_for_display(okr_data, **kwargs): return "Agentic modules not loaded. OKR display unavailable."
56
+
57
  # Configure logging
58
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
59
 
 
76
  current_chat_plot_id_st = gr.State(None)
77
  plot_data_for_chatbot_st = gr.State({})
78
 
79
+ # --- NEW STATES FOR AGENTIC PIPELINE ---
80
+ orchestration_raw_results_st = gr.State(None)
81
+ # Stores list of dicts: {'okr_index', 'kr_index', 'okr_objective', 'kr_description', 'unique_kr_id'}
82
+ key_results_for_selection_st = gr.State([])
83
+ # Stores list of unique_kr_id strings that are selected by the user
84
+ selected_key_result_ids_st = gr.State([])
85
+
86
+
87
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
88
  url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
89
  status_box = gr.Textbox(label="Stato Generale Token LinkedIn", interactive=False, value="Inizializzazione...")
 
94
  def initial_load_sequence(url_token, org_urn_val, current_state):
95
  status_msg, new_state, btn_update = process_and_store_bubble_token(url_token, org_urn_val, current_state)
96
  dashboard_content = display_main_dashboard(new_state)
97
+ # Initial call to populate analytics plots with default "Sempre"
98
+ # This will now also trigger the agentic pipeline for the first time.
99
+ # However, the outputs for agentic tabs are handled by apply_filter_btn.click and sync.then
100
+ # So, we don't need to return agentic UI updates from here directly.
101
  return status_msg, new_state, btn_update, dashboard_content
102
 
103
  with gr.Tabs() as tabs:
 
277
  for _ in plot_configs:
278
  generated_panel_vis_updates.append(gr.update(visible=True))
279
  generated_explore_btn_updates.append(gr.update(value=EXPLORE_ICON))
280
+
281
  if action_type == "insights": new_current_chat_plot_id = None
282
 
283
  else: # Toggling ON a new action or switching actions
 
337
  ]
338
 
339
  final_updates.extend(generated_panel_vis_updates)
340
+ final_updates.extend(generated_bomb_btn_updates)
341
  final_updates.extend(generated_formula_btn_updates)
342
  final_updates.extend(generated_explore_btn_updates)
343
  final_updates.extend(section_title_vis_updates)
 
430
  action_col_upd = gr.update(visible=False)
431
  new_active_panel_state_upd = None
432
  formula_hint_upd = gr.update(visible=False)
433
+ # Reset bomb and formula buttons to non-active
434
+ bomb_btns_updates = [gr.update(value=BOMB_ICON) for _ in plot_configs]
435
+ formula_btns_updates = [gr.update(value=FORMULA_ICON) for _ in plot_configs]
436
+ else: # No active panel, so no need to reset bomb/formula buttons beyond what explore does
437
+ bomb_btns_updates = [gr.update() for _ in plot_configs]
438
+ formula_btns_updates = [gr.update() for _ in plot_configs]
 
439
 
440
  final_explore_updates = [
441
  new_explored_id_to_set, action_col_upd, new_active_panel_state_upd, formula_hint_upd
 
461
 
462
  action_panel_outputs_list = _base_action_panel_ui_outputs + _action_panel_state_outputs
463
  action_panel_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("panel_component", gr.update()) for pc in plot_configs])
464
+ action_panel_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("bomb_button", gr.update()) for pc in plot_configs])
465
  action_panel_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("formula_button", gr.update()) for pc in plot_configs])
466
  action_panel_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("explore_button", gr.update()) for pc in plot_configs])
467
  action_panel_outputs_list.extend([section_titles_map.get(s_name, gr.update()) for s_name in unique_ordered_sections])
 
470
  explore_outputs_list = _explore_base_outputs
471
  explore_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("panel_component", gr.update()) for pc in plot_configs])
472
  explore_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("explore_button", gr.update()) for pc in plot_configs])
473
+ explore_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("bomb_button", gr.update()) for pc in plot_configs])
474
  explore_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("formula_button", gr.update()) for pc in plot_configs])
475
  explore_outputs_list.extend([section_titles_map.get(s_name, gr.update()) for s_name in unique_ordered_sections])
476
 
 
491
  if ui_obj.get("formula_button"):
492
  ui_obj["formula_button"].click(fn=create_panel_action_handler(plot_id, "formula"), inputs=action_click_inputs, outputs=action_panel_outputs_list, api_name=f"action_formula_{plot_id}")
493
  if ui_obj.get("explore_button"):
 
494
  ui_obj["explore_button"].click(
495
  fn=lambda current_explored_val, current_active_panel_val, p_id=plot_id: handle_explore_click(p_id, current_explored_val, current_active_panel_val),
496
  inputs=explore_click_inputs,
497
  outputs=explore_outputs_list,
498
  api_name=f"action_explore_{plot_id}"
499
+ )
500
  else: logging.warning(f"UI object for plot_id '{plot_id}' not found for click handlers.")
501
 
502
 
 
509
  insights_suggestion_2_btn.click(fn=handle_suggested_question_click, inputs=[insights_suggestion_2_btn] + suggestion_click_inputs_base, outputs=chat_submission_outputs, api_name="click_suggestion_2")
510
  insights_suggestion_3_btn.click(fn=handle_suggested_question_click, inputs=[insights_suggestion_3_btn] + suggestion_click_inputs_base, outputs=chat_submission_outputs, api_name="click_suggestion_3")
511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  with gr.TabItem("3️⃣ Menzioni", id="tab_mentions"):
513
  refresh_mentions_display_btn = gr.Button("🔄 Aggiorna Visualizzazione Menzioni", variant="secondary")
514
  mentions_html = gr.HTML("Dati menzioni...")
 
534
  show_progress="full"
535
  )
536
 
537
+ # --- NEW TABS FOR AGENTIC PIPELINE RESULTS ---
538
+ with gr.TabItem("5️⃣ Agentic Analysis Report", id="tab_agentic_report", visible=AGENTIC_MODULES_LOADED):
539
+ gr.Markdown("## 🤖 Comprehensive Analysis Report (AI Generated)")
540
+ gr.Markdown("This report is generated by an AI agent based on the selected date range. Please review critically.")
541
+ agentic_report_display_md = gr.Markdown("Apply a date filter in Tab 2 to generate the report.")
542
+ if not AGENTIC_MODULES_LOADED:
543
+ gr.Markdown("🔴 **Error:** Agentic pipeline modules could not be loaded. This tab is disabled.")
544
+
545
+
546
+ with gr.TabItem("6️⃣ Agentic OKRs & Tasks", id="tab_agentic_okrs", visible=AGENTIC_MODULES_LOADED):
547
+ gr.Markdown("## 🎯 AI Generated OKRs and Actionable Tasks")
548
+ gr.Markdown("Based on the analysis, the AI agent has proposed the following Objectives and Key Results (OKRs), along with actionable tasks. Select Key Results you want to focus on to see detailed tasks.")
549
+ if not AGENTIC_MODULES_LOADED:
550
+ gr.Markdown("🔴 **Error:** Agentic pipeline modules could not be loaded. This tab is disabled.")
551
+
552
+ with gr.Row():
553
+ with gr.Column(scale=1):
554
+ gr.Markdown("### Suggested Key Results")
555
+ key_results_cbg = gr.CheckboxGroup(
556
+ label="Select Key Results",
557
+ choices=[], # Will be populated dynamically
558
+ value=[],
559
+ interactive=True
560
+ )
561
+ with gr.Column(scale=3):
562
+ gr.Markdown("### Detailed OKRs and Tasks for Selected Key Results")
563
+ okr_detail_display_md = gr.Markdown("Select Key Results from the left to see details, or apply a date filter in Tab 2 to generate OKRs.")
564
+
565
+ def update_okr_display_on_selection(selected_kr_unique_ids: list, raw_orchestration_results: dict, all_krs_for_selection: list):
566
+ """
567
+ Updates the Markdown display for OKRs based on selected Key Results.
568
+ If no KRs are selected, it might show all, or a prompt.
569
+ """
570
+ if not raw_orchestration_results or not AGENTIC_MODULES_LOADED:
571
+ return gr.update(value="No orchestration data available or agentic modules not loaded. Apply a date filter first.")
572
+
573
+ actionable_okrs_dict = raw_orchestration_results.get("actionable_okrs_and_tasks")
574
+ if not actionable_okrs_dict or not isinstance(actionable_okrs_dict.get("okrs"), list):
575
+ return gr.update(value="No OKRs found in the orchestration results.")
576
+
577
+ okrs_list = actionable_okrs_dict["okrs"]
578
+
579
+ # Create a mapping from unique_kr_id to (okr_idx, kr_idx)
580
+ kr_id_to_indices = {
581
+ kr_info['unique_kr_id']: (kr_info['okr_index'], kr_info['kr_index'])
582
+ for kr_info in all_krs_for_selection
583
+ }
584
+
585
+ # Determine which KRs are selected for each OKR
586
+ # okr_idx -> list of selected kr_indices for that OKR
587
+ selected_krs_by_okr_idx = defaultdict(list)
588
+ if selected_kr_unique_ids: # If specific KRs are selected
589
+ for kr_unique_id in selected_kr_unique_ids:
590
+ if kr_unique_id in kr_id_to_indices:
591
+ okr_idx, kr_idx = kr_id_to_indices[kr_unique_id]
592
+ selected_krs_by_okr_idx[okr_idx].append(kr_idx)
593
+
594
+ output_md_parts = []
595
+ if not okrs_list:
596
+ output_md_parts.append("No OKRs were generated.")
597
+ else:
598
+ for okr_idx, okr_data in enumerate(okrs_list):
599
+ # If specific KRs were selected, only display OKRs that have at least one selected KR,
600
+ # or if no KRs are selected at all, display all OKRs with all their KRs.
601
+ accepted_indices_for_this_okr = selected_krs_by_okr_idx.get(okr_idx) # This will be None if no KRs from this OKR are selected
602
+
603
+ if selected_kr_unique_ids: # User has made a selection
604
+ if accepted_indices_for_this_okr is not None : # Only show this OKR if some of its KRs are selected
605
+ output_md_parts.append(format_single_okr_for_display(
606
+ okr_data,
607
+ accepted_kr_indices=accepted_indices_for_this_okr,
608
+ okr_main_index=okr_idx
609
+ ))
610
+ else: # No KRs selected by user, show all OKRs with all their KRs
611
+ output_md_parts.append(format_single_okr_for_display(
612
+ okr_data,
613
+ accepted_kr_indices=None, # None means show all KRs for this OKR
614
+ okr_main_index=okr_idx
615
+ ))
616
+
617
+ if not output_md_parts and selected_kr_unique_ids: # User made selection, but no matching OKRs displayed
618
+ final_md = "No OKRs match the current Key Result selection, or the selected Key Results do not have detailed tasks."
619
+ elif not output_md_parts and not selected_kr_unique_ids: # No user selection and no OKRs at all
620
+ final_md = "No OKRs generated. Please apply a filter in Tab 2."
621
+ else:
622
+ final_md = "\n\n---\n\n".join(output_md_parts)
623
+
624
+ return gr.update(value=final_md)
625
+
626
+ if AGENTIC_MODULES_LOADED:
627
+ key_results_cbg.change(
628
+ fn=update_okr_display_on_selection,
629
+ inputs=[key_results_cbg, orchestration_raw_results_st, key_results_for_selection_st],
630
+ outputs=[okr_detail_display_md]
631
+ )
632
+
633
+ # --- END NEW TABS ---
634
+
635
+ # --- REFRESH ANALYTICS (NOW ASYNC and includes AGENTIC PIPELINE) ---
636
+ async def refresh_all_analytics_ui_elements(current_token_state_val, date_filter_val, custom_start_val, custom_end_val, current_chat_histories_val):
637
+ logging.info("Refreshing all analytics UI elements and resetting actions/chat.")
638
+ start_time = time.time()
639
+
640
+ # 1. Standard Analytics Plot Generation
641
+ plot_gen_results = update_analytics_plots_figures(current_token_state_val, date_filter_val, custom_start_val, custom_end_val, plot_configs)
642
+ status_msg, gen_figs, new_summaries = plot_gen_results[0], plot_gen_results[1:-1], plot_gen_results[-1]
643
+
644
+ all_updates = [status_msg]
645
+ all_updates.extend(gen_figs if len(gen_figs) == len(plot_configs) else [create_placeholder_plot("Error", f"Fig missing {i}") for i in range(len(plot_configs))])
646
+
647
+ # UI Resets for Analytics Tab Action Panel
648
+ all_updates.extend([
649
+ gr.update(visible=False), # global_actions_column_ui
650
+ gr.update(value=[], visible=False), # insights_chatbot_ui (value & visibility)
651
+ gr.update(value="", visible=False), # insights_chat_input_ui (value & visibility)
652
+ gr.update(visible=False), # insights_suggestions_row_ui
653
+ gr.update(value="S1"), gr.update(value="S2"), gr.update(value="S3"), # suggestion_btns
654
+ gr.update(value="Formula details here.", visible=False), # formula_display_markdown_ui (value & visibility)
655
+ gr.update(visible=False) # formula_close_hint_md
656
+ ])
657
+
658
+ # State Resets for Analytics Tab
659
+ all_updates.extend([
660
+ None, # active_panel_action_state
661
+ None, # current_chat_plot_id_st
662
+ {}, # chat_histories_st (resetting, new insights will be generated if panel opened)
663
+ new_summaries # plot_data_for_chatbot_st
664
+ ])
665
+
666
+ # Plot button and panel visibility resets for Analytics Tab
667
+ for _ in plot_configs:
668
+ all_updates.extend([
669
+ gr.update(value=BOMB_ICON),
670
+ gr.update(value=FORMULA_ICON),
671
+ gr.update(value=EXPLORE_ICON),
672
+ gr.update(visible=True) # panel_component visibility
673
+ ])
674
+
675
+ all_updates.append(None) # explored_plot_id_state reset
676
+ all_updates.extend([gr.update(visible=True)] * num_unique_sections) # section_title visibility reset
677
+
678
+ # 2. Agentic Pipeline Execution
679
+ agentic_status_update = "Agentic pipeline processing..."
680
+ # Placeholder for agentic UI updates initially
681
+ agentic_report_md_update = gr.update(value="Analisi AI in corso...")
682
+ key_results_cbg_update = gr.update(choices=[], value=[], interactive=False)
683
+ okr_detail_display_md_update = gr.update(value="Dettagli OKR in corso di generazione...")
684
+ orchestration_results_update = None
685
+ selected_krs_update = []
686
+ krs_for_selection_update = []
687
+
688
+ if AGENTIC_MODULES_LOADED:
689
+ try:
690
+ logging.info("Starting agentic pipeline...")
691
+ orchestration_output = await run_full_analytics_orchestration(
692
+ current_token_state_val, date_filter_val, custom_start_val, custom_end_val
693
+ )
694
+ agentic_status_update = "Agentic pipeline completed."
695
+ logging.info(f"Agentic pipeline finished. Output keys: {orchestration_output.keys() if orchestration_output else 'None'}")
696
+
697
+
698
+ if orchestration_output:
699
+ orchestration_results_update = orchestration_output # Store raw results
700
+
701
+ # Format report for Tab 5
702
+ report_str = orchestration_output.get('comprehensive_analysis_report_str')
703
+ agentic_report_md_update = gr.update(value=format_report_to_markdown(report_str))
704
+
705
+ # Prepare KRs for Tab 6
706
+ actionable_okrs = orchestration_output.get('actionable_okrs_and_tasks')
707
+ krs_for_ui_selection_list = extract_key_results_for_selection(actionable_okrs)
708
+ krs_for_selection_update = krs_for_ui_selection_list # Store for later use in event handler
709
+
710
+ kr_choices_for_cbg = [(kr['kr_description'], kr['unique_kr_id']) for kr in krs_for_ui_selection_list]
711
+ key_results_cbg_update = gr.update(choices=kr_choices_for_cbg, value=[], interactive=True)
712
+
713
+ # Initially display all OKRs in Tab 6
714
+ all_okrs_md_parts = []
715
+ if actionable_okrs and isinstance(actionable_okrs.get("okrs"), list):
716
+ for okr_idx, okr_item in enumerate(actionable_okrs["okrs"]):
717
+ all_okrs_md_parts.append(format_single_okr_for_display(okr_item, accepted_kr_indices=None, okr_main_index=okr_idx))
718
+
719
+ if not all_okrs_md_parts:
720
+ okr_detail_display_md_update = gr.update(value="Nessun OKR generato o trovato.")
721
+ else:
722
+ okr_detail_display_md_update = gr.update(value="\n\n---\n\n".join(all_okrs_md_parts))
723
+ selected_krs_update = [] # Reset selection
724
+
725
+ else:
726
+ agentic_report_md_update = gr.update(value="Nessun report generato dalla pipeline AI.")
727
+ key_results_cbg_update = gr.update(choices=[], value=[], interactive=False)
728
+ okr_detail_display_md_update = gr.update(value="Nessun OKR generato o errore nella pipeline AI.")
729
+ orchestration_results_update = None
730
+ selected_krs_update = []
731
+ krs_for_selection_update = []
732
+
733
+ except Exception as e:
734
+ logging.error(f"Error during agentic pipeline execution: {e}", exc_info=True)
735
+ agentic_status_update = f"Errore pipeline AI: {e}"
736
+ agentic_report_md_update = gr.update(value=f"Errore durante la generazione del report AI: {e}")
737
+ key_results_cbg_update = gr.update(choices=[], value=[], interactive=False)
738
+ okr_detail_display_md_update = gr.update(value=f"Errore durante la generazione degli OKR AI: {e}")
739
+ orchestration_results_update = None
740
+ selected_krs_update = []
741
+ krs_for_selection_update = []
742
+ else: # AGENTIC_MODULES_LOADED is False
743
+ agentic_status_update = "Moduli AI non caricati. Pipeline saltata."
744
+ agentic_report_md_update = gr.update(value="Moduli AI non caricati. Report non disponibile.")
745
+ key_results_cbg_update = gr.update(choices=[], value=[], interactive=False, visible=False) # Hide if not loaded
746
+ okr_detail_display_md_update = gr.update(value="Moduli AI non caricati. OKR non disponibili.", visible=False) # Hide
747
+ orchestration_results_update = None
748
+ selected_krs_update = []
749
+ krs_for_selection_update = []
750
+
751
+
752
+ # Append agentic pipeline updates to all_updates
753
+ # Order must match apply_filter_and_sync_outputs_list extension
754
+ all_updates.extend([
755
+ agentic_report_md_update, # For agentic_report_display_md
756
+ key_results_cbg_update, # For key_results_cbg
757
+ okr_detail_display_md_update, # For okr_detail_display_md
758
+ orchestration_results_update, # For orchestration_raw_results_st
759
+ selected_krs_update, # For selected_key_result_ids_st
760
+ krs_for_selection_update # For key_results_for_selection_st
761
+ ])
762
+
763
+ end_time = time.time()
764
+ logging.info(f"Analytics and Agentic refresh took {end_time - start_time:.2f} seconds.")
765
+ logging.info(f"Prepared {len(all_updates)} updates for analytics refresh. Expected {15 + 5*len(plot_configs) + num_unique_sections + 6}.")
766
+ # The status_msg from plot_gen_results is the first item. We can augment it.
767
+ all_updates[0] = f"{status_msg} | {agentic_status_update}"
768
+
769
+ return tuple(all_updates) # Return as tuple for Gradio
770
+
771
+ # Define the list of outputs for apply_filter_btn and sync.then
772
+ apply_filter_and_sync_outputs_list = [analytics_status_md]
773
+ apply_filter_and_sync_outputs_list.extend([plot_ui_objects.get(pc["id"], {}).get("plot_component", gr.update()) for pc in plot_configs])
774
+
775
+ _ui_resets_for_filter = [
776
+ global_actions_column_ui, insights_chatbot_ui, insights_chat_input_ui,
777
+ insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
778
+ formula_display_markdown_ui, formula_close_hint_md
779
+ ]
780
+ apply_filter_and_sync_outputs_list.extend(_ui_resets_for_filter)
781
+
782
+ _state_resets_for_filter = [active_panel_action_state, current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st]
783
+ apply_filter_and_sync_outputs_list.extend(_state_resets_for_filter)
784
+
785
+ for pc in plot_configs:
786
+ pid = pc["id"]
787
+ apply_filter_and_sync_outputs_list.extend([
788
+ plot_ui_objects.get(pid, {}).get("bomb_button", gr.update()),
789
+ plot_ui_objects.get(pid, {}).get("formula_button", gr.update()),
790
+ plot_ui_objects.get(pid, {}).get("explore_button", gr.update()),
791
+ plot_ui_objects.get(pid, {}).get("panel_component", gr.update())
792
+ ])
793
+
794
+ apply_filter_and_sync_outputs_list.append(explored_plot_id_state)
795
+
796
+ apply_filter_and_sync_outputs_list.extend([section_titles_map.get(s_name, gr.update()) for s_name in unique_ordered_sections])
797
+
798
+ # --- Add new outputs for agentic tabs and states ---
799
+ apply_filter_and_sync_outputs_list.extend([
800
+ agentic_report_display_md, # Tab 5 UI
801
+ key_results_cbg, # Tab 6 UI (CheckboxGroup)
802
+ okr_detail_display_md, # Tab 6 UI (Markdown display)
803
+ orchestration_raw_results_st, # New State
804
+ selected_key_result_ids_st, # New State
805
+ key_results_for_selection_st # New State
806
+ ])
807
+ # --- End new outputs ---
808
+
809
+ apply_filter_btn.click(
810
+ fn=refresh_all_analytics_ui_elements, # Now async
811
+ inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
812
+ outputs=apply_filter_and_sync_outputs_list, show_progress="full"
813
+ )
814
 
815
  # --- Sync Events (at the end of the app's 'with gr.Blocks()' context) ---
816
  sync_event_part1 = sync_data_btn.click(fn=sync_all_linkedin_data_orchestrator, inputs=[token_state], outputs=[sync_status_html_output, token_state], show_progress="full")
817
  sync_event_part2 = sync_event_part1.then(fn=process_and_store_bubble_token, inputs=[url_user_token_display, org_urn_display, token_state], outputs=[status_box, token_state, sync_data_btn], show_progress=False)
818
  sync_event_part3 = sync_event_part2.then(fn=display_main_dashboard, inputs=[token_state], outputs=[dashboard_display_html], show_progress=False)
819
  sync_event_final = sync_event_part3.then(
820
+ fn=refresh_all_analytics_ui_elements, # Now async
821
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
822
  outputs=apply_filter_and_sync_outputs_list,
823
  show_progress="full"
 
829
  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]):
830
  logging.warning("ATTENZIONE: Variabili Bubble non impostate.")
831
 
832
+ if not AGENTIC_MODULES_LOADED:
833
+ logging.warning("CRITICAL: Agentic pipeline modules (run_agentic_pipeline.py, insights_ui_generator.py) failed to load. Tabs 5 and 6 will be non-functional.")
834
+ if not os.environ.get("GEMINI_API_KEY") and AGENTIC_MODULES_LOADED:
835
+ logging.warning("ATTENZIONE: 'GEMINI_API_KEY' non impostata. La pipeline AI per le tab 5 e 6 potrebbe non funzionare.")
836
+
837
  try: logging.info(f"Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
838
  except ImportError: logging.warning("Matplotlib non trovato.")
839