GuglielmoTor commited on
Commit
365263e
·
verified ·
1 Parent(s): 2601f1c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -156
app.py CHANGED
@@ -141,20 +141,19 @@ def update_analytics_plots_figures(token_state_value, date_filter_option, custom
141
  plot_figs.append(create_placeholder_plot(title=f"Errore Grafico {i+1}", message=f"Dettaglio: {str(plot_e)[:100]}"))
142
 
143
  message = f"📊 Analisi aggiornate per il periodo: {date_filter_option}"
144
- if date_filter_option == "Intervallo Personalizzato": # Corrected from "Custom Range"
145
  s_display = start_dt_for_msg.strftime('%Y-%m-%d') if start_dt_for_msg else "Qualsiasi"
146
  e_display = end_dt_for_msg.strftime('%Y-%m-%d') if end_dt_for_msg else "Qualsiasi"
147
  message += f" (Da: {s_display} A: {e_display})"
148
 
149
  final_plot_figs = []
150
  for i, p_fig in enumerate(plot_figs):
151
- if p_fig is not None and not isinstance(p_fig, str): # Checking if it's a valid plot object
152
  final_plot_figs.append(p_fig)
153
  else:
154
  logging.warning(f"Plot generation failed or unexpected type for slot {i}, using placeholder. Figure: {p_fig}")
155
  final_plot_figs.append(create_placeholder_plot(title="Errore Grafico", message="Impossibile generare questa figura."))
156
 
157
- # Ensure the list has exactly num_expected_plots items
158
  while len(final_plot_figs) < num_expected_plots:
159
  logging.warning(f"Adding missing plot placeholder. Expected {num_expected_plots}, got {len(final_plot_figs)}.")
160
  final_plot_figs.append(create_placeholder_plot(title="Grafico Mancante", message="Figura non generata."))
@@ -181,12 +180,8 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
181
  "config_eb_labels_col": "li_eb_label"
182
  })
183
 
184
- # --- NEW CHATBOT STATES ---
185
- # Stores chat history for each plot_id: {plot_id: [{"role": "user/assistant", "content": "..."}, ...]}
186
  chat_histories_st = gr.State({})
187
- # Stores the plot_id of the currently active chat, e.g., "followers_count"
188
  current_chat_plot_id_st = gr.State(None)
189
- # --- END NEW CHATBOT STATES ---
190
 
191
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
192
  url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
@@ -224,7 +219,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
224
  ["Sempre", "Ultimi 7 Giorni", "Ultimi 30 Giorni", "Intervallo Personalizzato"],
225
  label="Seleziona Intervallo Date", value="Sempre", scale=3
226
  )
227
- with gr.Column(scale=2): # Ensure this column is defined for date pickers
228
  custom_start_date_picker = gr.DateTime(label="Data Inizio", visible=False, include_time=False, type="datetime")
229
  custom_end_date_picker = gr.DateTime(label="Data Fine", visible=False, include_time=False, type="datetime")
230
 
@@ -263,20 +258,18 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
263
  ]
264
  assert len(plot_configs) == 19, "Mancata corrispondenza in plot_configs e grafici attesi."
265
 
266
- active_panel_action_state = gr.State(None) # Stores {"plot_id": "...", "type": "insights/formula"}
267
- explored_plot_id_state = gr.State(None) # Stores plot_id of the currently "explored" (maximized) plot
268
 
269
- plot_ui_objects = {} # Will be populated by build_analytics_tab_plot_area
270
 
271
  with gr.Row(equal_height=False):
272
  with gr.Column(scale=8) as plots_area_col:
273
  plot_ui_objects = build_analytics_tab_plot_area(plot_configs)
274
 
275
- # --- UPDATED GLOBAL ACTIONS COLUMN with CHATBOT ---
276
- with gr.Column(scale=4, visible=False) as global_actions_column_ui: # This column's visibility is controlled
277
- gr.Markdown("### 💡 Azioni Contestuali Grafico") # General title for the panel
278
 
