GuglielmoTor commited on
Commit
6277fe0
·
verified ·
1 Parent(s): bfed44d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +328 -296
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py
2
  # (Showing relevant parts that need modification)
3
  import gradio as gr
4
  import pandas as pd
@@ -24,11 +24,12 @@ 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
31
- from formulas import PLOT_FORMULAS
32
 
33
  # --- NEW CHATBOT MODULE IMPORTS ---
34
  from chatbot_prompts import get_initial_insight_prompt_and_suggestions # MODIFIED IMPORT
@@ -41,7 +42,7 @@ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(
41
 
42
  # --- Gradio UI Blocks ---
43
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
44
- title="LinkedIn Organization Dashboard") as app:
45
  token_state = gr.State(value={
46
  "token": None, "client_id": None, "org_urn": None,
47
  "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
@@ -51,16 +52,16 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
51
  "config_date_col_followers": "date", "config_media_type_col": "media_type",
52
  "config_eb_labels_col": "li_eb_label"
53
  })
54
-
55
- chat_histories_st = gr.State({})
56
- current_chat_plot_id_st = gr.State(None)
57
- plot_data_for_chatbot_st = gr.State({}) # NEW: Store data summaries for chatbot
58
 
59
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
60
  url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
61
  status_box = gr.Textbox(label="Stato Generale Token LinkedIn", interactive=False, value="Inizializzazione...")
62
  org_urn_display = gr.Textbox(label="URN Organizzazione (Nascosto)", interactive=False, visible=False)
63
-
64
  app.load(fn=get_url_user_token, inputs=None, outputs=[url_user_token_display, org_urn_display], api_name="get_url_params", show_progress=False)
65
 
66
  def initial_load_sequence(url_token, org_urn_val, current_state):
@@ -74,7 +75,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
74
  sync_data_btn = gr.Button("🔄 Sincronizza Dati LinkedIn", variant="primary", visible=False, interactive=False)
75
  sync_status_html_output = gr.HTML("<p style='text-align:center;'>Stato sincronizzazione...</p>")
76
  dashboard_display_html = gr.HTML("<p style='text-align:center;'>Caricamento dashboard...</p>")
77
-
78
  org_urn_display.change(
79
  fn=initial_load_sequence,
80
  inputs=[url_user_token_display, org_urn_display, token_state],
@@ -86,15 +87,15 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
86
  gr.Markdown("## 📈 Analisi Performance LinkedIn")
87
  gr.Markdown("Seleziona un intervallo di date. Clicca i pulsanti (💣 Insights, ƒ Formula, 🧭 Esplora) su un grafico per azioni.")
88
  analytics_status_md = gr.Markdown("Stato analisi...")
89
-
90
  with gr.Row():
91
  date_filter_selector = gr.Radio(
92
  ["Sempre", "Ultimi 7 Giorni", "Ultimi 30 Giorni", "Intervallo Personalizzato"],
93
  label="Seleziona Intervallo Date", value="Sempre", scale=3
94
  )
95
- with gr.Column(scale=2):
96
- custom_start_date_picker = gr.DateTime(label="Data Inizio", visible=False, include_time=False, type="datetime") # Use gr.DateTime
97
- custom_end_date_picker = gr.DateTime(label="Data Fine", visible=False, include_time=False, type="datetime") # Use gr.DateTime
98
 
99
  apply_filter_btn = gr.Button("🔍 Applica Filtro & Aggiorna Analisi", variant="primary")
100
 
@@ -107,7 +108,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
107
  inputs=[date_filter_selector],
108
  outputs=[custom_start_date_picker, custom_end_date_picker]
109
  )
