GuglielmoTor commited on
Commit
a6bc02b
·
verified ·
1 Parent(s): bc9479c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -365
app.py CHANGED
@@ -10,6 +10,7 @@ import matplotlib.pyplot as plt
10
  import time # For profiling if needed
11
  from datetime import datetime, timedelta # Added timedelta
12
  import numpy as np
 
13
 
14
  # --- Module Imports ---
15
  from gradio_utils import get_url_user_token
@@ -24,10 +25,11 @@ from ui_generators import (
24
  display_main_dashboard,
25
  run_mentions_tab_display,
26
  run_follower_stats_tab_display,
27
- build_analytics_tab_plot_area,
28
- BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON
 
29
  )
30
- from analytics_plot_generator import update_analytics_plots_figures, create_placeholder_plot
31
  from formulas import PLOT_FORMULAS
32
 
33
  # --- NEW CHATBOT MODULE IMPORTS ---
@@ -130,15 +132,42 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
130
  {"label": "Ripartizione Menzioni per Sentiment (Dettaglio)", "id": "mention_analysis_sentiment", "section": "Analisi Menzioni (Dettaglio)"}
131
  ]
132
  assert len(plot_configs) == 19, "Mancata corrispondenza in plot_configs e grafici attesi."
 
 
 
 
133
 
134
  active_panel_action_state = gr.State(None)
135
  explored_plot_id_state = gr.State(None)
136
 
137
- plot_ui_objects = {}
 
 
 
138
 
139
  with gr.Row(equal_height=False):
140
  with gr.Column(scale=8) as plots_area_col:
141
- plot_ui_objects = build_analytics_tab_plot_area(plot_configs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  with gr.Column(scale=4, visible=False) as global_actions_column_ui:
144
  gr.Markdown("### 💡 Azioni Contestuali Grafico")
@@ -162,12 +191,12 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
162
 
163
  async def handle_panel_action(
164
  plot_id_clicked: str,
165
- action_type: str, # "insights" or "formula"
166
  current_active_action_from_state: dict,
167
  current_chat_histories: dict,
168
  current_chat_plot_id: str,
169
  current_plot_data_for_chatbot: dict,
170
- current_explored_plot_id: str # NEW: Current explore state
171
  ):
172
  logging.info(f"Azione '{action_type}' per grafico: {plot_id_clicked}. Attualmente attivo: {current_active_action_from_state}, Esplorato: {current_explored_plot_id}")
173
 
@@ -180,453 +209,286 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
180
  gr.update(visible=False), gr.update(visible=False), gr.update(), gr.update(), gr.update(),
181
  gr.update(visible=False), gr.update(value=""),
182
  current_active_action_from_state, current_chat_plot_id, current_chat_histories,
183
- current_explored_plot_id # Pass back existing explore state
184
  ]
185
  error_output_list.extend([gr.update()] * num_plots) # Panel vis
186
  error_output_list.extend([gr.update()] * (2 * num_plots)) # Bomb, Formula icons
187
  error_output_list.extend([gr.update()] * num_plots) # Explore icons
 
188
  return error_output_list
189
 
190
  clicked_plot_label = clicked_plot_config["label"]
 
191
  hypothetical_new_active_state = {"plot_id": plot_id_clicked, "type": action_type}
192
  is_toggling_off = current_active_action_from_state == hypothetical_new_active_state
193
 
194
- # Initialize updates
195
  action_col_visible_update = gr.update(visible=False)
196
- insights_chatbot_visible_update = gr.update(visible=False)
197
- insights_chat_input_visible_update = gr.update(visible=False)
198
- insights_suggestions_row_visible_update = gr.update(visible=False)
199
  formula_display_visible_update = gr.update(visible=False)
200
- chatbot_content_update = gr.update()
201
- suggestion_1_update, suggestion_2_update, suggestion_3_update = gr.update(), gr.update(), gr.update()
202
- formula_content_update = gr.update()
203
 
204
  new_active_action_state_to_set = None
205
  new_current_chat_plot_id = current_chat_plot_id
206
  updated_chat_histories = current_chat_histories
207
- new_explored_plot_id_to_set = current_explored_plot_id # Default to not changing explore state
208
 
209
- panel_visibility_component_updates = []
210
- action_button_component_updates = [] # For bomb and formula buttons
211
- explore_button_component_updates = []
 
212
 
213
 
214
  if is_toggling_off:
215
  new_active_action_state_to_set = None
216
  action_col_visible_update = gr.update(visible=False)
217
- # new_explored_plot_id_to_set remains current_explored_plot_id
218
-
219
  logging.info(f"Chiusura pannello {action_type} per {plot_id_clicked}.")
220
- if current_explored_plot_id: # If an explore view is active, keep it
 
 
 
 
 
221
  for cfg_item_iter in plot_configs:
222
- p_id_iter = cfg_item_iter["id"]
223
- is_explored = (p_id_iter == current_explored_plot_id)
224
- panel_visibility_component_updates.append(gr.update(visible=is_explored))
225
- explore_button_component_updates.append(gr.update(value=ACTIVE_ICON if is_explored else EXPLORE_ICON))
226
- else: # No explore active, make all plots visible
227
  for _ in plot_configs:
228
- panel_visibility_component_updates.append(gr.update(visible=True))
229
- explore_button_component_updates.append(gr.update(value=EXPLORE_ICON))
230
-
231
- # Reset bomb/formula icons for all plots to default
232
- for _ in plot_configs:
233
- action_button_component_updates.append(gr.update(value=BOMB_ICON))
234
- action_button_component_updates.append(gr.update(value=FORMULA_ICON))
235
 
 
 
 
236
  if action_type == "insights": new_current_chat_plot_id = None
237
 
238
  else: # Opening a new panel or switching
239
  new_active_action_state_to_set = hypothetical_new_active_state
240
  action_col_visible_update = gr.update(visible=True)
241
-
242
- # IMPORTANT: Cancel any active explore view when opening insight/formula
243
- new_explored_plot_id_to_set = None
244
 
245
  logging.info(f"Apertura pannello {action_type} per {plot_id_clicked}. Solo questo grafico visibile. Esplorazione annullata.")
246
 
247
- for cfg_item_iter in plot_configs:
248
- p_id_iter = cfg_item_iter["id"]
249
- is_target_plot = (p_id_iter == plot_id_clicked)
250
- panel_visibility_component_updates.append(gr.update(visible=is_target_plot))
251
- # Reset all explore buttons to default as explore is cancelled
252
- explore_button_component_updates.append(gr.update(value=EXPLORE_ICON))
253
-
254
- # Set active icons for the clicked plot's bomb/formula buttons
255
- for cfg_item_btn_iter in plot_configs:
256
- p_id_btn_iter = cfg_item_btn_iter["id"]
257
- is_active_insights = new_active_action_state_to_set == {"plot_id": p_id_btn_iter, "type": "insights"}
258
- is_active_formula = new_active_action_state_to_set == {"plot_id": p_id_btn_iter, "type": "formula"}
259
- action_button_component_updates.append(gr.update(value=ACTIVE_ICON if is_active_insights else BOMB_ICON))
260
- action_button_component_updates.append(gr.update(value=ACTIVE_ICON if is_active_formula else FORMULA_ICON))
261
 
262
  if action_type == "insights":