279
- # Chatbot components (initially hidden)
280
  insights_chatbot_ui = gr.Chatbot(
281
  label="Chat Insights", type="messages", height=450,
282
  bubble_full_width=False, visible=False, show_label=False,
@@ -291,28 +284,22 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
291
  insights_suggestion_2_btn = gr.Button(value="Suggerimento 2", size="sm", min_width=50)
292
  insights_suggestion_3_btn = gr.Button(value="Suggerimento 3", size="sm", min_width=50)
293
 
294
- # Formula display component (initially hidden)
295
  formula_display_markdown_ui = gr.Markdown(
296
  "I dettagli sulla formula/metodologia appariranno qui.", visible=False
297
  )
298
- # --- END UPDATED GLOBAL ACTIONS COLUMN ---
299
 
300
- # --- Event Handler for Insights (Chatbot) and Formula Buttons ---
301
  async def handle_panel_action(
302
  plot_id_clicked: str,
303
- action_type: str, # "insights" or "formula"
304
- current_active_action_from_state: dict, # Stored active action {"plot_id": ..., "type": ...}
305
- # token_state_val: dict, # No longer directly needed here for chat/formula text
306
- current_chat_histories: dict, # from chat_histories_st
307
- current_chat_plot_id: str # from current_chat_plot_id_st
308
  ):
309
  logging.info(f"Azione '{action_type}' per grafico: {plot_id_clicked}. Attualmente attivo: {current_active_action_from_state}")
310
 
311
- # Find the label for the clicked plot_id
312
  clicked_plot_config = next((p for p in plot_configs if p["id"] == plot_id_clicked), None)
313
  if not clicked_plot_config:
314
  logging.error(f"Configurazione non trovata per plot_id {plot_id_clicked}")
315
- # Basic error feedback if needed, though this shouldn't happen if UI is built correctly
316
  return [gr.update(visible=False)] * 7 + [current_active_action_from_state, current_chat_plot_id, current_chat_histories] + [gr.update() for _ in range(2 * len(plot_configs))]
317
 
318
  clicked_plot_label = clicked_plot_config["label"]
@@ -323,27 +310,24 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
323
  new_active_action_state_to_set = None
324
  action_col_visible_update = gr.update(visible=True)
325
 
326
- # Default visibility for components in the action column
327
  insights_chatbot_visible_update = gr.update(visible=False)
328
  insights_chat_input_visible_update = gr.update(visible=False)
329
  insights_suggestions_row_visible_update = gr.update(visible=False)
330
  formula_display_visible_update = gr.update(visible=False)
331
 
332
- # Chat-specific updates
333
  chatbot_content_update = gr.update()
334
  suggestion_1_update = gr.update()
335
  suggestion_2_update = gr.update()
336
  suggestion_3_update = gr.update()
337
- new_current_chat_plot_id = current_chat_plot_id # Preserve by default
338
- updated_chat_histories = current_chat_histories # Preserve by default
339
 
340
- # Formula-specific updates
341
  formula_content_update = gr.update()
342
 
343
  if is_toggling_off:
344
  new_active_action_state_to_set = None
345
  action_col_visible_update = gr.update(visible=False)
346
- new_current_chat_plot_id = None # Clear active chat plot ID when panel closes
347
  logging.info(f"Chiusura pannello {action_type} per {plot_id_clicked}")
348
  else:
349
  new_active_action_state_to_set = hypothetical_new_active_state
@@ -355,12 +339,12 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
355
  new_current_chat_plot_id = plot_id_clicked
356
  chat_history_for_this_plot = current_chat_histories.get(plot_id_clicked, [])
357
 
358
- if not chat_history_for_this_plot: # First time opening chat for this plot
359
  initial_insight_msg, suggestions = get_initial_insight_and_suggestions(plot_id_clicked, clicked_plot_label)
360
  chat_history_for_this_plot = [initial_insight_msg]
361
  updated_chat_histories = current_chat_histories.copy()
362
  updated_chat_histories[plot_id_clicked] = chat_history_for_this_plot
363
- else: # History exists, get suggestions again (or could store them)
364
  _, suggestions = get_initial_insight_and_suggestions(plot_id_clicked, clicked_plot_label)
365
 
366
  chatbot_content_update = gr.update(value=chat_history_for_this_plot)
@@ -383,19 +367,16 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
383
  else:
384
  formula_text += "(Nessuna informazione dettagliata sulla formula trovata per questo ID grafico in `formulas.py`)"
385
  formula_content_update = gr.update(value=formula_text)
386
- new_current_chat_plot_id = None # Clear active chat plot ID when formula panel opens
387
  logging.info(f"Apertura pannello FORMULA per {plot_id_clicked} (mappato a {formula_key})")
388
 
389
- # Update button icons
390
  all_button_icon_updates = []
391
  for cfg_item in plot_configs:
392
  p_id_iter = cfg_item["id"]
393
- # Insights (Bomb) button
394
  if new_active_action_state_to_set == {"plot_id": p_id_iter, "type": "insights"}:
395
  all_button_icon_updates.append(gr.update(value=ACTIVE_ICON))
396
  else:
397
  all_button_icon_updates.append(gr.update(value=BOMB_ICON))
398
- # Formula button
399
  if new_active_action_state_to_set == {"plot_id": p_id_iter, "type": "formula"}:
400
  all_button_icon_updates.append(gr.update(value=ACTIVE_ICON))
401
  else:
@@ -407,36 +388,32 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
407
  insights_chat_input_visible_update,
408
  insights_suggestions_row_visible_update, suggestion_1_update, suggestion_2_update, suggestion_3_update,
409
  formula_display_visible_update, formula_content_update,
410
- new_active_action_state_to_set, # For active_panel_action_state
411
- new_current_chat_plot_id, # For current_chat_plot_id_st
412
- updated_chat_histories # For chat_histories_st
413
  ] + all_button_icon_updates
414
 
415
  return final_updates
416
 
417
- # --- Event Handler for Chat Message Submission ---
418
  async def handle_chat_message_submission(
419
  user_message: str,
420
- current_plot_id: str, # From current_chat_plot_id_st
421
- chat_histories: dict, # From chat_histories_st
422
- # token_state_val: dict # If needed for context, but LLM should get context from history
423
  ):
424
  if not current_plot_id or not user_message.strip():
425
- # Return current history for the plot_id if message is empty
426
  history_for_plot = chat_histories.get(current_plot_id, [])
427
- return history_for_plot, "", chat_histories # Chatbot, Textbox, Histories State
 
 
428
 
429
- # Find plot label for context
430
  plot_config = next((p for p in plot_configs if p["id"] == current_plot_id), None)
431
  plot_label = plot_config["label"] if plot_config else "Grafico Selezionato"
432
 
433
  history_for_plot = chat_histories.get(current_plot_id, []).copy()
434
  history_for_plot.append({"role": "user", "content": user_message})
435
 
436
- # Show user message immediately
437
- yield history_for_plot, "", chat_histories # Update chatbot, clear input, keep histories
438
 
439
- # Generate bot response
440
  bot_response_text = await generate_llm_response(user_message, current_plot_id, plot_label, history_for_plot)
441
 
442
  history_for_plot.append({"role": "assistant", "content": bot_response_text})
@@ -444,24 +421,18 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
444
  updated_chat_histories = chat_histories.copy()
445
  updated_chat_histories[current_plot_id] = history_for_plot
446
 
447
- yield history_for_plot, "", updated_chat_histories # Final update
448
 
449
- # --- Event Handler for Suggested Question Click ---
450
  async def handle_suggested_question_click(
451
  suggestion_text: str,
452
- current_plot_id: str, # From current_chat_plot_id_st
453
- chat_histories: dict, # From chat_histories_st
454
- # token_state_val: dict
455
  ):
456
- # This will effectively call handle_chat_message_submission
457
- # We need to ensure the output signature matches what Gradio expects for this button's .click event
458
- # which is the same as handle_chat_message_submission's output.
459
- # The 'yield' pattern is for streaming, if handle_chat_message_submission uses it, this should too.
460
-
461
- # Simulate the submission process
462
  if not current_plot_id or not suggestion_text.strip():
463
  history_for_plot = chat_histories.get(current_plot_id, [])