110
-
111
  plot_configs = [
112
  {"label": "Numero di Follower nel Tempo", "id": "followers_count", "section": "Dinamiche dei Follower"},
113
  {"label": "Tasso di Crescita Follower", "id": "followers_growth_rate", "section": "Dinamiche dei Follower"},
@@ -131,130 +132,163 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
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")
145
-
146
  insights_chatbot_ui = gr.Chatbot(
147
- label="Chat Insights", type="messages", height=450,
148
  bubble_full_width=False, visible=False, show_label=False,
149
  placeholder="L'analisi AI del grafico apparirà qui. Fai domande di approfondimento!"
150
  )
151
  insights_chat_input_ui = gr.Textbox(
152
- label="La tua domanda:", placeholder="Chiedi all'AI riguardo a questo grafico...",
153
  lines=2, visible=False, show_label=False
154
  )
155
  with gr.Row(visible=False) as insights_suggestions_row_ui:
156
  insights_suggestion_1_btn = gr.Button(value="Suggerimento 1", size="sm", min_width=50)
157
  insights_suggestion_2_btn = gr.Button(value="Suggerimento 2", size="sm", min_width=50)
158
  insights_suggestion_3_btn = gr.Button(value="Suggerimento 3", size="sm", min_width=50)
159
-
160
  formula_display_markdown_ui = gr.Markdown(
161
  "I dettagli sulla formula/metodologia appariranno qui.", visible=False
162
  )
163
 
164
- async def handle_panel_action(
165
- plot_id_clicked: str,
166
- action_type: str,
167
- current_active_action_from_state: dict,
168
- current_chat_histories: dict,
169
  current_chat_plot_id: str,
170
- current_plot_data_for_chatbot: dict # NEW: data summaries
 
171
  ):
172
- logging.info(f"Azione '{action_type}' per grafico: {plot_id_clicked}. Attualmente attivo: {current_active_action_from_state}")
173
-
174
  clicked_plot_config = next((p for p in plot_configs if p["id"] == plot_id_clicked), None)
175
  if not clicked_plot_config:
176
  logging.error(f"Configurazione non trovata per plot_id {plot_id_clicked}")
177
- num_button_updates = 2 * len(plot_configs) # insights, formula buttons
178
- error_updates = [gr.update(visible=False)] * 7 # action_col, chatbot, input, suggestions_row, 3x sugg_btn
179
- error_updates.append(gr.update(visible=False, value="")) # formula_md (visibility and value)
180
- error_updates.extend([current_active_action_from_state, current_chat_plot_id, current_chat_histories])
181
- error_updates.extend([gr.update()] * num_button_updates)
182
- return error_updates
 
 
 
 
 
 
183
 
184
  clicked_plot_label = clicked_plot_config["label"]
185
-
186
  hypothetical_new_active_state = {"plot_id": plot_id_clicked, "type": action_type}
187
  is_toggling_off = current_active_action_from_state == hypothetical_new_active_state
188
-
189
- new_active_action_state_to_set = None
190
- action_col_visible_update = gr.update(visible=True)
191
-
192
  insights_chatbot_visible_update = gr.update(visible=False)
193
  insights_chat_input_visible_update = gr.update(visible=False)
194
  insights_suggestions_row_visible_update = gr.update(visible=False)
195
  formula_display_visible_update = gr.update(visible=False)
 
 
 
196
 
197
- chatbot_content_update = gr.update() # No change by default
198
- suggestion_1_update = gr.update()
199
- suggestion_2_update = gr.update()
200
- suggestion_3_update = gr.update()
201
- new_current_chat_plot_id = current_chat_plot_id
202
- updated_chat_histories = current_chat_histories
 
 
203
 
204
- formula_content_update = gr.update()
205
 
206
  if is_toggling_off:
207
  new_active_action_state_to_set = None
208
  action_col_visible_update = gr.update(visible=False)
209
- new_current_chat_plot_id = None
210
- logging.info(f"Chiusura pannello {action_type} per {plot_id_clicked}")
211
- else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  new_active_action_state_to_set = hypothetical_new_active_state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  if action_type == "insights":
214
  insights_chatbot_visible_update = gr.update(visible=True)
215
  insights_chat_input_visible_update = gr.update(visible=True)
216
  insights_suggestions_row_visible_update = gr.update(visible=True)
217
-
218
  new_current_chat_plot_id = plot_id_clicked
219
  chat_history_for_this_plot = current_chat_histories.get(plot_id_clicked, [])
220
-
221
  plot_specific_data_summary = current_plot_data_for_chatbot.get(plot_id_clicked, f"Nessun sommario dati specifico disponibile per '{clicked_plot_label}'.")
222
 
223
- if not chat_history_for_this_plot:
224
  initial_llm_prompt, suggestions = get_initial_insight_prompt_and_suggestions(
225
- plot_id_clicked,
226
- clicked_plot_label,
227
- plot_specific_data_summary
228
  )
229
- # History for LLM's first turn: the system's prompt as a user message
230
  history_for_llm_first_turn = [{"role": "user", "content": initial_llm_prompt}]
231
-
232
- logging.info(f"Generating initial LLM insight for {plot_id_clicked}...")
233
  initial_bot_response_text = await generate_llm_response(
234
- user_message=initial_llm_prompt, # For context/logging in handler
235
- plot_id=plot_id_clicked,
236
- plot_label=clicked_plot_label,
237
- chat_history_for_plot=history_for_llm_first_turn,
238
- plot_data_summary=plot_specific_data_summary
239
  )
240
- logging.info(f"LLM initial insight received for {plot_id_clicked}.")
241
-
242
- # History for Gradio display starts with the assistant's response
243
  chat_history_for_this_plot = [{"role": "assistant", "content": initial_bot_response_text}]
244
  updated_chat_histories = current_chat_histories.copy()
245
  updated_chat_histories[plot_id_clicked] = chat_history_for_this_plot
246
- else: # History exists, get fresh suggestions
247
  _, suggestions = get_initial_insight_prompt_and_suggestions(
248
- plot_id_clicked,
249
- clicked_plot_label,
250
- plot_specific_data_summary
251
  )
252
-
253
  chatbot_content_update = gr.update(value=chat_history_for_this_plot)
254
- suggestion_1_update = gr.update(value=suggestions[0])
255
- suggestion_2_update = gr.update(value=suggestions[1])
256
- suggestion_3_update = gr.update(value=suggestions[2])
257
- logging.info(f"Apertura pannello CHAT per {plot_id_clicked} ('{clicked_plot_label}')")
258
 
259
  elif action_type == "formula":
260
  formula_display_visible_update = gr.update(visible=True)
@@ -262,187 +296,211 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
262
  formula_text = f"**Formula/Metodologia per: {clicked_plot_label}**\n\nID Grafico: `{plot_id_clicked}`.\n\n"
263
  if formula_key and formula_key in PLOT_FORMULAS:
264
  formula_data = PLOT_FORMULAS[formula_key]
265
- formula_text += f"### {formula_data['title']}\n\n"
266
- formula_text += f"**Descrizione:**\n{formula_data['description']}\n\n"
267
- formula_text += "**Come viene calcolato:**\n"
268
- for step in formula_data['calculation_steps']:
269
- formula_text += f"- {step}\n"
270
  else:
271
- formula_text += "(Nessuna informazione dettagliata sulla formula trovata per questo ID grafico in `formulas.py`)"
272
  formula_content_update = gr.update(value=formula_text)
273
- new_current_chat_plot_id = None
274
- logging.info(f"Apertura pannello FORMULA per {plot_id_clicked} (mappato a {formula_key})")
275
-
276
- all_button_icon_updates = []
277
- for cfg_item in plot_configs:
278
- p_id_iter = cfg_item["id"]
279
- # Update insights button icon
280
- if new_active_action_state_to_set == {"plot_id": p_id_iter, "type": "insights"}:
281
- all_button_icon_updates.append(gr.update(value=ACTIVE_ICON))
282
- else:
283
- all_button_icon_updates.append(gr.update(value=BOMB_ICON))
284
- # Update formula button icon
285
- if new_active_action_state_to_set == {"plot_id": p_id_iter, "type": "formula"}:
286
- all_button_icon_updates.append(gr.update(value=ACTIVE_ICON))
287
- else:
288
- all_button_icon_updates.append(gr.update(value=FORMULA_ICON))
289
-
290
  final_updates = [
291
  action_col_visible_update,
292
  insights_chatbot_visible_update, chatbot_content_update,
293
  insights_chat_input_visible_update,
294
  insights_suggestions_row_visible_update, suggestion_1_update, suggestion_2_update, suggestion_3_update,
295
  formula_display_visible_update, formula_content_update,
296
- new_active_action_state_to_set,
297
- new_current_chat_plot_id,
298
- updated_chat_histories
299
- ] + all_button_icon_updates
 
 
 
 
300
 
301
  return final_updates
302
 
303
  async def handle_chat_message_submission(
304
- user_message: str,
305
- current_plot_id: str,
306
- chat_histories: dict,
307
- current_plot_data_for_chatbot: dict # NEW: data summaries
308
  ):
309
  if not current_plot_id or not user_message.strip():
310
  history_for_plot = chat_histories.get(current_plot_id, [])
311
- # Yield current state if no action needed
312
- yield history_for_plot, gr.update(value=""), chat_histories # Clear input, return current history
313
  return
314
 
315
  plot_config = next((p for p in plot_configs if p["id"] == current_plot_id), None)
316
  plot_label = plot_config["label"] if plot_config else "Grafico Selezionato"
317
-
318
- # Retrieve the specific data summary for the current plot
319
  plot_specific_data_summary = current_plot_data_for_chatbot.get(current_plot_id, f"Nessun sommario dati specifico disponibile per '{plot_label}'.")
320
-
321
  history_for_plot = chat_histories.get(current_plot_id, []).copy()
322
  history_for_plot.append({"role": "user", "content": user_message})
323
-
324
- # Update UI immediately with user message
325
- yield history_for_plot, gr.update(value=""), chat_histories # Clear input
326
 
327
- # Pass the data summary to the LLM along with the history
328
  bot_response_text = await generate_llm_response(
329
- user_message,
330
- current_plot_id,
331
- plot_label,
332
- history_for_plot, # This history now includes the user message
333
- plot_specific_data_summary # Explicitly pass for this turn if needed by LLM handler logic
334
  )
335
-
336
  history_for_plot.append({"role": "assistant", "content": bot_response_text})
337
-
338
  updated_chat_histories = chat_histories.copy()
339
  updated_chat_histories[current_plot_id] = history_for_plot
340
-
341
  yield history_for_plot, "", updated_chat_histories
342
 
343
-
344
  async def handle_suggested_question_click(
345
  suggestion_text: str,
346
- current_plot_id: str,
347
- chat_histories: dict,
348
- current_plot_data_for_chatbot: dict # NEW: data summaries
349
  ):
350
  if not current_plot_id or not suggestion_text.strip():
351
  history_for_plot = chat_histories.get(current_plot_id, [])
352
- yield history_for_plot, gr.update(value=""), chat_histories
353
  return
354
-
355
- # This is essentially the same as submitting a message, so reuse logic
356
- # The suggestion_text becomes the user_message
357
  async for update in handle_chat_message_submission(
358
- suggestion_text,
359
- current_plot_id,
360
- chat_histories,
361
- current_plot_data_for_chatbot
362
  ):
363
  yield update
364
 
365
-
366
- def handle_explore_click(plot_id_clicked, current_explored_plot_id_from_state):
367
- logging.info(f"Click su Esplora per: {plot_id_clicked}. Attualmente esplorato da stato: {current_explored_plot_id_from_state}")
 
 
 
368
  if not plot_ui_objects:
369
  logging.error("plot_ui_objects non popolato durante handle_explore_click.")
370
- updates_for_missing_ui = [current_explored_plot_id_from_state]
371
- for _ in plot_configs: # panel_component, explore_button
372
- updates_for_missing_ui.extend([gr.update(), gr.update()])
373
- return updates_for_missing_ui
374
-
 
 
375
  new_explored_id_to_set = None
376
- is_toggling_off = (plot_id_clicked == current_explored_plot_id_from_state)
 
 
 
377
 
378
- if is_toggling_off:
 
 
 
 
 
379
  new_explored_id_to_set = None
380
- logging.info(f"Interruzione esplorazione grafico: {plot_id_clicked}")
381
- else:
 
 
 
 
 
 
 
 
382
  new_explored_id_to_set = plot_id_clicked
383
- logging.info(f"Esplorazione grafico: {plot_id_clicked}")
384
-
385
- panel_and_button_updates = []
386
- for cfg in plot_configs:
387
- p_id = cfg["id"]
388
- if p_id in plot_ui_objects:
389
- panel_visible = not new_explored_id_to_set or (p_id == new_explored_id_to_set)
390
- panel_and_button_updates.append(gr.update(visible=panel_visible))
391
-
392
- if p_id == new_explored_id_to_set:
393
- panel_and_button_updates.append(gr.update(value=ACTIVE_ICON))
394
- else:
395
- panel_and_button_updates.append(gr.update(value=EXPLORE_ICON))
396
- else:
397
- panel_and_button_updates.extend([gr.update(), gr.update()])
398
-
399
- final_updates = [new_explored_id_to_set] + panel_and_button_updates
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  return final_updates
401
-
402
- # Outputs for panel actions
403
  action_panel_outputs_list = [
404
- global_actions_column_ui,
405
- insights_chatbot_ui, insights_chatbot_ui, # Target chatbot UI for visibility and value
406
- insights_chat_input_ui,
407
- insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
408
- formula_display_markdown_ui, formula_display_markdown_ui, # Target markdown for visibility and value
409
- active_panel_action_state,
410
  current_chat_plot_id_st,
411
- chat_histories_st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  ]
413
- for cfg_item_action in plot_configs:
414
- pid_action = cfg_item_action["id"]
415
- if pid_action in plot_ui_objects:
416
- action_panel_outputs_list.append(plot_ui_objects[pid_action]["bomb_button"])
417
- action_panel_outputs_list.append(plot_ui_objects[pid_action]["formula_button"])
418
- else:
419
- action_panel_outputs_list.extend([gr.update(), gr.update()]) # Use gr.update() as placeholder
420
-
421
- # Outputs for explore actions
422
- explore_buttons_outputs_list = [explored_plot_id_state]
423
- for cfg_item_explore in plot_configs:
424
- pid_explore = cfg_item_explore["id"]
425
- if pid_explore in plot_ui_objects:
426
- explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["panel_component"])
427
- explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["explore_button"])
428
- else:
429
- explore_buttons_outputs_list.extend([gr.update(), gr.update()])
430
-
431
- # Inputs for panel actions
432
  action_click_inputs = [
433
- active_panel_action_state,
434
  chat_histories_st,
435
  current_chat_plot_id_st,
436
- plot_data_for_chatbot_st # NEW: pass data summaries state
 
 
 
 
 
437
  ]