263
- insights_chatbot_visible_update = gr.update(visible=True)
264
- insights_chat_input_visible_update = gr.update(visible=True)
265
- insights_suggestions_row_visible_update = gr.update(visible=True)
266
  new_current_chat_plot_id = plot_id_clicked
267
  chat_history_for_this_plot = current_chat_histories.get(plot_id_clicked, [])
268
  plot_specific_data_summary = current_plot_data_for_chatbot.get(plot_id_clicked, f"Nessun sommario dati specifico disponibile per '{clicked_plot_label}'.")
269
-
270
  if not chat_history_for_this_plot:
271
- initial_llm_prompt, suggestions = get_initial_insight_prompt_and_suggestions(
272
- plot_id_clicked, clicked_plot_label, plot_specific_data_summary
273
- )
274
  history_for_llm_first_turn = [{"role": "user", "content": initial_llm_prompt}]
275
- initial_bot_response_text = await generate_llm_response(
276
- initial_llm_prompt, plot_id_clicked, clicked_plot_label,
277
- history_for_llm_first_turn, plot_specific_data_summary
278
- )
279
  chat_history_for_this_plot = [{"role": "assistant", "content": initial_bot_response_text}]
280
- updated_chat_histories = current_chat_histories.copy()
281
- updated_chat_histories[plot_id_clicked] = chat_history_for_this_plot
282
  else:
283
- _, suggestions = get_initial_insight_prompt_and_suggestions(
284
- plot_id_clicked, clicked_plot_label, plot_specific_data_summary
285
- )
286
-
287
  chatbot_content_update = gr.update(value=chat_history_for_this_plot)
288
- suggestion_1_update = gr.update(value=suggestions[0] if len(suggestions) > 0 else "N/A")
289
- suggestion_2_update = gr.update(value=suggestions[1] if len(suggestions) > 1 else "N/A")
290
- suggestion_3_update = gr.update(value=suggestions[2] if len(suggestions) > 2 else "N/A")
291
-
292
  elif action_type == "formula":
293
  formula_display_visible_update = gr.update(visible=True)
294
  formula_key = PLOT_ID_TO_FORMULA_KEY_MAP.get(plot_id_clicked)
295
  formula_text = f"**Formula/Metodologia per: {clicked_plot_label}**\n\nID Grafico: `{plot_id_clicked}`.\n\n"
296
- if formula_key and formula_key in PLOT_FORMULAS:
297
- formula_data = PLOT_FORMULAS[formula_key]
298
- formula_text += f"### {formula_data['title']}\n\n{formula_data['description']}\n\n**Come viene calcolato:**\n"
299
- formula_text += "\n".join([f"- {step}" for step in formula_data['calculation_steps']])
300
- else:
301
- formula_text += "(Nessuna informazione dettagliata sulla formula trovata)"
302
  formula_content_update = gr.update(value=formula_text)
303
- new_current_chat_plot_id = None # Ensure chat is cleared if formula is opened
304
 
305
  final_updates = [
306
- action_col_visible_update,
307
- insights_chatbot_visible_update, chatbot_content_update,
308
- insights_chat_input_visible_update,
309
- insights_suggestions_row_visible_update, suggestion_1_update, suggestion_2_update, suggestion_3_update,
310
  formula_display_visible_update, formula_content_update,
311
- new_active_action_state_to_set,
312
- new_current_chat_plot_id,
313
- updated_chat_histories,
314
- new_explored_plot_id_to_set # NEW: update explored_plot_id_state
315
  ]
316
- final_updates.extend(panel_visibility_component_updates)
317
- final_updates.extend(action_button_component_updates)
318
- final_updates.extend(explore_button_component_updates)
319
-
320
  return final_updates
321
 
322
- async def handle_chat_message_submission(
323
- user_message: str,
324
- current_plot_id: str,
325
- chat_histories: dict,
326
- current_plot_data_for_chatbot: dict
327
- ):
328
  if not current_plot_id or not user_message.strip():
329
- history_for_plot = chat_histories.get(current_plot_id, [])
330
- yield history_for_plot, gr.update(value=""), chat_histories
331
  return
332
-
333
  plot_config = next((p for p in plot_configs if p["id"] == current_plot_id), None)
334
  plot_label = plot_config["label"] if plot_config else "Grafico Selezionato"
335
  plot_specific_data_summary = current_plot_data_for_chatbot.get(current_plot_id, f"Nessun sommario dati specifico disponibile per '{plot_label}'.")
336
- history_for_plot = chat_histories.get(current_plot_id, []).copy()
337
- history_for_plot.append({"role": "user", "content": user_message})
338
  yield history_for_plot, gr.update(value=""), chat_histories
339
-
340
- bot_response_text = await generate_llm_response(
341
- user_message, current_plot_id, plot_label, history_for_plot, plot_specific_data_summary
342
- )
343
  history_for_plot.append({"role": "assistant", "content": bot_response_text})
344
- updated_chat_histories = chat_histories.copy()
345
- updated_chat_histories[current_plot_id] = history_for_plot
346
- yield history_for_plot, "", updated_chat_histories
347
-
348
- async def handle_suggested_question_click(
349
- suggestion_text: str,
350
- current_plot_id: str,
351
- chat_histories: dict,
352
- current_plot_data_for_chatbot: dict
353
- ):
354
  if not current_plot_id or not suggestion_text.strip():
355
- history_for_plot = chat_histories.get(current_plot_id, [])
356
- yield history_for_plot, gr.update(value=""), chat_histories
357
  return
358
- async for update in handle_chat_message_submission(
359
- suggestion_text, current_plot_id, chat_histories, current_plot_data_for_chatbot
360
- ):
361
  yield update
362
 
363
- def handle_explore_click(
364
- plot_id_clicked,
365
- current_explored_plot_id_from_state,
366
- current_active_panel_action_state # NEW: current insight/formula state
367
- ):
368
- logging.info(f"Click su Esplora per: {plot_id_clicked}. Attualmente esplorato: {current_explored_plot_id_from_state}. Pannello Azioni Attivo: {current_active_panel_action_state}")
369
- if not plot_ui_objects:
370
- logging.error("plot_ui_objects non popolato durante handle_explore_click.")
371
- num_plots = len(plot_configs)
372
- error_updates = [current_explored_plot_id_from_state, gr.update(visible=False), current_active_panel_action_state]
373
- error_updates.extend([gr.update()] * num_plots) # Panel vis
374
- error_updates.extend([gr.update()] * num_plots) # Explore icons
375
- error_updates.extend([gr.update()] * (2 * num_plots)) # Bomb, Formula icons
376
  return error_updates
377
 
378
  new_explored_id_to_set = None
379
  is_toggling_off_explore = (plot_id_clicked == current_explored_plot_id_from_state)
380
-
381
  action_col_update = gr.update()
382
  new_active_panel_action_state_update = current_active_panel_action_state
383
 
384
- panel_vis_updates = []
385
- explore_btn_updates = []
386
- bomb_btn_icon_upds = []
387
- formula_btn_icon_upds = []
 
388
 
389
  if is_toggling_off_explore:
390
  new_explored_id_to_set = None
391
- logging.info(f"Interruzione esplorazione grafico: {plot_id_clicked}. Tutti i grafici diventano visibili.")
392
- # All plots become visible
393
  for _ in plot_configs:
394
  panel_vis_updates.append(gr.update(visible=True))
395
  explore_btn_updates.append(gr.update(value=EXPLORE_ICON))