464
- return history_for_plot, "", chat_histories
 
 
465
 
466
  plot_config = next((p for p in plot_configs if p["id"] == current_plot_id), None)
467
  plot_label = plot_config["label"] if plot_config else "Grafico Selezionato"
@@ -469,7 +440,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
469
  history_for_plot = chat_histories.get(current_plot_id, []).copy()
470
  history_for_plot.append({"role": "user", "content": suggestion_text})
471
 
472
- yield history_for_plot, "", chat_histories # Update chatbot, clear input (though no input here), keep histories
473
 
474
  bot_response_text = await generate_llm_response(suggestion_text, current_plot_id, plot_label, history_for_plot)
475
  history_for_plot.append({"role": "assistant", "content": bot_response_text})
@@ -479,18 +450,13 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
479
 
480
  yield history_for_plot, "", updated_chat_histories
481
 
482
-
483
- # --- Explore button logic (remains largely the same) ---
484
  def handle_explore_click(plot_id_clicked, current_explored_plot_id_from_state):
485
- # (This function's logic for showing/hiding plot panels based on explore state)
486
- # ... (original logic from user's code) ...
487
  logging.info(f"Click su Esplora per: {plot_id_clicked}. Attualmente esplorato da stato: {current_explored_plot_id_from_state}")
488
  if not plot_ui_objects:
489
  logging.error("plot_ui_objects non popolato durante handle_explore_click.")
490
- # Need to return updates for all explore buttons and panels
491
  updates_for_missing_ui = [current_explored_plot_id_from_state]
492
  for _ in plot_configs:
493
- updates_for_missing_ui.extend([gr.update(), gr.update()]) # panel, button
494
  return updates_for_missing_ui
495
 
496
  new_explored_id_to_set = None
@@ -508,80 +474,69 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
508
  p_id = cfg["id"]
509
  if p_id in plot_ui_objects:
510
  panel_visible = not new_explored_id_to_set or (p_id == new_explored_id_to_set)
511
- panel_and_button_updates.append(gr.update(visible=panel_visible)) # Panel component
512
 
513
- if p_id == new_explored_id_to_set: # Explored button icon
514
  panel_and_button_updates.append(gr.update(value=ACTIVE_ICON))
515
  else:
516
  panel_and_button_updates.append(gr.update(value=EXPLORE_ICON))
517
- else: # Should not happen if UI built correctly
518
  panel_and_button_updates.extend([gr.update(), gr.update()])
519
 
520
- final_updates = [new_explored_id_to_set] + panel_and_button_updates # state + N*(panel_update, button_update)
521
  return final_updates
522
-
523
- # --- Define output lists for event handlers ---
524
 
525
- # Outputs for handle_panel_action (insights/formula clicks)
526
  action_panel_outputs_list = [
527
- global_actions_column_ui, # Column visibility
528
- insights_chatbot_ui, insights_chatbot_ui, # Visibility, Content for chatbot
529
- insights_chat_input_ui, # Visibility for chat input
530
- insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn, # Row visibility, button content
531
- formula_display_markdown_ui, formula_display_markdown_ui, # Visibility, Content for formula
532
  active_panel_action_state,
533
  current_chat_plot_id_st,
534
  chat_histories_st
535
  ]
536
- for cfg_item_action in plot_configs: # Add bomb and formula button icon updates
537
  pid_action = cfg_item_action["id"]
538
  if pid_action in plot_ui_objects:
539
  action_panel_outputs_list.append(plot_ui_objects[pid_action]["bomb_button"])
540
  action_panel_outputs_list.append(plot_ui_objects[pid_action]["formula_button"])
541
- else: # Should not happen
542
  action_panel_outputs_list.extend([None, None])
543
 
544
- # Outputs for handle_explore_click
545
  explore_buttons_outputs_list = [explored_plot_id_state]
546
  for cfg_item_explore in plot_configs:
547
  pid_explore = cfg_item_explore["id"]
548
  if pid_explore in plot_ui_objects:
549
  explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["panel_component"])
550
  explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["explore_button"])
551
- else: # Should not happen
552
  explore_buttons_outputs_list.extend([None, None])
553
 
554
- # Inputs for action clicks (insights/formula)
555
  action_click_inputs = [
556
  active_panel_action_state,
557
- # token_state, # No longer passing full token_state if not needed by handle_panel_action
558
  chat_histories_st,
559
  current_chat_plot_id_st
560
  ]
561
- # Inputs for explore clicks
562
  explore_click_inputs = [explored_plot_id_state]
563
 
564
- # Wire up action and explore buttons
565
  for config_item in plot_configs:
566
  plot_id = config_item["id"]
567
- plot_label = config_item["label"] # Get label for context
568
  if plot_id in plot_ui_objects:
569
  ui_obj = plot_ui_objects[plot_id]
570
- # Insights (Bomb) Button
571
  ui_obj["bomb_button"].click(
572
  fn=lambda current_active_val, current_chats_val, current_chat_pid, p_id=plot_id: handle_panel_action(p_id, "insights", current_active_val, current_chats_val, current_chat_pid),
573
- inputs=action_click_inputs, # current_active_action_from_state, current_chat_histories, current_chat_plot_id
574
  outputs=action_panel_outputs_list,
575
  api_name=f"action_insights_{plot_id}"
576
  )
577
- # Formula Button
578
  ui_obj["formula_button"].click(
579
  fn=lambda current_active_val, current_chats_val, current_chat_pid, p_id=plot_id: handle_panel_action(p_id, "formula", current_active_val, current_chats_val, current_chat_pid),
580
  inputs=action_click_inputs,
581
  outputs=action_panel_outputs_list,
582
  api_name=f"action_formula_{plot_id}"
583
  )