438
- # Inputs for explore actions
439
- explore_click_inputs = [explored_plot_id_state]
440
 
 
441
  def create_panel_action_handler(p_id, action_type_str):
442
- async def _handler(current_active_val, current_chats_val, current_chat_pid, current_plot_data_summaries): # Add summaries
443
  logging.debug(f"Entering _handler for plot_id: {p_id}, action: {action_type_str}")
444
- result = await handle_panel_action(p_id, action_type_str, current_active_val, current_chats_val, current_chat_pid, current_plot_data_summaries) # Pass summaries
445
- logging.debug(f"_handler for plot_id: {p_id}, action: {action_type_str} completed.")
446
  return result
447
  return _handler
448
 
@@ -450,143 +508,117 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
450
  plot_id = config_item["id"]
451
  if plot_id in plot_ui_objects:
452
  ui_obj = plot_ui_objects[plot_id]
453
-
454
  ui_obj["bomb_button"].click(
455
- fn=create_panel_action_handler(plot_id, "insights"),
456
- inputs=action_click_inputs,
457
  outputs=action_panel_outputs_list,
458
  api_name=f"action_insights_{plot_id}"
459
  )
460
  ui_obj["formula_button"].click(
461
- fn=create_panel_action_handler(plot_id, "formula"),
462
  inputs=action_click_inputs,
463
  outputs=action_panel_outputs_list,
464
  api_name=f"action_formula_{plot_id}"
465
  )
466
  ui_obj["explore_button"].click(
467
- fn=lambda current_explored_val, p_id=plot_id: handle_explore_click(p_id, current_explored_val),
468
- inputs=explore_click_inputs,
469
- outputs=explore_buttons_outputs_list,
470
  api_name=f"action_explore_{plot_id}"
471
  )
472
  else:
473
  logging.warning(f"Oggetto UI per plot_id '{plot_id}' non trovato durante il tentativo di associare i gestori di click.")
474
-
475
  chat_submission_outputs = [insights_chatbot_ui, insights_chat_input_ui, chat_histories_st]
476
- chat_submission_inputs = [insights_chat_input_ui, current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st] # Add data summaries state
477
 
478
  insights_chat_input_ui.submit(
479
  fn=handle_chat_message_submission,
480
- inputs=chat_submission_inputs,
481
  outputs=chat_submission_outputs,
482
  api_name="submit_chat_message"
483
  )
484
 
485
- suggestion_click_inputs = [current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st] # Add data summaries state
486
  insights_suggestion_1_btn.click(
487
  fn=handle_suggested_question_click,
488
- inputs=[insights_suggestion_1_btn] + suggestion_click_inputs, # Pass button value as first arg
489
- outputs=chat_submission_outputs,
490
- api_name="click_suggestion_1"
491
  )
492
  insights_suggestion_2_btn.click(
493
  fn=handle_suggested_question_click,
494
- inputs=[insights_suggestion_2_btn] + suggestion_click_inputs,
495
- outputs=chat_submission_outputs,
496
- api_name="click_suggestion_2"
497
  )
498
  insights_suggestion_3_btn.click(
499
  fn=handle_suggested_question_click,
500
- inputs=[insights_suggestion_3_btn] + suggestion_click_inputs,
501
- outputs=chat_submission_outputs,
502
- api_name="click_suggestion_3"
503
  )
504
 
505
  def refresh_all_analytics_ui_elements(current_token_state, date_filter_val, custom_start_val, custom_end_val, current_chat_histories):
506
  logging.info("Aggiornamento di tutti gli elementi UI delle analisi e reset delle azioni/chat.")
507
-
508
- # Pass plot_configs to the update function so it can be used by generate_chatbot_data_summaries
509
  plot_generation_results = update_analytics_plots_figures(
510
  current_token_state, date_filter_val, custom_start_val, custom_end_val, plot_configs
511
  )
512
  status_message_update = plot_generation_results[0]
513
- generated_plot_figures = plot_generation_results[1:-1] # All items except first (status) and last (summaries)
514
- new_plot_data_summaries = plot_generation_results[-1] # Last item is the summaries dict
515
-
516
- all_updates = [status_message_update]
517
-
518
- for i in range(len(plot_configs)):
519
  if i < len(generated_plot_figures):
520
- all_updates.append(generated_plot_figures[i])
521
- else:
 
522
  all_updates.append(create_placeholder_plot("Errore Figura", f"Figura mancante per grafico {plot_configs[i]['id']}"))
523
 