396
- # Insight/formula buttons remain as they were (no change)
397
- for _ in plot_configs:
398
- bomb_btn_icon_upds.append(gr.update())
399
- formula_btn_icon_upds.append(gr.update())
400
- else: # Activating explore for plot_id_clicked or switching explore
401
  new_explored_id_to_set = plot_id_clicked
402
- logging.info(f"Esplorazione grafico: {plot_id_clicked}. Altri grafici nascosti.")
403
- # Make only explored plot visible
 
404
  for cfg in plot_configs:
405
- p_id = cfg["id"]
406
- is_target_plot = (p_id == new_explored_id_to_set)
407
- panel_vis_updates.append(gr.update(visible=is_target_plot))
408
- explore_btn_updates.append(gr.update(value=ACTIVE_ICON if is_target_plot else EXPLORE_ICON))
409
-
410
- # If activating explore, close any active insight/formula panel
411
- if current_active_panel_action_state:
412
- logging.info("Chiusura pannello azioni (insight/formula) a causa dell'attivazione dell'esplorazione.")
413
  action_col_update = gr.update(visible=False)
414
  new_active_panel_action_state_update = None
415
- # Reset all insight/formula button icons
416
  for _ in plot_configs:
417
  bomb_btn_icon_upds.append(gr.update(value=BOMB_ICON))
418
  formula_btn_icon_upds.append(gr.update(value=FORMULA_ICON))
419
- else: # No insight/formula panel active, no change to their buttons
420
- for _ in plot_configs:
421
- bomb_btn_icon_upds.append(gr.update())
422
- formula_btn_icon_upds.append(gr.update())
423
 
424
- final_updates = [
425
- new_explored_id_to_set,
426
- action_col_update,
427
- new_active_panel_action_state_update
428
- ]
429
- final_updates.extend(panel_vis_updates)
430
- final_updates.extend(explore_btn_updates)
431
- final_updates.extend(bomb_btn_icon_upds)
432
- final_updates.extend(formula_btn_icon_upds)
433
- return final_updates
434
 
435
  # --- Define Output Lists for Event Handlers ---
436
- action_panel_outputs_list = [
437
- global_actions_column_ui,
438
- insights_chatbot_ui, insights_chatbot_ui,
439
- insights_chat_input_ui,
440
- insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
441
- formula_display_markdown_ui, formula_display_markdown_ui,
442
- active_panel_action_state,
443
- current_chat_plot_id_st,
444
- chat_histories_st,
445
- explored_plot_id_state # NEW: output for explore state
446
- ]
447
- for cfg_item_action_out in plot_configs:
448
- pid_action_out = cfg_item_action_out["id"]
449
- ui_obj_ref = plot_ui_objects.get(pid_action_out)
450
- action_panel_outputs_list.append(ui_obj_ref["panel_component"] if ui_obj_ref else gr.update()) # Panel visibility
451
- for cfg_item_action_out in plot_configs:
452
- pid_action_out = cfg_item_action_out["id"]
453
- ui_obj_ref = plot_ui_objects.get(pid_action_out)
454
- action_panel_outputs_list.append(ui_obj_ref["bomb_button"] if ui_obj_ref else gr.update()) # Bomb icon
455
- action_panel_outputs_list.append(ui_obj_ref["formula_button"] if ui_obj_ref else gr.update()) # Formula icon
456
- for cfg_item_action_out in plot_configs:
457
- pid_action_out = cfg_item_action_out["id"]
458
- ui_obj_ref = plot_ui_objects.get(pid_action_out)
459
- action_panel_outputs_list.append(ui_obj_ref["explore_button"] if ui_obj_ref else gr.update()) # Explore icon
460
-
461
- explore_outputs_list = [
462
- explored_plot_id_state,
463
- global_actions_column_ui, # NEW: output for action column
464
- active_panel_action_state # NEW: output for active panel state
465
- ]
466
- for cfg_item_explore_out in plot_configs: # Panel visibility
467
- pid_explore_out = cfg_item_explore_out["id"]
468
- ui_obj_ref = plot_ui_objects.get(pid_explore_out)
469
- explore_outputs_list.append(ui_obj_ref["panel_component"] if ui_obj_ref else gr.update())
470
- for cfg_item_explore_out in plot_configs: # Explore button icons
471
- pid_explore_out = cfg_item_explore_out["id"]
472
- ui_obj_ref = plot_ui_objects.get(pid_explore_out)
473
- explore_outputs_list.append(ui_obj_ref["explore_button"] if ui_obj_ref else gr.update())
474
- for cfg_item_explore_out in plot_configs: # Bomb button icons
475
- pid_explore_out = cfg_item_explore_out["id"]
476
- ui_obj_ref = plot_ui_objects.get(pid_explore_out)
477
- explore_outputs_list.append(ui_obj_ref["bomb_button"] if ui_obj_ref else gr.update())
478
- for cfg_item_explore_out in plot_configs: # Formula button icons
479
- pid_explore_out = cfg_item_explore_out["id"]
480
- ui_obj_ref = plot_ui_objects.get(pid_explore_out)
481
- explore_outputs_list.append(ui_obj_ref["formula_button"] if ui_obj_ref else gr.update())
482
-
483
-
484
- # --- Define Input Lists for Event Handlers ---
485
- action_click_inputs = [
486
- active_panel_action_state,
487
- chat_histories_st,
488
- current_chat_plot_id_st,
489
- plot_data_for_chatbot_st,
490
- explored_plot_id_state # NEW: input explore state
491
- ]
492
- explore_click_inputs = [
493
- explored_plot_id_state,
494
- active_panel_action_state # NEW: input active panel state
495
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
 
497
- # --- Register Event Handlers ---
498
  def create_panel_action_handler(p_id, action_type_str):
499
- async def _handler(current_active_val, current_chats_val, current_chat_pid, current_plot_data_summaries, current_explored_id_val): # Added current_explored_id_val
500
- logging.debug(f"Entering _handler for plot_id: {p_id}, action: {action_type_str}")
501
- result = await handle_panel_action(p_id, action_type_str, current_active_val, current_chats_val, current_chat_pid, current_plot_data_summaries, current_explored_id_val) # Pass current_explored_id_val
502
- logging.debug(f"_handler for plot_id: {p_id}, action: {action_type_str} completed with {len(result)} updates.")
503
- return result
504
  return _handler
505
 
506
  for config_item in plot_configs:
507
  plot_id = config_item["id"]
508
  if plot_id in plot_ui_objects:
509
  ui_obj = plot_ui_objects[plot_id]
510
- ui_obj["bomb_button"].click(
511
- fn=create_panel_action_handler(plot_id, "insights"),
512
- inputs=action_click_inputs,
513
- outputs=action_panel_outputs_list,
514
- api_name=f"action_insights_{plot_id}"
515
- )
516
- ui_obj["formula_button"].click(
517
- fn=create_panel_action_handler(plot_id, "formula"),
518
- inputs=action_click_inputs,
519
- outputs=action_panel_outputs_list,
520
- api_name=f"action_formula_{plot_id}"
521
- )
522
- ui_obj["explore_button"].click(
523
- 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),
524
- inputs=explore_click_inputs, # Uses updated explore_click_inputs
525
- outputs=explore_outputs_list, # Uses updated explore_outputs_list
526
- api_name=f"action_explore_{plot_id}"
527
- )
528
- else:
529
- logging.warning(f"Oggetto UI per plot_id '{plot_id}' non trovato durante il tentativo di associare i gestori di click.")
530
 