584
- # Explore Button
585
  ui_obj["explore_button"].click(
586
  fn=lambda current_explored_val, p_id=plot_id: handle_explore_click(p_id, current_explored_val),
587
  inputs=explore_click_inputs,
@@ -591,37 +546,33 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
591
  else:
592
  logging.warning(f"Oggetto UI per plot_id '{plot_id}' non trovato durante il tentativo di associare i gestori di click.")
593
 
594
- # Wire up chat input submission
595
  chat_submission_outputs = [insights_chatbot_ui, insights_chat_input_ui, chat_histories_st]
596
  insights_chat_input_ui.submit(
597
  fn=handle_chat_message_submission,
598
- inputs=[insights_chat_input_ui, current_chat_plot_id_st, chat_histories_st], # token_state removed
599
  outputs=chat_submission_outputs,
600
  api_name="submit_chat_message"
601
  )
602
 
603
- # Wire up suggested question buttons
604
  insights_suggestion_1_btn.click(
605
  fn=handle_suggested_question_click,
606
- inputs=[insights_suggestion_1_btn, current_chat_plot_id_st, chat_histories_st], # token_state removed
607
- outputs=chat_submission_outputs, # Same outputs as direct message submission
608
  api_name="click_suggestion_1"
609
  )
610
  insights_suggestion_2_btn.click(
611
  fn=handle_suggested_question_click,
612
- inputs=[insights_suggestion_2_btn, current_chat_plot_id_st, chat_histories_st], # token_state removed
613
  outputs=chat_submission_outputs,
614
  api_name="click_suggestion_2"
615
  )
616
  insights_suggestion_3_btn.click(
617
  fn=handle_suggested_question_click,
618
- inputs=[insights_suggestion_3_btn, current_chat_plot_id_st, chat_histories_st], # token_state removed
619
  outputs=chat_submission_outputs,
620
  api_name="click_suggestion_3"
621
  )
622
 
623
-
624
- # --- Function to refresh all analytics UI elements (plots and action panel states) ---
625
  def refresh_all_analytics_ui_elements(current_token_state, date_filter_val, custom_start_val, custom_end_val, current_chat_histories):
626
  logging.info("Aggiornamento di tutti gli elementi UI delle analisi e reset delle azioni/chat.")
627
  plot_generation_results = update_analytics_plots_figures(
@@ -630,84 +581,76 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
630
  status_message_update = plot_generation_results[0]
631
  generated_plot_figures = plot_generation_results[1:]
632
 
633
- all_updates = [status_message_update] # For analytics_status_md
634
 
635
- # Add plot figure updates
636
  for i in range(len(plot_configs)):
637
  if i < len(generated_plot_figures):
638
- all_updates.append(generated_plot_figures[i]) # Plot component
639
- else: # Should not happen if update_analytics_plots_figures is correct
640
  all_updates.append(create_placeholder_plot("Errore Figura", f"Figura mancante per grafico {plot_configs[i]['id']}"))
641
 
642
- # Reset global actions column and its content
643
  all_updates.extend([
644
- gr.update(visible=False), # global_actions_column_ui
645
- gr.update(value=[], visible=False), # insights_chatbot_ui (clear history, hide)
646
- gr.update(value="", visible=False), # insights_chat_input_ui (clear text, hide)
647
- gr.update(visible=False), # insights_suggestions_row_ui
648
- gr.update(value="Suggerimento 1", visible=True), # insights_suggestion_1_btn (reset text, keep visible within hidden row)
649
- gr.update(value="Suggerimento 2", visible=True), # insights_suggestion_2_btn
650
- gr.update(value="Suggerimento 3", visible=True), # insights_suggestion_3_btn
651
- gr.update(value="I dettagli sulla formula/metodologia appariranno qui.", visible=False), # formula_display_markdown_ui
652
- None, # active_panel_action_state (reset to None)
653
- None, # current_chat_plot_id_st (reset to None)
654
- current_chat_histories, # chat_histories_st (preserve existing histories across refresh unless explicitly cleared)
655
- # If you want to clear all chats on refresh: use `{}` instead of current_chat_histories
656
  ])
657
 
658
- # Reset action buttons (bomb, formula, explore icons) and plot panel visibility (for explore)
659
  for cfg in plot_configs:
660
  pid = cfg["id"]
661
  if pid in plot_ui_objects:
662
- all_updates.append(gr.update(value=BOMB_ICON)) # bomb_button
663
- all_updates.append(gr.update(value=FORMULA_ICON)) # formula_button
664
- all_updates.append(gr.update(value=EXPLORE_ICON)) # explore_button
665
- all_updates.append(gr.update(visible=True)) # panel_component (reset to default visibility)
666
- else: # Should not happen
667
  all_updates.extend([None, None, None, None])
668
 
669
- all_updates.append(None) # explored_plot_id_state (reset to None)
670
 
671
  logging.info(f"Preparati {len(all_updates)} aggiornamenti per il refresh completo delle analisi.")
672
  return all_updates
673
 
674
- # Define the output list for apply_filter_btn and sync events that refresh analytics
675
- apply_filter_and_sync_outputs_list = [analytics_status_md] # Status message
676
- # Add plot components
677
  for config_item_filter_sync in plot_configs:
678
  pid_filter_sync = config_item_filter_sync["id"]
679
  if pid_filter_sync in plot_ui_objects and "plot_component" in plot_ui_objects[pid_filter_sync]:
680
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync]["plot_component"])
681
  else:
682
- apply_filter_and_sync_outputs_list.append(None) # Placeholder if UI object not found
683
 
684
- # Add updates for the global actions column and its contents + states
685
  apply_filter_and_sync_outputs_list.extend([
686
- global_actions_column_ui, # Column visibility
687
- insights_chatbot_ui, # Chatbot content and visibility
688
- insights_chat_input_ui, # Chat input text and visibility
689
- insights_suggestions_row_ui, # Suggestions row visibility
690
- insights_suggestion_1_btn, # Suggestion button 1 text/visibility
691
- insights_suggestion_2_btn, # Suggestion button 2 text/visibility
692
- insights_suggestion_3_btn, # Suggestion button 3 text/visibility
693
- formula_display_markdown_ui, # Formula markdown content and visibility
694
- active_panel_action_state, # State for active panel
695
- current_chat_plot_id_st, # State for current chat plot ID
696
- chat_histories_st # State for all chat histories
697
  ])
698
 