524
  all_updates.extend([
525
  gr.update(visible=False), # global_actions_column_ui
526
- gr.update(value=[], visible=False), # insights_chatbot_ui (value & visibility)
527
- gr.update(value="", visible=False), # insights_chat_input_ui (value & visibility)
528
  gr.update(visible=False), # insights_suggestions_row_ui
529
- gr.update(value="Suggerimento 1"), # insights_suggestion_1_btn (reset value, visibility handled by row)
530
- gr.update(value="Suggerimento 2"), # insights_suggestion_2_btn
531
- gr.update(value="Suggerimento 3"), # insights_suggestion_3_btn
532
  gr.update(value="I dettagli sulla formula/metodologia appariranno qui.", visible=False), # formula_display_markdown_ui
533
  None, # active_panel_action_state
534
  None, # current_chat_plot_id_st
535
- {}, # chat_histories_st (reset chat histories on filter change)
536
- new_plot_data_summaries # NEW: plot_data_for_chatbot_st
537
  ])
538
-
539
- for cfg in plot_configs:
540
  pid = cfg["id"]
541
- if pid in plot_ui_objects:
542
- all_updates.append(gr.update(value=BOMB_ICON))
543
- all_updates.append(gr.update(value=FORMULA_ICON))
544
- all_updates.append(gr.update(value=EXPLORE_ICON))
545
- all_updates.append(gr.update(visible=True)) # panel_component visibility
546
- else:
547
- all_updates.extend([gr.update(), gr.update(), gr.update(), gr.update()])
548
-
549
- all_updates.append(None) # explored_plot_id_state
550
 
 
551
  logging.info(f"Preparati {len(all_updates)} aggiornamenti per il refresh delle analisi.")
552
  return all_updates
553
 
554
- apply_filter_and_sync_outputs_list = [analytics_status_md]
555
- for config_item_filter_sync in plot_configs:
556
- pid_filter_sync = config_item_filter_sync["id"]
557
- if pid_filter_sync in plot_ui_objects and "plot_component" in plot_ui_objects[pid_filter_sync]:
558
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync]["plot_component"])
559
- else:
560
- apply_filter_and_sync_outputs_list.append(gr.update())
561
 
562
- apply_filter_and_sync_outputs_list.extend([
563
- global_actions_column_ui, # Reset visibility
564
- insights_chatbot_ui, # Reset content & visibility
565
- insights_chat_input_ui, # Reset content & visibility
566
- insights_suggestions_row_ui, # Reset visibility
567
- insights_suggestion_1_btn, # Reset text & visibility
568
- insights_suggestion_2_btn,
569
- insights_suggestion_3_btn,
570
- formula_display_markdown_ui, # Reset content & visibility
571
- active_panel_action_state, # Reset state
572
- current_chat_plot_id_st, # Reset state
573
- chat_histories_st, # Preserve or reset state (resetting via refresh_all_analytics_ui_elements)
574
- plot_data_for_chatbot_st # NEW: Update this state
575
  ])
576
-
577
- for cfg_filter_sync_btns in plot_configs:
578
- pid_filter_sync_btns = cfg_filter_sync_btns["id"]
579
- if pid_filter_sync_btns in plot_ui_objects:
580
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["bomb_button"])
581
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["formula_button"])
582
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["explore_button"])
583
- apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["panel_component"])
584
- else:
585
- apply_filter_and_sync_outputs_list.extend([gr.update(), gr.update(), gr.update(), gr.update()])
586
 
587
- apply_filter_and_sync_outputs_list.append(explored_plot_id_state) # Reset state
588
-
589
- logging.info(f"Output totali definiti per apply_filter/sync: {len(apply_filter_and_sync_outputs_list)}")
590
 
591
  apply_filter_btn.click(
592
  fn=refresh_all_analytics_ui_elements,
@@ -608,7 +640,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
608
  with gr.TabItem("4️⃣ Statistiche Follower", id="tab_follower_stats"):
609
  refresh_follower_stats_btn = gr.Button("🔄 Aggiorna Visualizzazione Statistiche Follower", variant="secondary")
610
  follower_stats_html = gr.HTML("Statistiche follower...")
611
- with gr.Row():
612
  fs_plot_monthly_gains = gr.Plot(label="Guadagni Mensili Follower")
613
  with gr.Row():
614
  fs_plot_seniority = gr.Plot(label="Follower per Anzianità (Top 10 Organici)")
@@ -624,19 +656,19 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
624
  fn=sync_all_linkedin_data_orchestrator,
625
  inputs=[token_state], outputs=[sync_status_html_output, token_state], show_progress="full"
626
  )
627
- sync_event_part2 = sync_event_part1.then(
628
- fn=process_and_store_bubble_token,
629
  inputs=[url_user_token_display, org_urn_display, token_state],
630
- outputs=[status_box, token_state, sync_data_btn], show_progress=False
631
  )
632
  sync_event_part3 = sync_event_part2.then(
633
- fn=display_main_dashboard,
634
  inputs=[token_state], outputs=[dashboard_display_html], show_progress=False
635
  )
636
  sync_event_final = sync_event_part3.then(
637
- fn=refresh_all_analytics_ui_elements, # This will now also update chatbot data summaries
638
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
639
- outputs=apply_filter_and_sync_outputs_list,
640
  show_progress="full"
641
  )
642
 
@@ -650,7 +682,7 @@ if __name__ == "__main__":
650
 
651
  try:
652
  logging.info(f"Versione Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
653
- except ImportError:
654
  logging.warning("Matplotlib non trovato direttamente, ma potrebbe essere usato dai generatori di grafici.")
655
 
656
- app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
 
1
+ # app.py
2
  # (Showing relevant parts that need modification)
3
  import gradio as gr
4
  import pandas as pd
 
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
+ create_placeholder_plot # Assuming you have this or similar for error cases
30
  )
31
  from analytics_plot_generator import update_analytics_plots_figures
32
+ from formulas import PLOT_FORMULAS
33
 
34
  # --- NEW CHATBOT MODULE IMPORTS ---
35
  from chatbot_prompts import get_initial_insight_prompt_and_suggestions # MODIFIED IMPORT
 
42
 
43
  # --- Gradio UI Blocks ---
44
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
45
+ title="LinkedIn Organization Dashboard") as app:
46
  token_state = gr.State(value={
47
  "token": None, "client_id": None, "org_urn": None,
48
  "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
 
52
  "config_date_col_followers": "date", "config_media_type_col": "media_type",
53
  "config_eb_labels_col": "li_eb_label"
54
  })
55
+
56
+ chat_histories_st = gr.State({})
57
+ current_chat_plot_id_st = gr.State(None)
58
+ plot_data_for_chatbot_st = gr.State({})
59
 
60
  gr.Markdown("# 🚀 LinkedIn Organization Dashboard")
61
  url_user_token_display = gr.Textbox(label="User Token (Nascosto)", interactive=False, visible=False)
62
  status_box = gr.Textbox(label="Stato Generale Token LinkedIn", interactive=False, value="Inizializzazione...")
63
  org_urn_display = gr.Textbox(label="URN Organizzazione (Nascosto)", interactive=False, visible=False)
64
+
65
  app.load(fn=get_url_user_token, inputs=None, outputs=[url_user_token_display, org_urn_display], api_name="get_url_params", show_progress=False)
66
 
67
  def initial_load_sequence(url_token, org_urn_val, current_state):
 
75
  sync_data_btn = gr.Button("🔄 Sincronizza Dati LinkedIn", variant="primary", visible=False, interactive=False)
76
  sync_status_html_output = gr.HTML("<p style='text-align:center;'>Stato sincronizzazione...</p>")
77
  dashboard_display_html = gr.HTML("<p style='text-align:center;'>Caricamento dashboard...</p>")
