Spaces:
Running
Running
Update app.py
Browse files
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 |
-
|
404 |
-
|
405 |
-
|
406 |
-
else:
|
407 |
-
for _ in plot_configs
|
408 |
-
|
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 |
-
)
|
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, #
|
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 |
|