699
- # Add updates for individual plot action buttons (bomb, formula, explore) and plot panels (explore visibility)
700
  for cfg_filter_sync_btns in plot_configs:
701
  pid_filter_sync_btns = cfg_filter_sync_btns["id"]
702
  if pid_filter_sync_btns in plot_ui_objects:
703
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["bomb_button"])
704
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["formula_button"])
705
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["explore_button"])
706
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["panel_component"]) # For explore visibility
707
  else:
708
- apply_filter_and_sync_outputs_list.extend([None, None, None, None]) # Placeholders
709
 
710
- apply_filter_and_sync_outputs_list.append(explored_plot_id_state) # State for explored plot ID
711
 
712
  logging.info(f"Output totali definiti per apply_filter/sync: {len(apply_filter_and_sync_outputs_list)}")
713
 
@@ -731,7 +674,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
731
  with gr.TabItem("4️⃣ Statistiche Follower", id="tab_follower_stats"):
732
  refresh_follower_stats_btn = gr.Button("🔄 Aggiorna Visualizzazione Statistiche Follower", variant="secondary")
733
  follower_stats_html = gr.HTML("Statistiche follower...")
734
- with gr.Row(): # Ensure plots are within rows or columns for layout
735
  fs_plot_monthly_gains = gr.Plot(label="Guadagni Mensili Follower")
736
  with gr.Row():
737
  fs_plot_seniority = gr.Plot(label="Follower per Anzianità (Top 10 Organici)")
@@ -743,25 +686,23 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
743
  show_progress="full"
744
  )
745
 
746
- # Sync data flow
747
  sync_event_part1 = sync_data_btn.click(
748
  fn=sync_all_linkedin_data_orchestrator,
749
  inputs=[token_state], outputs=[sync_status_html_output, token_state], show_progress="full"
750
  )
751
- sync_event_part2 = sync_event_part1.then( # Use .then() for sequential execution
752
- fn=process_and_store_bubble_token, # This function was defined in state_manager.py
753
  inputs=[url_user_token_display, org_urn_display, token_state],
754
- outputs=[status_box, token_state, sync_data_btn], show_progress=False # Assuming this is correct from original
755
  )
756
  sync_event_part3 = sync_event_part2.then(
757
- fn=display_main_dashboard, # This function was defined in ui_generators.py
758
  inputs=[token_state], outputs=[dashboard_display_html], show_progress=False
759
  )
760
- # After sync, refresh analytics tab including plots and resetting chat/formula panels
761
  sync_event_final = sync_event_part3.then(
762
  fn=refresh_all_analytics_ui_elements,
763
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
764
- outputs=apply_filter_and_sync_outputs_list, # Use the comprehensive list
765
  show_progress="full"
766
  )
767
 
@@ -775,7 +716,7 @@ if __name__ == "__main__":
775
 
776
  try:
777
  logging.info(f"Versione Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
778
- except ImportError: # Matplotlib might not be an explicit import if only used by plot generators
779
  logging.warning("Matplotlib non trovato direttamente, ma potrebbe essere usato dai generatori di grafici.")
780
 
781
  app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
 
141
  plot_figs.append(create_placeholder_plot(title=f"Errore Grafico {i+1}", message=f"Dettaglio: {str(plot_e)[:100]}"))
142
 
143
  message = f"📊 Analisi aggiornate per il periodo: {date_filter_option}"
144
+ if date_filter_option == "Intervallo Personalizzato":
145
  s_display = start_dt_for_msg.strftime('%Y-%m-%d') if start_dt_for_msg else "Qualsiasi"
146
  e_display = end_dt_for_msg.strftime('%Y-%m-%d') if end_dt_for_msg else "Qualsiasi"
147
  message += f" (Da: {s_display} A: {e_display})"
148
 
149
  final_plot_figs = []
150
  for i, p_fig in enumerate(plot_figs):
151
+ if p_fig is not None and not isinstance(p_fig, str):
152
  final_plot_figs.append(p_fig)
153
  else:
154
  logging.warning(f"Plot generation failed or unexpected type for slot {i}, using placeholder. Figure: {p_fig}")
155
  final_plot_figs.append(create_placeholder_plot(title="Errore Grafico", message="Impossibile generare questa figura."))
156
 
 
157
  while len(final_plot_figs) < num_expected_plots:
158
  logging.warning(f"Adding missing plot placeholder. Expected {num_expected_plots}, got {len(final_plot_figs)}.")
159
  final_plot_figs.append(create_placeholder_plot(title="Grafico Mancante", message="Figura non generata."))
 
180
  "config_eb_labels_col": "li_eb_label"
181
  })
182
 
 
 
183
  chat_histories_st = gr.State({})
 
184
  current_chat_plot_id_st = gr.State(None)
 
185
 
186
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
187
  url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
 
219
  ["Sempre", "Ultimi 7 Giorni", "Ultimi 30 Giorni", "Intervallo Personalizzato"],
220
  label="Seleziona Intervallo Date", value="Sempre", scale=3
221
  )
222
+ with gr.Column(scale=2):
223
  custom_start_date_picker = gr.DateTime(label="Data Inizio", visible=False, include_time=False, type="datetime")
224
  custom_end_date_picker = gr.DateTime(label="Data Fine", visible=False, include_time=False, type="datetime")
225
 
 
258
  ]
259
  assert len(plot_configs) == 19, "Mancata corrispondenza in plot_configs e grafici attesi."
260
 
261
+ active_panel_action_state = gr.State(None)
262
+ explored_plot_id_state = gr.State(None)
263
 
264
+ plot_ui_objects = {}
265
 
266
  with gr.Row(equal_height=False):
267
  with gr.Column(scale=8) as plots_area_col:
268
  plot_ui_objects = build_analytics_tab_plot_area(plot_configs)
269
 
270
+ with gr.Column(scale=4, visible=False) as global_actions_column_ui:
271
+ gr.Markdown("### 💡 Azioni Contestuali Grafico")
 