78
+
79
  org_urn_display.change(
80
  fn=initial_load_sequence,
81
  inputs=[url_user_token_display, org_urn_display, token_state],
 
87
  gr.Markdown("## 📈 Analisi Performance LinkedIn")
88
  gr.Markdown("Seleziona un intervallo di date. Clicca i pulsanti (💣 Insights, ƒ Formula, 🧭 Esplora) su un grafico per azioni.")
89
  analytics_status_md = gr.Markdown("Stato analisi...")
90
+
91
  with gr.Row():
92
  date_filter_selector = gr.Radio(
93
  ["Sempre", "Ultimi 7 Giorni", "Ultimi 30 Giorni", "Intervallo Personalizzato"],
94
  label="Seleziona Intervallo Date", value="Sempre", scale=3
95
  )
96
+ with gr.Column(scale=2):
97
+ custom_start_date_picker = gr.DateTime(label="Data Inizio", visible=False, include_time=False, type="datetime")
98
+ custom_end_date_picker = gr.DateTime(label="Data Fine", visible=False, include_time=False, type="datetime")
99
 
100
  apply_filter_btn = gr.Button("🔍 Applica Filtro & Aggiorna Analisi", variant="primary")
101
 
 
108
  inputs=[date_filter_selector],
109
  outputs=[custom_start_date_picker, custom_end_date_picker]
110
  )
111
+
112
  plot_configs = [
113
  {"label": "Numero di Follower nel Tempo", "id": "followers_count", "section": "Dinamiche dei Follower"},
114
  {"label": "Tasso di Crescita Follower", "id": "followers_growth_rate", "section": "Dinamiche dei Follower"},
 
132
  ]
133
  assert len(plot_configs) == 19, "Mancata corrispondenza in plot_configs e grafici attesi."
134
 
135
+ active_panel_action_state = gr.State(None)
136
+ explored_plot_id_state = gr.State(None)
137
+
138
+ plot_ui_objects = {}
139
 
140
  with gr.Row(equal_height=False):
141
  with gr.Column(scale=8) as plots_area_col:
142
  plot_ui_objects = build_analytics_tab_plot_area(plot_configs)
143
+
144
+ with gr.Column(scale=4, visible=False) as global_actions_column_ui:
145
+ gr.Markdown("### 💡 Azioni Contestuali Grafico")
 
146
  insights_chatbot_ui = gr.Chatbot(
147
+ label="Chat Insights", type="messages", height=450,
148
  bubble_full_width=False, visible=False, show_label=False,
149
  placeholder="L'analisi AI del grafico apparirà qui. Fai domande di approfondimento!"
150
  )
151
  insights_chat_input_ui = gr.Textbox(
152
+ label="La tua domanda:", placeholder="Chiedi all'AI riguardo a questo grafico...",
153
  lines=2, visible=False, show_label=False
154
  )
155
  with gr.Row(visible=False) as insights_suggestions_row_ui:
156
  insights_suggestion_1_btn = gr.Button(value="Suggerimento 1", size="sm", min_width=50)
157
  insights_suggestion_2_btn = gr.Button(value="Suggerimento 2", size="sm", min_width=50)
158
  insights_suggestion_3_btn = gr.Button(value="Suggerimento 3", size="sm", min_width=50)
159
+
160
  formula_display_markdown_ui = gr.Markdown(
161
  "I dettagli sulla formula/metodologia appariranno qui.", visible=False
162
  )
163
 
164
+ async def handle_panel_action(
165
+ plot_id_clicked: str,
166
+ action_type: str, # "insights" or "formula"
167
+ current_active_action_from_state: dict,
168
+ current_chat_histories: dict,
169
  current_chat_plot_id: str,
170
+ current_plot_data_for_chatbot: dict,
171
+ current_explored_plot_id: str # NEW: Current explore state
172
  ):
173
+ logging.info(f"Azione '{action_type}' per grafico: {plot_id_clicked}. Attualmente attivo: {current_active_action_from_state}, Esplorato: {current_explored_plot_id}")
174
+
175
  clicked_plot_config = next((p for p in plot_configs if p["id"] == plot_id_clicked), None)
176
  if not clicked_plot_config:
177
  logging.error(f"Configurazione non trovata per plot_id {plot_id_clicked}")
178
+ num_plots = len(plot_configs)
179
+ error_output_list = [
180
+ gr.update(visible=False), gr.update(visible=False), gr.update(value=[]),
181
+ gr.update(visible=False), gr.update(visible=False), gr.update(), gr.update(), gr.update(),
182
+ gr.update(visible=False), gr.update(value=""),
183
+ current_active_action_from_state, current_chat_plot_id, current_chat_histories,
184
+ current_explored_plot_id # Pass back existing explore state
185
+ ]
186
+ error_output_list.extend([gr.update()] * num_plots) # Panel vis
187
+ error_output_list.extend([gr.update()] * (2 * num_plots)) # Bomb, Formula icons
188
+ error_output_list.extend([gr.update()] * num_plots) # Explore icons
189
+ return error_output_list
190
 
191
  clicked_plot_label = clicked_plot_config["label"]
 
192
  hypothetical_new_active_state = {"plot_id": plot_id_clicked, "type": action_type}
193
  is_toggling_off = current_active_action_from_state == hypothetical_new_active_state
194
+
195
+ # Initialize updates
196
+ action_col_visible_update = gr.update(visible=False)
 
197
  insights_chatbot_visible_update = gr.update(visible=False)
198
  insights_chat_input_visible_update = gr.update(visible=False)
199
  insights_suggestions_row_visible_update = gr.update(visible=False)
200
  formula_display_visible_update = gr.update(visible=False)
201
+ chatbot_content_update = gr.update()
202
+ suggestion_1_update, suggestion_2_update, suggestion_3_update = gr.update(), gr.update(), gr.update()
203
+ formula_content_update = gr.update()
204
 
205
+ new_active_action_state_to_set = None
206
+ new_current_chat_plot_id = current_chat_plot_id
207
+ updated_chat_histories = current_chat_histories
208
+ new_explored_plot_id_to_set = current_explored_plot_id # Default to not changing explore state
209
+
210
+ panel_visibility_component_updates = []
211
+ action_button_component_updates = [] # For bomb and formula buttons
212
+ explore_button_component_updates = []
213
 
 
214
 
215
  if is_toggling_off:
216
  new_active_action_state_to_set = None
217
  action_col_visible_update = gr.update(visible=False)
218
+ # new_explored_plot_id_to_set remains current_explored_plot_id
219
+
220
+ logging.info(f"Chiusura pannello {action_type} per {plot_id_clicked}.")
221
+ if current_explored_plot_id: # If an explore view is active, keep it
222
+ for cfg_item_iter in plot_configs:
223
+ p_id_iter = cfg_item_iter["id"]
224
+ is_explored = (p_id_iter == current_explored_plot_id)
225
+ panel_visibility_component_updates.append(gr.update(visible=is_explored))
226
+ explore_button_component_updates.append(gr.update(value=ACTIVE_ICON if is_explored else EXPLORE_ICON))
227
+ else: # No explore active, make all plots visible
228
+ for _ in plot_configs:
229
+ panel_visibility_component_updates.append(gr.update(visible=True))
230
+ explore_button_component_updates.append(gr.update(value=EXPLORE_ICON))
231
+
232
+ # Reset bomb/formula icons for all plots to default
233
+ for _ in plot_configs:
234
+ action_button_component_updates.append(gr.update(value=BOMB_ICON))
235
+ action_button_component_updates.append(gr.update(value=FORMULA_ICON))
236
+
237
+ if action_type == "insights": new_current_chat_plot_id = None
238
+
239
+ else: # Opening a new panel or switching
240
  new_active_action_state_to_set = hypothetical_new_active_state