531
  chat_submission_outputs = [insights_chatbot_ui, insights_chat_input_ui, chat_histories_st]
532
  chat_submission_inputs = [insights_chat_input_ui, current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st]
533
-
534
- insights_chat_input_ui.submit(
535
- fn=handle_chat_message_submission,
536
- inputs=chat_submission_inputs,
537
- outputs=chat_submission_outputs,
538
- api_name="submit_chat_message"
539
- )
540
-
541
  suggestion_click_inputs = [current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st]
542
- insights_suggestion_1_btn.click(
543
- fn=handle_suggested_question_click,
544
- inputs=[insights_suggestion_1_btn] + suggestion_click_inputs,
545
- outputs=chat_submission_outputs, api_name="click_suggestion_1"
546
- )
547
- insights_suggestion_2_btn.click(
548
- fn=handle_suggested_question_click,
549
- inputs=[insights_suggestion_2_btn] + suggestion_click_inputs,
550
- outputs=chat_submission_outputs, api_name="click_suggestion_2"
551
- )
552
- insights_suggestion_3_btn.click(
553
- fn=handle_suggested_question_click,
554
- inputs=[insights_suggestion_3_btn] + suggestion_click_inputs,
555
- outputs=chat_submission_outputs, api_name="click_suggestion_3"
556
- )
557
 
558
  def refresh_all_analytics_ui_elements(current_token_state, date_filter_val, custom_start_val, custom_end_val, current_chat_histories):
559
- logging.info("Aggiornamento di tutti gli elementi UI delle analisi e reset delle azioni/chat.")
560
- plot_generation_results = update_analytics_plots_figures(
561
- current_token_state, date_filter_val, custom_start_val, custom_end_val, plot_configs
562
- )
563
- status_message_update = plot_generation_results[0]
564
- generated_plot_figures = plot_generation_results[1:-1]
565
- new_plot_data_summaries = plot_generation_results[-1]
566
-
567
- all_updates = [status_message_update]
568
- for i in range(len(plot_configs)):
569
- if i < len(generated_plot_figures):
570
- all_updates.append(generated_plot_figures[i])
571
- else:
572
- # Ensure create_placeholder_plot is defined or handle appropriately
573
- all_updates.append(create_placeholder_plot("Errore Figura", f"Figura mancante per grafico {plot_configs[i]['id']}"))
574
 
575
- all_updates.extend([
576
- gr.update(visible=False), # global_actions_column_ui
577
- gr.update(value=[], visible=False), # insights_chatbot_ui
578
- gr.update(value="", visible=False), # insights_chat_input_ui
579
- gr.update(visible=False), # insights_suggestions_row_ui
580
- gr.update(value="Suggerimento 1"), gr.update(value="Suggerimento 2"), gr.update(value="Suggerimento 3"),
581
- gr.update(value="I dettagli sulla formula/metodologia appariranno qui.", visible=False), # formula_display_markdown_ui
582
- None, # active_panel_action_state
583
- None, # current_chat_plot_id_st
584
- {}, # chat_histories_st (reset)
585
- new_plot_data_summaries # plot_data_for_chatbot_st
586
- ])
587
-
588
- for cfg in plot_configs:
589
- pid = cfg["id"]
590
- # Reset button icons
591
- all_updates.append(gr.update(value=BOMB_ICON))
592
- all_updates.append(gr.update(value=FORMULA_ICON))
593
- all_updates.append(gr.update(value=EXPLORE_ICON))
594
- # Make all plot panels visible
595
- all_updates.append(gr.update(visible=True)) # panel_component visibility
596
 
597
- all_updates.append(None) # explored_plot_id_state (reset)
598
- logging.info(f"Preparati {len(all_updates)} aggiornamenti per il refresh delle analisi.")
 
 
 
 
 
 
 
 
 
599
  return all_updates
600
 
601
- apply_filter_and_sync_outputs_list = [analytics_status_md]
602
- for config_item_filter_sync in plot_configs: # Plot components
603
- pid = config_item_filter_sync["id"]
604
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["plot_component"] if pid in plot_ui_objects and "plot_component" in plot_ui_objects[pid] else gr.update())
605
-
606
- apply_filter_and_sync_outputs_list.extend([ # UI elements to reset
607
  global_actions_column_ui, insights_chatbot_ui, insights_chat_input_ui,
608
  insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
609
  formula_display_markdown_ui,
610
- active_panel_action_state, current_chat_plot_id_st, chat_histories_st,
611
- plot_data_for_chatbot_st
612
  ])
613
- for cfg_filter_sync_btns in plot_configs: # Button icons and panel visibility
614
- pid = cfg_filter_sync_btns["id"]
615
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["bomb_button"] if pid in plot_ui_objects else gr.update())
616
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["formula_button"] if pid in plot_ui_objects else gr.update())
617
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["explore_button"] if pid in plot_ui_objects else gr.update())
618
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["panel_component"] if pid in plot_ui_objects else gr.update())
619
-
 
620
  apply_filter_and_sync_outputs_list.append(explored_plot_id_state) # Reset explored state
 
621
 
622
  apply_filter_btn.click(
623
  fn=refresh_all_analytics_ui_elements,
624
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
625
- outputs=apply_filter_and_sync_outputs_list,
626
- show_progress="full"
627
  )
628
 
629
  with gr.TabItem("3️⃣ Menzioni", id="tab_mentions"):
 
630
  refresh_mentions_display_btn = gr.Button("🔄 Aggiorna Visualizzazione Menzioni", variant="secondary")
631
  mentions_html = gr.HTML("Dati menzioni...")
632
  mentions_sentiment_dist_plot = gr.Plot(label="Distribuzione Sentiment Menzioni")
@@ -637,6 +499,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
637
  )
638
 
639
  with gr.TabItem("4️⃣ Statistiche Follower", id="tab_follower_stats"):
 
640
  refresh_follower_stats_btn = gr.Button("🔄 Aggiorna Visualizzazione Statistiche Follower", variant="secondary")
641
  follower_stats_html = gr.HTML("Statistiche follower...")
642
  with gr.Row():
@@ -651,37 +514,15 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
651
  show_progress="full"
652
  )
653
 
654
- sync_event_part1 = sync_data_btn.click(
655
- fn=sync_all_linkedin_data_orchestrator,
656
- inputs=[token_state], outputs=[sync_status_html_output, token_state], show_progress="full"
657
- )
658
- sync_event_part2 = sync_event_part1.then(
659
- fn=process_and_store_bubble_token,
660
- inputs=[url_user_token_display, org_urn_display, token_state],
661
- outputs=[status_box, token_state, sync_data_btn], show_progress=False
662
- )
663
- sync_event_part3 = sync_event_part2.then(
664
- fn=display_main_dashboard,
665
- inputs=[token_state], outputs=[dashboard_display_html], show_progress=False
666
- )
667
- sync_event_final = sync_event_part3.then(
668
- fn=refresh_all_analytics_ui_elements,
669
- inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
670
- outputs=apply_filter_and_sync_outputs_list,
671
- show_progress="full"
672
- )
673
 
674
  if __name__ == "__main__":