272
 
 
273
  insights_chatbot_ui = gr.Chatbot(
274
  label="Chat Insights", type="messages", height=450,
275
  bubble_full_width=False, visible=False, show_label=False,
 
284
  insights_suggestion_2_btn = gr.Button(value="Suggerimento 2", size="sm", min_width=50)
285
  insights_suggestion_3_btn = gr.Button(value="Suggerimento 3", size="sm", min_width=50)
286
 
 
287
  formula_display_markdown_ui = gr.Markdown(
288
  "I dettagli sulla formula/metodologia appariranno qui.", visible=False
289
  )
 
290
 
 
291
  async def handle_panel_action(
292
  plot_id_clicked: str,
293
+ action_type: str,
294
+ current_active_action_from_state: dict,
295
+ current_chat_histories: dict,
296
+ current_chat_plot_id: str
 
297
  ):
298
  logging.info(f"Azione '{action_type}' per grafico: {plot_id_clicked}. Attualmente attivo: {current_active_action_from_state}")
299
 
 
300
  clicked_plot_config = next((p for p in plot_configs if p["id"] == plot_id_clicked), None)
301
  if not clicked_plot_config:
302
  logging.error(f"Configurazione non trovata per plot_id {plot_id_clicked}")
 
303
  return [gr.update(visible=False)] * 7 + [current_active_action_from_state, current_chat_plot_id, current_chat_histories] + [gr.update() for _ in range(2 * len(plot_configs))]
304
 
305
  clicked_plot_label = clicked_plot_config["label"]
 
310
  new_active_action_state_to_set = None
311
  action_col_visible_update = gr.update(visible=True)
312
 
 
313
  insights_chatbot_visible_update = gr.update(visible=False)
314
  insights_chat_input_visible_update = gr.update(visible=False)
315
  insights_suggestions_row_visible_update = gr.update(visible=False)
316
  formula_display_visible_update = gr.update(visible=False)
317
 
 
318
  chatbot_content_update = gr.update()
319
  suggestion_1_update = gr.update()
320
  suggestion_2_update = gr.update()
321
  suggestion_3_update = gr.update()
322
+ new_current_chat_plot_id = current_chat_plot_id
323
+ updated_chat_histories = current_chat_histories
324
 
 
325
  formula_content_update = gr.update()
326
 
327
  if is_toggling_off:
328
  new_active_action_state_to_set = None
329
  action_col_visible_update = gr.update(visible=False)
330
+ new_current_chat_plot_id = None
331
  logging.info(f"Chiusura pannello {action_type} per {plot_id_clicked}")
332
  else:
333
  new_active_action_state_to_set = hypothetical_new_active_state
 
339
  new_current_chat_plot_id = plot_id_clicked
340
  chat_history_for_this_plot = current_chat_histories.get(plot_id_clicked, [])
341
 
342
+ if not chat_history_for_this_plot:
343
  initial_insight_msg, suggestions = get_initial_insight_and_suggestions(plot_id_clicked, clicked_plot_label)
344
  chat_history_for_this_plot = [initial_insight_msg]
345
  updated_chat_histories = current_chat_histories.copy()
346
  updated_chat_histories[plot_id_clicked] = chat_history_for_this_plot
347
+ else:
348
  _, suggestions = get_initial_insight_and_suggestions(plot_id_clicked, clicked_plot_label)
349
 
350
  chatbot_content_update = gr.update(value=chat_history_for_this_plot)
 
367
  else:
368
  formula_text += "(Nessuna informazione dettagliata sulla formula trovata per questo ID grafico in `formulas.py`)"
369
  formula_content_update = gr.update(value=formula_text)
370
+ new_current_chat_plot_id = None
371
  logging.info(f"Apertura pannello FORMULA per {plot_id_clicked} (mappato a {formula_key})")
372
 
 
373
  all_button_icon_updates = []
374
  for cfg_item in plot_configs:
375
  p_id_iter = cfg_item["id"]
 
376
  if new_active_action_state_to_set == {"plot_id": p_id_iter, "type": "insights"}:
377
  all_button_icon_updates.append(gr.update(value=ACTIVE_ICON))
378
  else:
379
  all_button_icon_updates.append(gr.update(value=BOMB_ICON))
 
380
  if new_active_action_state_to_set == {"plot_id": p_id_iter, "type": "formula"}:
381
  all_button_icon_updates.append(gr.update(value=ACTIVE_ICON))
382
  else:
 
388
  insights_chat_input_visible_update,
389
  insights_suggestions_row_visible_update, suggestion_1_update, suggestion_2_update, suggestion_3_update,
390
  formula_display_visible_update, formula_content_update,
391
+ new_active_action_state_to_set,
392
+ new_current_chat_plot_id,
393
+ updated_chat_histories
394
  ] + all_button_icon_updates
395
 
396
  return final_updates
397
 
 
398
  async def handle_chat_message_submission(
399
  user_message: str,
400
+ current_plot_id: str,
401
+ chat_histories: dict,
 
402
  ):
403
  if not current_plot_id or not user_message.strip():
 
404
  history_for_plot = chat_histories.get(current_plot_id, [])
405
+ # Corrected: Use yield and then a bare return for early exit in async generator
406
+ yield history_for_plot, "", chat_histories
407
+ return
408
 
 
409
  plot_config = next((p for p in plot_configs if p["id"] == current_plot_id), None)
410
  plot_label = plot_config["label"] if plot_config else "Grafico Selezionato"
411
 
412
  history_for_plot = chat_histories.get(current_plot_id, []).copy()
413
  history_for_plot.append({"role": "user", "content": user_message})
414
 
415
+ yield history_for_plot, "", chat_histories
 
416
 
 
417
  bot_response_text = await generate_llm_response(user_message, current_plot_id, plot_label, history_for_plot)
418
 
419
  history_for_plot.append({"role": "assistant", "content": bot_response_text})
 
421
  updated_chat_histories = chat_histories.copy()
422
  updated_chat_histories[current_plot_id] = history_for_plot
423
 
424
+ yield history_for_plot, "", updated_chat_histories
425
 
 
426
  async def handle_suggested_question_click(
427
  suggestion_text: str,
428
+ current_plot_id: str,
429
+ chat_histories: dict,
 
430
  ):
 
 
 
 
 
 