241
+ action_col_visible_update = gr.update(visible=True)
242
+
243
+ # IMPORTANT: Cancel any active explore view when opening insight/formula
244
+ new_explored_plot_id_to_set = None
245
+
246
+ logging.info(f"Apertura pannello {action_type} per {plot_id_clicked}. Solo questo grafico visibile. Esplorazione annullata.")
247
+
248
+ for cfg_item_iter in plot_configs:
249
+ p_id_iter = cfg_item_iter["id"]
250
+ is_target_plot = (p_id_iter == plot_id_clicked)
251
+ panel_visibility_component_updates.append(gr.update(visible=is_target_plot))
252
+ # Reset all explore buttons to default as explore is cancelled
253
+ explore_button_component_updates.append(gr.update(value=EXPLORE_ICON))
254
+
255
+ # Set active icons for the clicked plot's bomb/formula buttons
256
+ for cfg_item_btn_iter in plot_configs:
257
+ p_id_btn_iter = cfg_item_btn_iter["id"]
258
+ is_active_insights = new_active_action_state_to_set == {"plot_id": p_id_btn_iter, "type": "insights"}
259
+ is_active_formula = new_active_action_state_to_set == {"plot_id": p_id_btn_iter, "type": "formula"}
260
+ action_button_component_updates.append(gr.update(value=ACTIVE_ICON if is_active_insights else BOMB_ICON))
261
+ action_button_component_updates.append(gr.update(value=ACTIVE_ICON if is_active_formula else FORMULA_ICON))
262
+
263
  if action_type == "insights":
264
  insights_chatbot_visible_update = gr.update(visible=True)
265
  insights_chat_input_visible_update = gr.update(visible=True)
266
  insights_suggestions_row_visible_update = gr.update(visible=True)
 
267
  new_current_chat_plot_id = plot_id_clicked
268
  chat_history_for_this_plot = current_chat_histories.get(plot_id_clicked, [])
 
269
  plot_specific_data_summary = current_plot_data_for_chatbot.get(plot_id_clicked, f"Nessun sommario dati specifico disponibile per '{clicked_plot_label}'.")
270
 
271
+ if not chat_history_for_this_plot:
272
  initial_llm_prompt, suggestions = get_initial_insight_prompt_and_suggestions(
273
+ plot_id_clicked, clicked_plot_label, plot_specific_data_summary
 
 
274
  )
 
275
  history_for_llm_first_turn = [{"role": "user", "content": initial_llm_prompt}]
 
 
276
  initial_bot_response_text = await generate_llm_response(
277
+ initial_llm_prompt, plot_id_clicked, clicked_plot_label,
278
+ history_for_llm_first_turn, plot_specific_data_summary
 
 
 
279
  )
 
 
 
280
  chat_history_for_this_plot = [{"role": "assistant", "content": initial_bot_response_text}]
281
  updated_chat_histories = current_chat_histories.copy()
282
  updated_chat_histories[plot_id_clicked] = chat_history_for_this_plot
283
+ else:
284
  _, suggestions = get_initial_insight_prompt_and_suggestions(
285
+ plot_id_clicked, clicked_plot_label, plot_specific_data_summary
 
 
286
  )
287
+
288
  chatbot_content_update = gr.update(value=chat_history_for_this_plot)
289
+ suggestion_1_update = gr.update(value=suggestions[0] if len(suggestions) > 0 else "N/A")
290
+ suggestion_2_update = gr.update(value=suggestions[1] if len(suggestions) > 1 else "N/A")
291
+ suggestion_3_update = gr.update(value=suggestions[2] if len(suggestions) > 2 else "N/A")
 
292
 
293
  elif action_type == "formula":
294
  formula_display_visible_update = gr.update(visible=True)
 
296
  formula_text = f"**Formula/Metodologia per: {clicked_plot_label}**\n\nID Grafico: `{plot_id_clicked}`.\n\n"
297
  if formula_key and formula_key in PLOT_FORMULAS:
298
  formula_data = PLOT_FORMULAS[formula_key]
299
+ formula_text += f"### {formula_data['title']}\n\n{formula_data['description']}\n\n**Come viene calcolato:**\n"
300
+ formula_text += "\n".join([f"- {step}" for step in formula_data['calculation_steps']])
 
 
 
301
  else:
302
+ formula_text += "(Nessuna informazione dettagliata sulla formula trovata)"
303
  formula_content_update = gr.update(value=formula_text)
304
+ new_current_chat_plot_id = None # Ensure chat is cleared if formula is opened
305
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  final_updates = [
307
  action_col_visible_update,
308
  insights_chatbot_visible_update, chatbot_content_update,
309
  insights_chat_input_visible_update,
310
  insights_suggestions_row_visible_update, suggestion_1_update, suggestion_2_update, suggestion_3_update,
311
  formula_display_visible_update, formula_content_update,
312
+ new_active_action_state_to_set,
313
+ new_current_chat_plot_id,
314
+ updated_chat_histories,
315
+ new_explored_plot_id_to_set # NEW: update explored_plot_id_state
316
+ ]
317
+ final_updates.extend(panel_visibility_component_updates)
318
+ final_updates.extend(action_button_component_updates)
319
+ final_updates.extend(explore_button_component_updates)
320
 
321
  return final_updates
322
 
323
  async def handle_chat_message_submission(
324
+ user_message: str,
325
+ current_plot_id: str,
326
+ chat_histories: dict,
327
+ current_plot_data_for_chatbot: dict
328
  ):
329
  if not current_plot_id or not user_message.strip():
330
  history_for_plot = chat_histories.get(current_plot_id, [])
331
+ yield history_for_plot, gr.update(value=""), chat_histories
 
332
  return
333
 
334
  plot_config = next((p for p in plot_configs if p["id"] == current_plot_id), None)
335
  plot_label = plot_config["label"] if plot_config else "Grafico Selezionato"
 
 
336
  plot_specific_data_summary = current_plot_data_for_chatbot.get(current_plot_id, f"Nessun sommario dati specifico disponibile per '{plot_label}'.")
 
337
  history_for_plot = chat_histories.get(current_plot_id, []).copy()
338
  history_for_plot.append({"role": "user", "content": user_message})
339
+ yield history_for_plot, gr.update(value=""), chat_histories
 
 
340
 
 
341
  bot_response_text = await generate_llm_response(
342
+ user_message, current_plot_id, plot_label, history_for_plot, plot_specific_data_summary
 
 
 
 
343
  )
 
344
  history_for_plot.append({"role": "assistant", "content": bot_response_text})
 
345
  updated_chat_histories = chat_histories.copy()
346
  updated_chat_histories[current_plot_id] = history_for_plot
 
347
  yield history_for_plot, "", updated_chat_histories
348
 
 
349
  async def handle_suggested_question_click(
350
  suggestion_text: str,
351
+ current_plot_id: str,
352
+ chat_histories: dict,
353
+ current_plot_data_for_chatbot: dict
354
  ):
355
  if not current_plot_id or not suggestion_text.strip():
356
  history_for_plot = chat_histories.get(current_plot_id, [])
357
+ yield history_for_plot, gr.update(value=""), chat_histories
358
  return
 
 
 
359
  async for update in handle_chat_message_submission(
360
+ suggestion_text, current_plot_id, chat_histories, current_plot_data_for_chatbot
 
 
 
361
  ):
362
  yield update
363
 
364
+ def handle_explore_click(
365
+ plot_id_clicked,
366
+ current_explored_plot_id_from_state,
367
+ current_active_panel_action_state # NEW: current insight/formula state
368
+ ):
369
+ 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}")
370
  if not plot_ui_objects:
371
  logging.error("plot_ui_objects non popolato durante handle_explore_click.")
372
+ num_plots = len(plot_configs)
373
+ error_updates = [current_explored_plot_id_from_state, gr.update(visible=False), current_active_panel_action_state]
374
+ error_updates.extend([gr.update()] * num_plots) # Panel vis
375
+ error_updates.extend([gr.update()] * num_plots) # Explore icons
376
+ error_updates.extend([gr.update()] * (2 * num_plots)) # Bomb, Formula icons
377
+ return error_updates
378
+
379
  new_explored_id_to_set = None