675
- if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR):
676
- logging.warning(f"ATTENZIONE: Variabile d'ambiente '{LINKEDIN_CLIENT_ID_ENV_VAR}' non impostata.")
677
- if not os.environ.get(BUBBLE_APP_NAME_ENV_VAR) or \
678
- not os.environ.get(BUBBLE_API_KEY_PRIVATE_ENV_VAR) or \
679
- not os.environ.get(BUBBLE_API_ENDPOINT_ENV_VAR):
680
- logging.warning("ATTENZIONE: Variabili d'ambiente Bubble non completamente impostate.")
681
-
682
- try:
683
- logging.info(f"Versione Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
684
- except ImportError:
685
- logging.warning("Matplotlib non trovato direttamente, ma potrebbe essere usato dai generatori di grafici.")
686
-
687
  app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
 
10
  import time # For profiling if needed
11
  from datetime import datetime, timedelta # Added timedelta
12
  import numpy as np
13
+ from collections import OrderedDict # To maintain section order
14
 
15
  # --- Module Imports ---
16
  from gradio_utils import get_url_user_token
 
25
  display_main_dashboard,
26
  run_mentions_tab_display,
27
  run_follower_stats_tab_display,
28
+ build_analytics_tab_plot_area, # EXPECTED TO RETURN: plot_ui_objects, section_titles_map
29
+ BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON,
30
+ create_placeholder_plot # Assuming you have this or similar for error cases
31
  )
32
+ from analytics_plot_generator import update_analytics_plots_figures
33
  from formulas import PLOT_FORMULAS
34
 
35
  # --- NEW CHATBOT MODULE IMPORTS ---
 
132
  {"label": "Ripartizione Menzioni per Sentiment (Dettaglio)", "id": "mention_analysis_sentiment", "section": "Analisi Menzioni (Dettaglio)"}
133
  ]
134
  assert len(plot_configs) == 19, "Mancata corrispondenza in plot_configs e grafici attesi."
135
+
136
+ # Get unique section names in order of appearance in plot_configs
137
+ unique_ordered_sections = list(OrderedDict.fromkeys(pc["section"] for pc in plot_configs))
138
+ num_unique_sections = len(unique_ordered_sections)
139
 
140
  active_panel_action_state = gr.State(None)
141
  explored_plot_id_state = gr.State(None)
142
 
143
+ # plot_ui_objects will store plot-specific UI like panels and buttons
144
+ # section_titles_map will store Markdown components for section titles
145
+ plot_ui_objects = {}
146
+ section_titles_map = {} # Expected to be populated by build_analytics_tab_plot_area
147
 
148
  with gr.Row(equal_height=False):
149
  with gr.Column(scale=8) as plots_area_col:
150
+ # IMPORTANT ASSUMPTION: build_analytics_tab_plot_area now returns a tuple:
151
+ # (dict_of_plot_ui_elements, dict_of_section_title_markdown_components)
152
+ # Ensure ui_generators.py is updated accordingly.
153
+ ui_elements_tuple = build_analytics_tab_plot_area(plot_configs)
154
+ if isinstance(ui_elements_tuple, tuple) and len(ui_elements_tuple) == 2:
155
+ plot_ui_objects = ui_elements_tuple[0]
156
+ section_titles_map = ui_elements_tuple[1]
157
+ # Verify section_titles_map contains all unique_ordered_sections
158
+ if not all(sec_name in section_titles_map for sec_name in unique_ordered_sections):
159
+ logging.error("section_titles_map from build_analytics_tab_plot_area is incomplete or missing keys for defined sections.")
160
+ # Fallback for safety, though this indicates an issue in ui_generators
161
+ for sec_name in unique_ordered_sections:
162
+ if sec_name not in section_titles_map:
163
+ section_titles_map[sec_name] = gr.Markdown(f"## {sec_name} (Placeholder - Error)") # Placeholder
164
+ else:
165
+ logging.error("build_analytics_tab_plot_area did not return the expected tuple (plot_ui_objects, section_titles_map). Section titles will not work correctly.")
166
+ plot_ui_objects = ui_elements_tuple # Old behavior, section titles won't work
167
+ # Create placeholder section_titles_map to prevent crashes, but log error
168
+ for sec_name in unique_ordered_sections:
169
+ section_titles_map[sec_name] = gr.Markdown(f"## {sec_name} (Placeholder - Error)")
170
+
171
 
172
  with gr.Column(scale=4, visible=False) as global_actions_column_ui:
173
  gr.Markdown("### 💡 Azioni Contestuali Grafico")
 
191
 
192
  async def handle_panel_action(
193
  plot_id_clicked: str,
194
+ action_type: str,
195
  current_active_action_from_state: dict,
196
  current_chat_histories: dict,
197
  current_chat_plot_id: str,
198
  current_plot_data_for_chatbot: dict,
199
+ current_explored_plot_id: str
200
  ):
201
  logging.info(f"Azione '{action_type}' per grafico: {plot_id_clicked}. Attualmente attivo: {current_active_action_from_state}, Esplorato: {current_explored_plot_id}")
202
 
 
209
  gr.update(visible=False), gr.update(visible=False), gr.update(), gr.update(), gr.update(),
210
  gr.update(visible=False), gr.update(value=""),
211
  current_active_action_from_state, current_chat_plot_id, current_chat_histories,
212
+ current_explored_plot_id
213
  ]
214
  error_output_list.extend([gr.update()] * num_plots) # Panel vis
215
  error_output_list.extend([gr.update()] * (2 * num_plots)) # Bomb, Formula icons
216
  error_output_list.extend([gr.update()] * num_plots) # Explore icons
217
+ error_output_list.extend([gr.update()] * num_unique_sections) # Section title vis
218
  return error_output_list
219
 
220
  clicked_plot_label = clicked_plot_config["label"]
221
+ clicked_plot_section = clicked_plot_config["section"]
222
  hypothetical_new_active_state = {"plot_id": plot_id_clicked, "type": action_type}
223
  is_toggling_off = current_active_action_from_state == hypothetical_new_active_state
224
 
 
225
  action_col_visible_update = gr.update(visible=False)
226
+ insights_chatbot_visible_update, insights_chat_input_visible_update, insights_suggestions_row_visible_update = gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
 
227
  formula_display_visible_update = gr.update(visible=False)
228
+ chatbot_content_update, suggestion_1_update, suggestion_2_update, suggestion_3_update, formula_content_update = gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
 
 
229
 
230
  new_active_action_state_to_set = None
231
  new_current_chat_plot_id = current_chat_plot_id
232
  updated_chat_histories = current_chat_histories
233
+ new_explored_plot_id_to_set = current_explored_plot_id
234
 
235
+ panel_visibility_updates = []
236
+ action_button_updates = []
237
+ explore_button_updates = []
238
+ section_title_visibility_updates = [gr.update()] * num_unique_sections
239
 
240
 
241
  if is_toggling_off:
242
  new_active_action_state_to_set = None
243
  action_col_visible_update = gr.update(visible=False)
 
 
244
  logging.info(f"Chiusura pannello {action_type} per {plot_id_clicked}.")
245
+
246
+ if current_explored_plot_id:
247
+ explored_plot_cfg = next((p for p in plot_configs if p["id"] == current_explored_plot_id), None)
248
+ explored_section_to_show = explored_plot_cfg["section"] if explored_plot_cfg else None
249
+ for i, sec_name in enumerate(unique_ordered_sections):
250
+ section_title_visibility_updates[i] = gr.update(visible=(sec_name == explored_section_to_show))
251
  for cfg_item_iter in plot_configs:
252
+ is_explored = (cfg_item_iter["id"] == current_explored_plot_id)
253
+ panel_visibility_updates.append(gr.update(visible=is_explored))
254
+ explore_button_updates.append(gr.update(value=ACTIVE_ICON if is_explored else EXPLORE_ICON))
255
+ else:
256
+ for i in range(num_unique_sections): section_title_visibility_updates[i] = gr.update(visible=True)
257
  for _ in plot_configs:
258
+ panel_visibility_updates.append(gr.update(visible=True))
259
+ explore_button_updates.append(gr.update(value=EXPLORE_ICON))
 
 
 
 
 
260
 
261
+ for _ in plot_configs: # Reset bomb/formula icons
262
+ action_button_updates.append(gr.update(value=BOMB_ICON))
263
+ action_button_updates.append(gr.update(value=FORMULA_ICON))
264
  if action_type == "insights": new_current_chat_plot_id = None
265
 
266
  else: # Opening a new panel or switching
267
  new_active_action_state_to_set = hypothetical_new_active_state
268
  action_col_visible_update = gr.update(visible=True)
269
+ new_explored_plot_id_to_set = None # Cancel explore
 
 
270
 
271
  logging.info(f"Apertura pannello {action_type} per {plot_id_clicked}. Solo questo grafico visibile. Esplorazione annullata.")
272
 
273
+ for i, sec_name in enumerate(unique_ordered_sections): # Show only relevant section title
274
+ section_title_visibility_updates[i] = gr.update(visible=(sec_name == clicked_plot_section))
275
+
276
+ for cfg_item_iter in plot_configs: # Show only relevant plot panel
277
+ panel_visibility_updates.append(gr.update(visible=(cfg_item_iter["id"] == plot_id_clicked)))
278
+ explore_button_updates.append(gr.update(value=EXPLORE_ICON)) # Reset all explore buttons
279
+
280
+ for cfg_item_btn_iter in plot_configs: # Set active icons for bomb/formula
281
+ is_active_insights = new_active_action_state_to_set == {"plot_id": cfg_item_btn_iter["id"], "type": "insights"}
282
+ is_active_formula = new_active_action_state_to_set == {"plot_id": cfg_item_btn_iter["id"], "type": "formula"}
283
+ action_button_updates.append(gr.update(value=ACTIVE_ICON if is_active_insights else BOMB_ICON))
284
+ action_button_updates.append(gr.update(value=ACTIVE_ICON if is_active_formula else FORMULA_ICON))
 
 
285
 
286
  if action_type == "insights":
287
+ insights_chatbot_visible_update, insights_chat_input_visible_update, insights_suggestions_row_visible_update = gr.update(visible=True), gr.update(visible=True), gr.update(visible=True)
 
 
288
  new_current_chat_plot_id = plot_id_clicked
289
  chat_history_for_this_plot = current_chat_histories.get(plot_id_clicked, [])
290
  plot_specific_data_summary = current_plot_data_for_chatbot.get(plot_id_clicked, f"Nessun sommario dati specifico disponibile per '{clicked_plot_label}'.")
 
291
  if not chat_history_for_this_plot:
292
+ initial_llm_prompt, suggestions = get_initial_insight_prompt_and_suggestions(plot_id_clicked, clicked_plot_label, plot_specific_data_summary)
 
 
293
  history_for_llm_first_turn = [{"role": "user", "content": initial_llm_prompt}]
294
+ initial_bot_response_text = await generate_llm_response(initial_llm_prompt, plot_id_clicked, clicked_plot_label, history_for_llm_first_turn, plot_specific_data_summary)
 
 
 
295
  chat_history_for_this_plot = [{"role": "assistant", "content": initial_bot_response_text}]
296
+ updated_chat_histories = {**current_chat_histories, plot_id_clicked: chat_history_for_this_plot}
 
297
  else:
298
+ _, suggestions = get_initial_insight_prompt_and_suggestions(plot_id_clicked, clicked_plot_label, plot_specific_data_summary)
 
 
 
299
  chatbot_content_update = gr.update(value=chat_history_for_this_plot)
300
+ suggestion_1_update, suggestion_2_update, suggestion_3_update = gr.update(value=suggestions[0] if suggestions else "N/A"), gr.update(value=suggestions[1] if len(suggestions) > 1 else "N/A"), gr.update(value=suggestions[2] if len(suggestions) > 2 else "N/A")
 
 
 
301
  elif action_type == "formula":
302
  formula_display_visible_update = gr.update(visible=True)
303
  formula_key = PLOT_ID_TO_FORMULA_KEY_MAP.get(plot_id_clicked)
304
  formula_text = f"**Formula/Metodologia per: {clicked_plot_label}**\n\nID Grafico: `{plot_id_clicked}`.\n\n"
305
+ formula_text += PLOT_FORMULAS.get(formula_key, {}).get('description', "(Nessuna descrizione dettagliata)") if formula_key else "(Formula non definita)"
 
 
 
 
 
306
  formula_content_update = gr.update(value=formula_text)
307
+ new_current_chat_plot_id = None
308
 
309
  final_updates = [
310
+ action_col_visible_update, insights_chatbot_visible_update, chatbot_content_update,
311
+ insights_chat_input_visible_update, insights_suggestions_row_visible_update,
312
+ suggestion_1_update, suggestion_2_update, suggestion_3_update,
 
313
  formula_display_visible_update, formula_content_update,
314
+ new_active_action_state_to_set, new_current_chat_plot_id, updated_chat_histories,
315
+ new_explored_plot_id_to_set
 
 
316
  ]
317
+ final_updates.extend(panel_visibility_updates)
318
+ final_updates.extend(action_button_updates)
319
+ final_updates.extend(explore_button_updates)
320
+ final_updates.extend(section_title_visibility_updates)
321
  return final_updates
322
 
323
+ async def handle_chat_message_submission(user_message: str, current_plot_id: str, chat_histories: dict, current_plot_data_for_chatbot: dict ):
 
 
 
 
 
324
  if not current_plot_id or not user_message.strip():
325
+ yield chat_histories.get(current_plot_id, []), gr.update(value=""), chat_histories
 
326
  return
 
327
  plot_config = next((p for p in plot_configs if p["id"] == current_plot_id), None)
328
  plot_label = plot_config["label"] if plot_config else "Grafico Selezionato"
329
  plot_specific_data_summary = current_plot_data_for_chatbot.get(current_plot_id, f"Nessun sommario dati specifico disponibile per '{plot_label}'.")
330
+ history_for_plot = chat_histories.get(current_plot_id, []).copy() + [{"role": "user", "content": user_message}]
 
331
  yield history_for_plot, gr.update(value=""), chat_histories
332
+ bot_response_text = await generate_llm_response(user_message, current_plot_id, plot_label, history_for_plot, plot_specific_data_summary)
 
 
 
333
  history_for_plot.append({"role": "assistant", "content": bot_response_text})
334
+ yield history_for_plot, "", {**chat_histories, current_plot_id: history_for_plot}
335
+
336
+ async def handle_suggested_question_click(suggestion_text: str, current_plot_id: str, chat_histories: dict, current_plot_data_for_chatbot: dict):
 
 
 
 
 
 
 
337
  if not current_plot_id or not suggestion_text.strip():
338
+ yield chat_histories.get(current_plot_id, []), gr.update(value=""), chat_histories
 
339
  return