431
  if not current_plot_id or not suggestion_text.strip():
432
  history_for_plot = chat_histories.get(current_plot_id, [])
433
+ # Corrected: Use yield and then a bare return for early exit in async generator
434
+ yield history_for_plot, "", chat_histories
435
+ return
436
 
437
  plot_config = next((p for p in plot_configs if p["id"] == current_plot_id), None)
438
  plot_label = plot_config["label"] if plot_config else "Grafico Selezionato"
 
440
  history_for_plot = chat_histories.get(current_plot_id, []).copy()
441
  history_for_plot.append({"role": "user", "content": suggestion_text})
442
 
443
+ yield history_for_plot, "", chat_histories
444
 
445
  bot_response_text = await generate_llm_response(suggestion_text, current_plot_id, plot_label, history_for_plot)
446
  history_for_plot.append({"role": "assistant", "content": bot_response_text})
 
450
 
451
  yield history_for_plot, "", updated_chat_histories
452
 
 
 
453
  def handle_explore_click(plot_id_clicked, current_explored_plot_id_from_state):
 
 
454
  logging.info(f"Click su Esplora per: {plot_id_clicked}. Attualmente esplorato da stato: {current_explored_plot_id_from_state}")
455
  if not plot_ui_objects:
456
  logging.error("plot_ui_objects non popolato durante handle_explore_click.")
 
457
  updates_for_missing_ui = [current_explored_plot_id_from_state]
458
  for _ in plot_configs:
459
+ updates_for_missing_ui.extend([gr.update(), gr.update()])
460
  return updates_for_missing_ui
461
 
462
  new_explored_id_to_set = None
 
474
  p_id = cfg["id"]
475
  if p_id in plot_ui_objects:
476
  panel_visible = not new_explored_id_to_set or (p_id == new_explored_id_to_set)
477
+ panel_and_button_updates.append(gr.update(visible=panel_visible))
478
 
479
+ if p_id == new_explored_id_to_set:
480
  panel_and_button_updates.append(gr.update(value=ACTIVE_ICON))
481
  else:
482
  panel_and_button_updates.append(gr.update(value=EXPLORE_ICON))
483
+ else:
484
  panel_and_button_updates.extend([gr.update(), gr.update()])
485
 
486
+ final_updates = [new_explored_id_to_set] + panel_and_button_updates
487
  return final_updates
 
 
488
 
 
489
  action_panel_outputs_list = [
490
+ global_actions_column_ui,
491
+ insights_chatbot_ui, insights_chatbot_ui,
492
+ insights_chat_input_ui,
493
+ insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
494
+ formula_display_markdown_ui, formula_display_markdown_ui,
495
  active_panel_action_state,
496
  current_chat_plot_id_st,
497
  chat_histories_st
498
  ]
499
+ for cfg_item_action in plot_configs:
500
  pid_action = cfg_item_action["id"]
501
  if pid_action in plot_ui_objects:
502
  action_panel_outputs_list.append(plot_ui_objects[pid_action]["bomb_button"])
503
  action_panel_outputs_list.append(plot_ui_objects[pid_action]["formula_button"])
504
+ else:
505
  action_panel_outputs_list.extend([None, None])
506
 
 
507
  explore_buttons_outputs_list = [explored_plot_id_state]
508
  for cfg_item_explore in plot_configs:
509
  pid_explore = cfg_item_explore["id"]
510
  if pid_explore in plot_ui_objects:
511
  explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["panel_component"])
512
  explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["explore_button"])
513
+ else:
514
  explore_buttons_outputs_list.extend([None, None])
515
 
 
516
  action_click_inputs = [
517
  active_panel_action_state,
 
518
  chat_histories_st,
519
  current_chat_plot_id_st
520
  ]
 
521
  explore_click_inputs = [explored_plot_id_state]
522
 
 
523
  for config_item in plot_configs:
524
  plot_id = config_item["id"]
525
+ plot_label = config_item["label"]
526
  if plot_id in plot_ui_objects:
527
  ui_obj = plot_ui_objects[plot_id]
 
528
  ui_obj["bomb_button"].click(
529
  fn=lambda current_active_val, current_chats_val, current_chat_pid, p_id=plot_id: handle_panel_action(p_id, "insights", current_active_val, current_chats_val, current_chat_pid),
530
+ inputs=action_click_inputs,
531
  outputs=action_panel_outputs_list,
532
  api_name=f"action_insights_{plot_id}"
533
  )
 
534
  ui_obj["formula_button"].click(
535
  fn=lambda current_active_val, current_chats_val, current_chat_pid, p_id=plot_id: handle_panel_action(p_id, "formula", current_active_val, current_chats_val, current_chat_pid),
536
  inputs=action_click_inputs,
537
  outputs=action_panel_outputs_list,
538
  api_name=f"action_formula_{plot_id}"
539
  )
 