380
+ is_toggling_off_explore = (plot_id_clicked == current_explored_plot_id_from_state)
381
+
382
+ action_col_update = gr.update()
383
+ new_active_panel_action_state_update = current_active_panel_action_state
384
 
385
+ panel_vis_updates = []
386
+ explore_btn_updates = []
387
+ bomb_btn_icon_upds = []
388
+ formula_btn_icon_upds = []
389
+
390
+ if is_toggling_off_explore:
391
  new_explored_id_to_set = None
392
+ logging.info(f"Interruzione esplorazione grafico: {plot_id_clicked}. Tutti i grafici diventano visibili.")
393
+ # All plots become visible
394
+ for _ in plot_configs:
395
+ panel_vis_updates.append(gr.update(visible=True))
396
+ explore_btn_updates.append(gr.update(value=EXPLORE_ICON))
397
+ # Insight/formula buttons remain as they were (no change)
398
+ for _ in plot_configs:
399
+ bomb_btn_icon_upds.append(gr.update())
400
+ formula_btn_icon_upds.append(gr.update())
401
+ else: # Activating explore for plot_id_clicked or switching explore
402
  new_explored_id_to_set = plot_id_clicked
403
+ logging.info(f"Esplorazione grafico: {plot_id_clicked}. Altri grafici nascosti.")
404
+ # Make only explored plot visible
405
+ for cfg in plot_configs:
406
+ p_id = cfg["id"]
407
+ is_target_plot = (p_id == new_explored_id_to_set)
408
+ panel_vis_updates.append(gr.update(visible=is_target_plot))
409
+ explore_btn_updates.append(gr.update(value=ACTIVE_ICON if is_target_plot else EXPLORE_ICON))
410
+
411
+ # If activating explore, close any active insight/formula panel
412
+ if current_active_panel_action_state:
413
+ logging.info("Chiusura pannello azioni (insight/formula) a causa dell'attivazione dell'esplorazione.")
414
+ action_col_update = gr.update(visible=False)
415
+ new_active_panel_action_state_update = None
416
+ # Reset all insight/formula button icons
417
+ for _ in plot_configs:
418
+ bomb_btn_icon_upds.append(gr.update(value=BOMB_ICON))
419
+ formula_btn_icon_upds.append(gr.update(value=FORMULA_ICON))
420
+ else: # No insight/formula panel active, no change to their buttons
421
+ for _ in plot_configs:
422
+ bomb_btn_icon_upds.append(gr.update())
423
+ formula_btn_icon_upds.append(gr.update())
424
+
425
+ final_updates = [
426
+ new_explored_id_to_set,
427
+ action_col_update,
428
+ new_active_panel_action_state_update
429
+ ]
430
+ final_updates.extend(panel_vis_updates)
431
+ final_updates.extend(explore_btn_updates)
432
+ final_updates.extend(bomb_btn_icon_upds)
433
+ final_updates.extend(formula_btn_icon_upds)
434
  return final_updates
435
+
436
+ # --- Define Output Lists for Event Handlers ---
437
  action_panel_outputs_list = [
438
+ global_actions_column_ui,
439
+ insights_chatbot_ui, insights_chatbot_ui,
440
+ insights_chat_input_ui,
441
+ insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
442
+ formula_display_markdown_ui, formula_display_markdown_ui,
443
+ active_panel_action_state,
444
  current_chat_plot_id_st,
445
+ chat_histories_st,
446
+ explored_plot_id_state # NEW: output for explore state
447
+ ]
448
+ for cfg_item_action_out in plot_configs:
449
+ pid_action_out = cfg_item_action_out["id"]
450
+ ui_obj_ref = plot_ui_objects.get(pid_action_out)
451
+ action_panel_outputs_list.append(ui_obj_ref["panel_component"] if ui_obj_ref else gr.update()) # Panel visibility
452
+ for cfg_item_action_out in plot_configs:
453
+ pid_action_out = cfg_item_action_out["id"]
454
+ ui_obj_ref = plot_ui_objects.get(pid_action_out)
455
+ action_panel_outputs_list.append(ui_obj_ref["bomb_button"] if ui_obj_ref else gr.update()) # Bomb icon
456
+ action_panel_outputs_list.append(ui_obj_ref["formula_button"] if ui_obj_ref else gr.update()) # Formula icon
457
+ for cfg_item_action_out in plot_configs:
458
+ pid_action_out = cfg_item_action_out["id"]
459
+ ui_obj_ref = plot_ui_objects.get(pid_action_out)
460
+ action_panel_outputs_list.append(ui_obj_ref["explore_button"] if ui_obj_ref else gr.update()) # Explore icon
461
+
462
+ explore_outputs_list = [
463
+ explored_plot_id_state,
464
+ global_actions_column_ui, # NEW: output for action column
465
+ active_panel_action_state # NEW: output for active panel state
466
  ]
467
+ for cfg_item_explore_out in plot_configs: # Panel visibility
468
+ pid_explore_out = cfg_item_explore_out["id"]
469
+ ui_obj_ref = plot_ui_objects.get(pid_explore_out)
470
+ explore_outputs_list.append(ui_obj_ref["panel_component"] if ui_obj_ref else gr.update())
471
+ for cfg_item_explore_out in plot_configs: # Explore button icons
472
+ pid_explore_out = cfg_item_explore_out["id"]
473
+ ui_obj_ref = plot_ui_objects.get(pid_explore_out)
474
+ explore_outputs_list.append(ui_obj_ref["explore_button"] if ui_obj_ref else gr.update())
475
+ for cfg_item_explore_out in plot_configs: # Bomb button icons
476
+ pid_explore_out = cfg_item_explore_out["id"]
477
+ ui_obj_ref = plot_ui_objects.get(pid_explore_out)
478
+ explore_outputs_list.append(ui_obj_ref["bomb_button"] if ui_obj_ref else gr.update())
479
+ for cfg_item_explore_out in plot_configs: # Formula button icons
480
+ pid_explore_out = cfg_item_explore_out["id"]
481
+ ui_obj_ref = plot_ui_objects.get(pid_explore_out)
482
+ explore_outputs_list.append(ui_obj_ref["formula_button"] if ui_obj_ref else gr.update())
483
+
484
+
485
+ # --- Define Input Lists for Event Handlers ---
486
  action_click_inputs = [
487
+ active_panel_action_state,
488
  chat_histories_st,
489
  current_chat_plot_id_st,
490
+ plot_data_for_chatbot_st,
491
+ explored_plot_id_state # NEW: input explore state
492
+ ]
493
+ explore_click_inputs = [
494
+ explored_plot_id_state,
495
+ active_panel_action_state # NEW: input active panel state
496
  ]
 
 
497
 
498
+ # --- Register Event Handlers ---
499
  def create_panel_action_handler(p_id, action_type_str):
500
+ 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
501
  logging.debug(f"Entering _handler for plot_id: {p_id}, action: {action_type_str}")
502
+ 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
503
+ logging.debug(f"_handler for plot_id: {p_id}, action: {action_type_str} completed with {len(result)} updates.")
504
  return result
505
  return _handler
506
 
 
508
  plot_id = config_item["id"]
509
  if plot_id in plot_ui_objects:
510
  ui_obj = plot_ui_objects[plot_id]
 
511
  ui_obj["bomb_button"].click(
512
+ fn=create_panel_action_handler(plot_id, "insights"),
513
+ inputs=action_click_inputs,
514
  outputs=action_panel_outputs_list,
515
  api_name=f"action_insights_{plot_id}"
516
  )
517
  ui_obj["formula_button"].click(
518
+ fn=create_panel_action_handler(plot_id, "formula"),
519
  inputs=action_click_inputs,
520
  outputs=action_panel_outputs_list,
521
  api_name=f"action_formula_{plot_id}"
522
  )