340
+ async for update in handle_chat_message_submission(suggestion_text, current_plot_id, chat_histories, current_plot_data_for_chatbot):
 
 
341
  yield update
342
 
343
+ def handle_explore_click(plot_id_clicked, current_explored_plot_id_from_state, current_active_panel_action_state):
344
+ logging.info(f"Click Esplora: {plot_id_clicked}. Esplorato: {current_explored_plot_id_from_state}. Pannello Attivo: {current_active_panel_action_state}")
345
+ num_plots = len(plot_configs)
346
+ if not plot_ui_objects: # Should use plot_ui_objects here
347
+ logging.error("plot_ui_objects non popolato.")
348
+ error_updates = [current_explored_plot_id_from_state, gr.update(visible=False), current_active_panel_action_state] + \
349
+ [gr.update()] * (num_plots + num_plots + 2 * num_plots + num_unique_sections)
 
 
 
 
 
 
350
  return error_updates
351
 
352
  new_explored_id_to_set = None
353
  is_toggling_off_explore = (plot_id_clicked == current_explored_plot_id_from_state)
 
354
  action_col_update = gr.update()
355
  new_active_panel_action_state_update = current_active_panel_action_state
356
 
357
+ panel_vis_updates, explore_btn_updates, bomb_btn_icon_upds, formula_btn_icon_upds = [], [], [], []
358
+ section_title_vis_updates = [gr.update()] * num_unique_sections
359
+
360
+ clicked_plot_cfg = next((p for p in plot_configs if p["id"] == plot_id_clicked), None)
361
+ section_of_clicked_plot = clicked_plot_cfg["section"] if clicked_plot_cfg else None
362
 
363
  if is_toggling_off_explore:
364
  new_explored_id_to_set = None
365
+ logging.info(f"Stop esplorazione: {plot_id_clicked}. Tutti i grafici/titoli visibili.")
366
+ for i in range(num_unique_sections): section_title_vis_updates[i] = gr.update(visible=True)
367
  for _ in plot_configs:
368
  panel_vis_updates.append(gr.update(visible=True))
369
  explore_btn_updates.append(gr.update(value=EXPLORE_ICON))
370
+ bomb_btn_icon_upds.append(gr.update()) # No change if not active
371
+ formula_btn_icon_upds.append(gr.update()) # No change if not active
372
+ else:
 
 
373
  new_explored_id_to_set = plot_id_clicked
374
+ logging.info(f"Esplorazione: {plot_id_clicked}. Altri grafici/titoli nascosti.")
375
+ for i, sec_name in enumerate(unique_ordered_sections):
376
+ section_title_vis_updates[i] = gr.update(visible=(sec_name == section_of_clicked_plot))
377
  for cfg in plot_configs:
378
+ is_target = (cfg["id"] == new_explored_id_to_set)
379
+ panel_vis_updates.append(gr.update(visible=is_target))
380
+ explore_btn_updates.append(gr.update(value=ACTIVE_ICON if is_target else EXPLORE_ICON))
381
+ if current_active_panel_action_state: # Close insight/formula if explore is activated
382
+ logging.info("Chiusura pannello azioni per attivazione esplorazione.")
 
 
 
383
  action_col_update = gr.update(visible=False)
384
  new_active_panel_action_state_update = None
 
385
  for _ in plot_configs:
386
  bomb_btn_icon_upds.append(gr.update(value=BOMB_ICON))
387
  formula_btn_icon_upds.append(gr.update(value=FORMULA_ICON))
388
+ else:
389
+ for _ in plot_configs: bomb_btn_icon_upds.append(gr.update()); formula_btn_icon_upds.append(gr.update())
 
 
390
 
391
+ return [new_explored_id_to_set, action_col_update, new_active_panel_action_state_update] + \
392
+ panel_vis_updates + explore_btn_updates + bomb_btn_icon_upds + formula_btn_icon_upds + section_title_vis_updates
 
 
 
 
 
 
 
 
393
 
394
  # --- Define Output Lists for Event Handlers ---
395
+ # Base UI elements for action panel
396
+ _base_action_panel_ui_outputs = [
397
+ global_actions_column_ui, insights_chatbot_ui, insights_chatbot_ui,
398
+ insights_chat_input_ui, insights_suggestions_row_ui,
399
+ insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
400
+ formula_display_markdown_ui, formula_display_markdown_ui
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  ]
402
+ _action_panel_state_outputs = [active_panel_action_state, current_chat_plot_id_st, chat_histories_st, explored_plot_id_state]
403
+
404
+ action_panel_outputs_list = _base_action_panel_ui_outputs + _action_panel_state_outputs
405
+ action_panel_outputs_list.extend([plot_ui_objects[pid]["panel_component"] if pid in plot_ui_objects else gr.update() for pid in [pc["id"] for pc in plot_configs]]) # Panel vis
406
+ action_panel_outputs_list.extend([plot_ui_objects[pid]["bomb_button"] if pid in plot_ui_objects else gr.update() for pid in [pc["id"] for pc in plot_configs]]) # Bomb icon
407
+ action_panel_outputs_list.extend([plot_ui_objects[pid]["formula_button"] if pid in plot_ui_objects else gr.update() for pid in [pc["id"] for pc in plot_configs]]) # Formula icon
408
+ action_panel_outputs_list.extend([plot_ui_objects[pid]["explore_button"] if pid in plot_ui_objects else gr.update() for pid in [pc["id"] for pc in plot_configs]]) # Explore icon
409
+ action_panel_outputs_list.extend([section_titles_map[s_name] if s_name in section_titles_map else gr.update() for s_name in unique_ordered_sections]) # Section Titles
410
+
411
+ _explore_state_outputs = [explored_plot_id_state, global_actions_column_ui, active_panel_action_state]
412
+ explore_outputs_list = _explore_state_outputs
413
+ explore_outputs_list.extend([plot_ui_objects[pid]["panel_component"] if pid in plot_ui_objects else gr.update() for pid in [pc["id"] for pc in plot_configs]]) # Panel vis
414
+ explore_outputs_list.extend([plot_ui_objects[pid]["explore_button"] if pid in plot_ui_objects else gr.update() for pid in [pc["id"] for pc in plot_configs]]) # Explore icon
415
+ explore_outputs_list.extend([plot_ui_objects[pid]["bomb_button"] if pid in plot_ui_objects else gr.update() for pid in [pc["id"] for pc in plot_configs]]) # Bomb icon
416
+ explore_outputs_list.extend([plot_ui_objects[pid]["formula_button"] if pid in plot_ui_objects else gr.update() for pid in [pc["id"] for pc in plot_configs]]) # Formula icon
417
+ explore_outputs_list.extend([section_titles_map[s_name] if s_name in section_titles_map else gr.update() for s_name in unique_ordered_sections]) # Section Titles
418
+
419
+ action_click_inputs = [active_panel_action_state, chat_histories_st, current_chat_plot_id_st, plot_data_for_chatbot_st, explored_plot_id_state]
420
+ explore_click_inputs = [explored_plot_id_state, active_panel_action_state]
421
 
 
422
  def create_panel_action_handler(p_id, action_type_str):
423
+ async def _handler(curr_active_val, curr_chats_val, curr_chat_pid, curr_plot_data, curr_explored_id):
424
+ return await handle_panel_action(p_id, action_type_str, curr_active_val, curr_chats_val, curr_chat_pid, curr_plot_data, curr_explored_id)
 
 
 
425
  return _handler
426
 