540
  ui_obj["explore_button"].click(
541
  fn=lambda current_explored_val, p_id=plot_id: handle_explore_click(p_id, current_explored_val),
542
  inputs=explore_click_inputs,
 
546
  else:
547
  logging.warning(f"Oggetto UI per plot_id '{plot_id}' non trovato durante il tentativo di associare i gestori di click.")
548
 
 
549
  chat_submission_outputs = [insights_chatbot_ui, insights_chat_input_ui, chat_histories_st]
550
  insights_chat_input_ui.submit(
551
  fn=handle_chat_message_submission,
552
+ inputs=[insights_chat_input_ui, current_chat_plot_id_st, chat_histories_st],
553
  outputs=chat_submission_outputs,
554
  api_name="submit_chat_message"
555
  )
556
 
 
557
  insights_suggestion_1_btn.click(
558
  fn=handle_suggested_question_click,
559
+ inputs=[insights_suggestion_1_btn, current_chat_plot_id_st, chat_histories_st],
560
+ outputs=chat_submission_outputs,
561
  api_name="click_suggestion_1"
562
  )
563
  insights_suggestion_2_btn.click(
564
  fn=handle_suggested_question_click,
565
+ inputs=[insights_suggestion_2_btn, current_chat_plot_id_st, chat_histories_st],
566
  outputs=chat_submission_outputs,
567
  api_name="click_suggestion_2"
568
  )
569
  insights_suggestion_3_btn.click(
570
  fn=handle_suggested_question_click,
571
+ inputs=[insights_suggestion_3_btn, current_chat_plot_id_st, chat_histories_st],
572
  outputs=chat_submission_outputs,
573
  api_name="click_suggestion_3"
574
  )
575
 
 
 
576
  def refresh_all_analytics_ui_elements(current_token_state, date_filter_val, custom_start_val, custom_end_val, current_chat_histories):
577
  logging.info("Aggiornamento di tutti gli elementi UI delle analisi e reset delle azioni/chat.")
578
  plot_generation_results = update_analytics_plots_figures(
 
581
  status_message_update = plot_generation_results[0]
582
  generated_plot_figures = plot_generation_results[1:]
583
 
584
+ all_updates = [status_message_update]
585
 
 
586
  for i in range(len(plot_configs)):
587
  if i < len(generated_plot_figures):
588
+ all_updates.append(generated_plot_figures[i])
589
+ else:
590
  all_updates.append(create_placeholder_plot("Errore Figura", f"Figura mancante per grafico {plot_configs[i]['id']}"))
591
 
 
592
  all_updates.extend([
593
+ gr.update(visible=False),
594
+ gr.update(value=[], visible=False),
595
+ gr.update(value="", visible=False),
596
+ gr.update(visible=False),
597
+ gr.update(value="Suggerimento 1", visible=True),
598
+ gr.update(value="Suggerimento 2", visible=True),
599
+ gr.update(value="Suggerimento 3", visible=True),
600
+ gr.update(value="I dettagli sulla formula/metodologia appariranno qui.", visible=False),
601
+ None,
602
+ None,
603
+ current_chat_histories,
 
604
  ])
605
 
 
606
  for cfg in plot_configs:
607
  pid = cfg["id"]
608
  if pid in plot_ui_objects:
609
+ all_updates.append(gr.update(value=BOMB_ICON))
610
+ all_updates.append(gr.update(value=FORMULA_ICON))
611
+ all_updates.append(gr.update(value=EXPLORE_ICON))
612
+ all_updates.append(gr.update(visible=True))
613
+ else:
614
  all_updates.extend([None, None, None, None])
615
 
616
+ all_updates.append(None)
617
 
618
  logging.info(f"Preparati {len(all_updates)} aggiornamenti per il refresh completo delle analisi.")
619
  return all_updates
620
 
621
+ apply_filter_and_sync_outputs_list = [analytics_status_md]
 
 
622
  for config_item_filter_sync in plot_configs:
623
  pid_filter_sync = config_item_filter_sync["id"]
624
  if pid_filter_sync in plot_ui_objects and "plot_component" in plot_ui_objects[pid_filter_sync]:
625
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync]["plot_component"])
626
  else:
627
+ apply_filter_and_sync_outputs_list.append(None)
628
 
 
629
  apply_filter_and_sync_outputs_list.extend([
630
+ global_actions_column_ui,
631
+ insights_chatbot_ui,
632
+ insights_chat_input_ui,
633
+ insights_suggestions_row_ui,
634
+ insights_suggestion_1_btn,
635
+ insights_suggestion_2_btn,
636
+ insights_suggestion_3_btn,
637
+ formula_display_markdown_ui,
638
+ active_panel_action_state,
639
+ current_chat_plot_id_st,
640
+ chat_histories_st
641
  ])
642
 
 
643
  for cfg_filter_sync_btns in plot_configs:
644
  pid_filter_sync_btns = cfg_filter_sync_btns["id"]
645
  if pid_filter_sync_btns in plot_ui_objects:
646
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["bomb_button"])
647
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["formula_button"])
648
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["explore_button"])
649
+ apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["panel_component"])
650
  else:
651
+ apply_filter_and_sync_outputs_list.extend([None, None, None, None])
652
 
653
+ apply_filter_and_sync_outputs_list.append(explored_plot_id_state)
654
 
655
  logging.info(f"Output totali definiti per apply_filter/sync: {len(apply_filter_and_sync_outputs_list)}")
656
 
 
674
  with gr.TabItem("4️⃣ Statistiche Follower", id="tab_follower_stats"):
675
  refresh_follower_stats_btn = gr.Button("🔄 Aggiorna Visualizzazione Statistiche Follower", variant="secondary")
676
  follower_stats_html = gr.HTML("Statistiche follower...")
677
+ with gr.Row():
678
  fs_plot_monthly_gains = gr.Plot(label="Guadagni Mensili Follower")
679
  with gr.Row():
680
  fs_plot_seniority = gr.Plot(label="Follower per Anzianità (Top 10 Organici)")
 
686
  show_progress="full"
687
  )
688
 
 
689
  sync_event_part1 = sync_data_btn.click(
690
  fn=sync_all_linkedin_data_orchestrator,
691
  inputs=[token_state], outputs=[sync_status_html_output, token_state], show_progress="full"
692
  )
693
+ sync_event_part2 = sync_event_part1.then(
694
+ fn=process_and_store_bubble_token,
695
  inputs=[url_user_token_display, org_urn_display, token_state],
696
+ outputs=[status_box, token_state, sync_data_btn], show_progress=False
697
  )
698
  sync_event_part3 = sync_event_part2.then(
699
+ fn=display_main_dashboard,
700
  inputs=[token_state], outputs=[dashboard_display_html], show_progress=False
701
  )
 
702
  sync_event_final = sync_event_part3.then(
703
  fn=refresh_all_analytics_ui_elements,
704
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
705
+ outputs=apply_filter_and_sync_outputs_list,
706
  show_progress="full"
707
  )
708
 
 
716
 
717
  try:
718
  logging.info(f"Versione Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
719
+ except ImportError:
720
  logging.warning("Matplotlib non trovato direttamente, ma potrebbe essere usato dai generatori di grafici.")
721
 
722
  app.launch(server_name="0.0.0.0", server_port=7860, debug=True)