523
  ui_obj["explore_button"].click(
524
+ 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),
525
+ inputs=explore_click_inputs, # Uses updated explore_click_inputs
526
+ outputs=explore_outputs_list, # Uses updated explore_outputs_list
527
  api_name=f"action_explore_{plot_id}"
528
  )
529
  else:
530
  logging.warning(f"Oggetto UI per plot_id '{plot_id}' non trovato durante il tentativo di associare i gestori di click.")
531
+
532
  chat_submission_outputs = [insights_chatbot_ui, insights_chat_input_ui, chat_histories_st]
533
+ chat_submission_inputs = [insights_chat_input_ui, current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st]
534
 
535
  insights_chat_input_ui.submit(
536
  fn=handle_chat_message_submission,
537
+ inputs=chat_submission_inputs,
538
  outputs=chat_submission_outputs,
539
  api_name="submit_chat_message"
540
  )
541
 
542
+ suggestion_click_inputs = [current_chat_plot_id_st, chat_histories_st, plot_data_for_chatbot_st]
543
  insights_suggestion_1_btn.click(
544
  fn=handle_suggested_question_click,
545
+ inputs=[insights_suggestion_1_btn] + suggestion_click_inputs,
546
+ outputs=chat_submission_outputs, api_name="click_suggestion_1"
 
547
  )
548
  insights_suggestion_2_btn.click(
549
  fn=handle_suggested_question_click,
550
+ inputs=[insights_suggestion_2_btn] + suggestion_click_inputs,
551
+ outputs=chat_submission_outputs, api_name="click_suggestion_2"
 
552
  )
553
  insights_suggestion_3_btn.click(
554
  fn=handle_suggested_question_click,
555
+ inputs=[insights_suggestion_3_btn] + suggestion_click_inputs,
556
+ outputs=chat_submission_outputs, api_name="click_suggestion_3"
 
557
  )
558
 
559
  def refresh_all_analytics_ui_elements(current_token_state, date_filter_val, custom_start_val, custom_end_val, current_chat_histories):
560
  logging.info("Aggiornamento di tutti gli elementi UI delle analisi e reset delle azioni/chat.")
 
 
561
  plot_generation_results = update_analytics_plots_figures(
562
  current_token_state, date_filter_val, custom_start_val, custom_end_val, plot_configs
563
  )
564
  status_message_update = plot_generation_results[0]
565
+ generated_plot_figures = plot_generation_results[1:-1]
566
+ new_plot_data_summaries = plot_generation_results[-1]
567
+
568
+ all_updates = [status_message_update]
569
+ for i in range(len(plot_configs)):
 
570
  if i < len(generated_plot_figures):
571
+ all_updates.append(generated_plot_figures[i])
572
+ else:
573
+ # Ensure create_placeholder_plot is defined or handle appropriately
574
  all_updates.append(create_placeholder_plot("Errore Figura", f"Figura mancante per grafico {plot_configs[i]['id']}"))
575
 
576
  all_updates.extend([
577
  gr.update(visible=False), # global_actions_column_ui
578
+ gr.update(value=[], visible=False), # insights_chatbot_ui
579
+ gr.update(value="", visible=False), # insights_chat_input_ui
580
  gr.update(visible=False), # insights_suggestions_row_ui
581
+ gr.update(value="Suggerimento 1"), gr.update(value="Suggerimento 2"), gr.update(value="Suggerimento 3"),
 
 
582
  gr.update(value="I dettagli sulla formula/metodologia appariranno qui.", visible=False), # formula_display_markdown_ui
583
  None, # active_panel_action_state
584
  None, # current_chat_plot_id_st
585
+ {}, # chat_histories_st (reset)
586
+ new_plot_data_summaries # plot_data_for_chatbot_st
587
  ])
588
+
589
+ for cfg in plot_configs:
590
  pid = cfg["id"]
591
+ # Reset button icons
592
+ all_updates.append(gr.update(value=BOMB_ICON))
593
+ all_updates.append(gr.update(value=FORMULA_ICON))
594
+ all_updates.append(gr.update(value=EXPLORE_ICON))
595
+ # Make all plot panels visible
596
+ all_updates.append(gr.update(visible=True)) # panel_component visibility
 
 
 
597
 
598
+ all_updates.append(None) # explored_plot_id_state (reset)
599
  logging.info(f"Preparati {len(all_updates)} aggiornamenti per il refresh delle analisi.")
600
  return all_updates
601
 
602
+ apply_filter_and_sync_outputs_list = [analytics_status_md]
603
+ for config_item_filter_sync in plot_configs: # Plot components
604
+ pid = config_item_filter_sync["id"]
605
+ 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())
 
 
 
606
 
607
+ apply_filter_and_sync_outputs_list.extend([ # UI elements to reset
608
+ global_actions_column_ui, insights_chatbot_ui, insights_chat_input_ui,
609
+ insights_suggestions_row_ui, insights_suggestion_1_btn, insights_suggestion_2_btn, insights_suggestion_3_btn,
610
+ formula_display_markdown_ui,
611
+ active_panel_action_state, current_chat_plot_id_st, chat_histories_st,
612
+ plot_data_for_chatbot_st
 
 
 
 
 
 
 
613
  ])
614
+ for cfg_filter_sync_btns in plot_configs: # Button icons and panel visibility
615
+ pid = cfg_filter_sync_btns["id"]
616
+ apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["bomb_button"] if pid in plot_ui_objects else gr.update())
617
+ apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["formula_button"] if pid in plot_ui_objects else gr.update())
618
+ apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["explore_button"] if pid in plot_ui_objects else gr.update())
619
+ apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid]["panel_component"] if pid in plot_ui_objects else gr.update())
 
 
 
 
620
 
621
+ apply_filter_and_sync_outputs_list.append(explored_plot_id_state) # Reset explored state
 
 
622
 
623
  apply_filter_btn.click(
624
  fn=refresh_all_analytics_ui_elements,
 
640
  with gr.TabItem("4️⃣ Statistiche Follower", id="tab_follower_stats"):
641
  refresh_follower_stats_btn = gr.Button("🔄 Aggiorna Visualizzazione Statistiche Follower", variant="secondary")
642
  follower_stats_html = gr.HTML("Statistiche follower...")
643
+ with gr.Row():
644
  fs_plot_monthly_gains = gr.Plot(label="Guadagni Mensili Follower")
645
  with gr.Row():
646
  fs_plot_seniority = gr.Plot(label="Follower per Anzianità (Top 10 Organici)")
 
656
  fn=sync_all_linkedin_data_orchestrator,
657
  inputs=[token_state], outputs=[sync_status_html_output, token_state], show_progress="full"
658
  )
659
+ sync_event_part2 = sync_event_part1.then(
660
+ fn=process_and_store_bubble_token,
661
  inputs=[url_user_token_display, org_urn_display, token_state],
662
+ outputs=[status_box, token_state, sync_data_btn], show_progress=False
663
  )
664
  sync_event_part3 = sync_event_part2.then(
665
+ fn=display_main_dashboard,
666
  inputs=[token_state], outputs=[dashboard_display_html], show_progress=False
667
  )
668
  sync_event_final = sync_event_part3.then(
669
+ fn=refresh_all_analytics_ui_elements,
670
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker, chat_histories_st],
671
+ outputs=apply_filter_and_sync_outputs_list,
672
  show_progress="full"
673
  )
674
 
 
682
 
683
  try:
684
  logging.info(f"Versione Matplotlib: {matplotlib.__version__}, Backend: {matplotlib.get_backend()}")
685
+ except ImportError:
686
  logging.warning("Matplotlib non trovato direttamente, ma potrebbe essere usato dai generatori di grafici.")
687
 
688
+ app.launch(server_name="0.0.0.0", server_port=7860, debug=True)