427
  for config_item in plot_configs:
428
  plot_id = config_item["id"]
429
  if plot_id in plot_ui_objects:
430
  ui_obj = plot_ui_objects[plot_id]
431
+ ui_obj["bomb_button"].click(fn=create_panel_action_handler(plot_id, "insights"), inputs=action_click_inputs, outputs=action_panel_outputs_list, api_name=f"action_insights_{plot_id}")
432
+ 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}")
433
+ ui_obj["explore_button"].click(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), inputs=explore_click_inputs, outputs=explore_outputs_list, api_name=f"action_explore_{plot_id}")
434
+ else: logging.warning(f"UI object for plot_id '{plot_id}' not found for click handlers.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
 
436
  chat_submission_outputs = [insights_chatbot_ui, insights_chat_input_ui, chat_histories_st]
437
  chat_submission_inputs = [insights_chat_input_ui, current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st]
438
+ insights_chat_input_ui.submit(fn=handle_chat_message_submission, inputs=chat_submission_inputs, outputs=chat_submission_outputs, api_name="submit_chat_message")
 
 
 
 
 
 
 
439
  suggestion_click_inputs = [current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st]
440
+ insights_suggestion_1_btn.click(fn=handle_suggested_question_click, inputs=[insights_suggestion_1_btn] + suggestion_click_inputs, outputs=chat_submission_outputs, api_name="click_suggestion_1")
441
+ insights_suggestion_2_btn.click(fn=handle_suggested_question_click, inputs=[insights_suggestion_2_btn] + suggestion_click_inputs, outputs=chat_submission_outputs, api_name="click_suggestion_2")
442
+ insights_suggestion_3_btn.click(fn=handle_suggested_question_click, inputs=[insights_suggestion_3_btn] + suggestion_click_inputs, outputs=chat_submission_outputs, api_name="click_suggestion_3")
 
 
 
 
 
 
 
 
 
 
 
 
443
 
444
  def refresh_all_analytics_ui_elements(current_token_state, date_filter_val, custom_start_val, custom_end_val, current_chat_histories):
445
+ logging.info("Refreshing all analytics UI elements and resetting actions/chat.")
446
+ plot_generation_results = update_analytics_plots_figures(current_token_state, date_filter_val, custom_start_val, custom_end_val, plot_configs)
447
+ status_msg, generated_figs, new_plot_data_summaries = plot_generation_results[0], plot_generation_results[1:-1], plot_generation_results[-1]
 
 
 
 
 
 
 
 
 
 
 
 
448
 
449
+ all_updates = [status_msg]
450
+ all_updates.extend(generated_figs if len(generated_figs) == len(plot_configs) else [create_placeholder_plot("Error", f"Fig missing {i}") for i in range(len(plot_configs))])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
+ all_updates.extend([ # Reset action panel UI
453
+ gr.update(visible=False), gr.update(value=[], visible=False), gr.update(value="", visible=False),
454
+ gr.update(visible=False), gr.update(value="S1"), gr.update(value="S2"), gr.update(value="S3"),
455
+ gr.update(value="Formula details here.", visible=False),
456
+ None, None, {}, new_plot_data_summaries # Reset states
457
+ ])
458
+ for _ in plot_configs: # Reset button icons & panel visibility
459
+ all_updates.extend([gr.update(value=BOMB_ICON), gr.update(value=FORMULA_ICON), gr.update(value=EXPLORE_ICON), gr.update(visible=True)])
460
+ all_updates.append(None) # Reset explored_plot_id_state
461
+ all_updates.extend([gr.update(visible=True)] * num_unique_sections) # Make all section titles visible
462
+ logging.info(f"Prepared {len(all_updates)} updates for analytics refresh.")
463
  return all_updates
464
 
465
+ apply_filter_and_sync_outputs_list = [analytics_status_md] # Status
466
+ apply_filter_and_sync_outputs_list.extend([plot_ui_objects[pc["id"]]["plot_component"] if pc["id"] in plot_ui_objects and "plot_component" in plot_ui_objects[pc["id"]] else gr.update() for pc in plot_configs]) # Plot figures
467
+ apply_filter_and_sync_outputs_list.extend([ # Action panel UI reset
 
 
 
468
  global_actions_column_ui, insights_chatbot_ui, insights_chat_input_ui,
469
  insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
470
  formula_display_markdown_ui,
471
+ active_panel_action_state, current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st
 
472
  ])
473
+ for pc in plot_configs: # Button icons and panel visibility
474
+ pid = pc["id"]
475
+ apply_filter_and_sync_outputs_list.extend([
476
+ plot_ui_objects[pid]["bomb_button"] if pid in plot_ui_objects else gr.update(),
477
+ plot_ui_objects[pid]["formula_button"] if pid in plot_ui_objects else gr.update(),
478
+ plot_ui_objects[pid]["explore_button"] if pid in plot_ui_objects else gr.update(),
479
+ plot_ui_objects[pid]["panel_component"] if pid in plot_ui_objects else gr.update()
480
+ ])
481
  apply_filter_and_sync_outputs_list.append(explored_plot_id_state) # Reset explored state
482
+ apply_filter_and_sync_outputs_list.extend([section_titles_map[s_name] if s_name in section_titles_map else gr.update() for s_name in unique_ordered_sections]) # Section Titles
483
 
484
  apply_filter_btn.click(
485
  fn=refresh_all_analytics_ui_elements,
486
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
487
+ outputs=apply_filter_and_sync_outputs_list, show_progress="full"
 
488
  )
489
 
490
  with gr.TabItem("3️⃣ Menzioni", id="tab_mentions"):
491
+ # ... (rest of the tab unchanged) ...
492
  refresh_mentions_display_btn = gr.Button("🔄 Aggiorna Visualizzazione Menzioni", variant="secondary")
493
  mentions_html = gr.HTML("Dati menzioni...")
494
  mentions_sentiment_dist_plot = gr.Plot(label="Distribuzione Sentiment Menzioni")
 
499
  )
500
 
501
  with gr.TabItem("4️⃣ Statistiche Follower", id="tab_follower_stats"):
502
+ # ... (rest of the tab unchanged) ...
503
  refresh_follower_stats_btn = gr.Button("🔄 Aggiorna Visualizzazione Statistiche Follower", variant="secondary")
504
  follower_stats_html = gr.HTML("Statistiche follower...")
505
  with gr.Row():
 
514
  show_progress="full"
515
  )
516
 
517
+ 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")
518
+ 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)
519
+ sync_event_part3 = sync_event_part2.then(fn=display_main_dashboard, inputs=[token_state], outputs=[dashboard_display_html], show_progress=False)
520
+ sync_event_final = sync_event_part3.then(fn=refresh_all_analytics_ui_elements, inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st], outputs=apply_filter_and_sync_outputs_list, show_progress="full")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
 
522
  if __name__ == "__main__":
523
+ if not os.environ.get(LINKEDIN_CLIENT_ID_ENV_VAR): logging.warning(f"ATTENZIONE: '{LINKEDIN_CLIENT_ID_ENV_VAR}' non impostata.")
524
+ 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]):
525
+ logging.warning("ATTENZIONE: Variabili Bubble non impostate.")
526
+ try: logging.info(f"Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
527
+ except ImportError: logging.warning("Matplotlib non trovato.")
 
 
 
 
 
 
 
528
  app.launch(server_name="0.0.0.0", server_port=7860, debug=True)