GuglielmoTor commited on
Commit
abb0fcc
·
verified ·
1 Parent(s): 520f04f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +89 -73
app.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  import gradio as gr
2
  import pandas as pd
3
  import os
@@ -46,7 +48,7 @@ from analytics_plot_generator import (
46
  from formulas import PLOT_FORMULAS
47
 
48
  # --- NEW CHATBOT MODULE IMPORTS ---
49
- from chatbot_prompts import get_initial_insight_and_suggestions
50
  from chatbot_handler import generate_llm_response
51
  # --- END NEW CHATBOT MODULE IMPORTS ---
52
 
@@ -93,60 +95,65 @@ def generate_chatbot_data_summaries(
93
  """
94
  data_summaries = {}
95
  date_col_posts = token_state_value.get("config_date_col_posts", "published_at")
 
96
  # Ensure date columns are datetime objects for proper formatting and comparison
97
- if date_col_posts in filtered_merged_posts_df.columns:
 
 
 
98
  filtered_merged_posts_df[date_col_posts] = pd.to_datetime(filtered_merged_posts_df[date_col_posts], errors='coerce')
99
- if 'date' in date_filtered_follower_stats_df.columns:
100
- date_filtered_follower_stats_df['date'] = pd.to_datetime(date_filtered_follower_stats_df['date'], errors='coerce')
 
 
101
  # Add more date conversions as needed for other dataframes
102
 
103
  for plot_cfg in plot_configs_list:
104
  plot_id = plot_cfg["id"]
105
- summary_text = f"No data summary available for {plot_cfg['label']} for the selected period."
106
 
107
  try:
108
- if plot_id == "followers_count" and not date_filtered_follower_stats_df.empty:
109
  df_summary = date_filtered_follower_stats_df[['date', 'follower_gains_monthly']].copy()
110
  df_summary['date'] = df_summary['date'].dt.strftime('%Y-%m-%d')
111
- summary_text = f"Follower Count (Monthly Gains):\n{df_summary.tail(5).to_string(index=False)}"
112
 
113
- elif plot_id == "followers_growth_rate" and not date_filtered_follower_stats_df.empty:
114
- df_summary = date_filtered_follower_stats_df[['date', 'growth_rate_monthly']].copy() # Assuming 'growth_rate_monthly' column
115
  df_summary['date'] = df_summary['date'].dt.strftime('%Y-%m-%d')
116
- summary_text = f"Follower Growth Rate (Monthly):\n{df_summary.tail(5).to_string(index=False)}"
117
-
118
- elif plot_id == "followers_by_location" and not raw_follower_stats_df.empty and 'follower_geo' in raw_follower_stats_df.columns:
119
- # Assuming 'follower_geo' contains location names and another column like 'count' exists
120
- # This part needs to align with how generate_followers_by_demographics_plot processes 'follower_geo' data
121
- # For simplicity, let's assume raw_follower_stats_df has pre-aggregated data for geo
122
- # This is a placeholder, actual column names for count and name are needed from your data prep
123
- if 'geo_name' in raw_follower_stats_df.columns and 'geo_count' in raw_follower_stats_df.columns:
124
- df_summary = raw_follower_stats_df[['geo_name', 'geo_count']].nlargest(5, 'geo_count')
125
- summary_text = f"Followers by Location (Top 5):\n{df_summary.to_string(index=False)}"
126
- else:
127
- summary_text = "Follower location data structure not as expected for summary."
128
 
 
 
 
 
 
 
 
 
 
129
 
130
- elif plot_id == "engagement_rate" and not filtered_merged_posts_df.empty:
 
131
  df_summary = filtered_merged_posts_df[[date_col_posts, 'engagement_rate']].copy()
132
  df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
133
- summary_text = f"Engagement Rate Over Time:\n{df_summary.tail(5).to_string(index=False)}"
134
 
135
- elif plot_id == "reach_over_time" and not filtered_merged_posts_df.empty:
136
  df_summary = filtered_merged_posts_df[[date_col_posts, 'reach']].copy()
137
  df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
138
- summary_text = f"Reach Over Time:\n{df_summary.tail(5).to_string(index=False)}"
139
 
140
- elif plot_id == "impressions_over_time" and not filtered_merged_posts_df.empty:
141
- df_summary = filtered_merged_posts_df[[date_col_posts, 'impressions_sum']].copy() # Assuming 'impressions_sum' or similar
142
  df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
143
- summary_text = f"Impressions Over Time:\n{df_summary.tail(5).to_string(index=False)}"
144
 
145
  elif plot_id == "comments_sentiment" and not filtered_merged_posts_df.empty and 'comment_sentiment' in filtered_merged_posts_df.columns:
146
  sentiment_counts = filtered_merged_posts_df['comment_sentiment'].value_counts().reset_index()
147
  sentiment_counts.columns = ['Sentiment', 'Count']
148
  summary_text = f"Comments Sentiment Breakdown:\n{sentiment_counts.to_string(index=False)}"
149
-
150
  # Add more elif blocks for other plot_ids, extracting relevant data:
151
  # e.g., likes_over_time, clicks_over_time, shares_over_time, comments_over_time
152
  # post_frequency_cs, content_format_breakdown_cs, content_topic_breakdown_cs
@@ -217,7 +224,7 @@ def update_analytics_plots_figures(token_state_value, date_filter_option, custom
217
  try:
218
  # Dinamiche dei Follower (2 plots)
219
  plot_figs.append(generate_followers_count_over_time_plot(date_filtered_follower_stats_df, type_value='follower_gains_monthly'))
220
- plot_figs.append(generate_followers_growth_rate_plot(date_filtered_follower_stats_df, type_value='follower_gains_monthly'))
221
 
222
  # Demografia Follower (4 plots)
223
  plot_figs.append(generate_followers_by_demographics_plot(raw_follower_stats_df, type_value='follower_geo', plot_title="Follower per Località"))
@@ -228,7 +235,7 @@ def update_analytics_plots_figures(token_state_value, date_filter_option, custom
228
  # Approfondimenti Performance Post (4 plots)
229
  plot_figs.append(generate_engagement_rate_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts))
230
  plot_figs.append(generate_reach_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts))
231
- plot_figs.append(generate_impressions_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts))
232
  plot_figs.append(generate_likes_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts))
233
 
234
  # Engagement Dettagliato Post nel Tempo (4 plots)
@@ -288,13 +295,13 @@ def update_analytics_plots_figures(token_state_value, date_filter_option, custom
288
  placeholder_figs_general = [create_placeholder_plot(title=plot_titles_for_errors[i] if i < len(plot_titles_for_errors) else f"Grafico {i+1}", message=str(e_general)) for i in range(num_expected_plots)]
289
  for p_cfg in current_plot_configs: # Ensure summaries dict is populated on error
290
  if p_cfg["id"] not in plot_data_summaries_for_chatbot:
291
- plot_data_summaries_for_chatbot[p_cfg["id"]] = f"Errore generale grafici: {e_general}"
292
  return [error_msg] + placeholder_figs_general + [plot_data_summaries_for_chatbot]
293
 
294
 
295
  # --- Gradio UI Blocks ---
296
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
297
- title="LinkedIn Organization Dashboard") as app:
298
  token_state = gr.State(value={
299
  "token": None, "client_id": None, "org_urn": None,
300
  "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
@@ -361,8 +368,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
361
  outputs=[custom_start_date_picker, custom_end_date_picker]
362
  )
363
 
364
- # Moved plot_configs to be globally accessible within the Blocks scope if needed by update_analytics_plots_figures
365
- # or pass it explicitly. For now, it's defined here.
366
  plot_configs = [
367
  {"label": "Numero di Follower nel Tempo", "id": "followers_count", "section": "Dinamiche dei Follower"},
368
  {"label": "Tasso di Crescita Follower", "id": "followers_growth_rate", "section": "Dinamiche dei Follower"},
@@ -430,7 +435,8 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
430
  if not clicked_plot_config:
431
  logging.error(f"Configurazione non trovata per plot_id {plot_id_clicked}")
432
  num_button_updates = 2 * len(plot_configs) # insights, formula buttons
433
- error_updates = [gr.update(visible=False)] * 10 # action_col, chatbot, input, suggestions_row, 3x sugg_btn, formula_md
 
434
  error_updates.extend([current_active_action_from_state, current_chat_plot_id, current_chat_histories])
435
  error_updates.extend([gr.update()] * num_button_updates)
436
  return error_updates
@@ -448,7 +454,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
448
  insights_suggestions_row_visible_update = gr.update(visible=False)
449
  formula_display_visible_update = gr.update(visible=False)
450
 
451
- chatbot_content_update = gr.update()
452
  suggestion_1_update = gr.update()
453
  suggestion_2_update = gr.update()
454
  suggestion_3_update = gr.update()
@@ -472,27 +478,38 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
472
  new_current_chat_plot_id = plot_id_clicked
473
  chat_history_for_this_plot = current_chat_histories.get(plot_id_clicked, [])
474
 
475
- # Get data summary for this plot
476
  plot_specific_data_summary = current_plot_data_for_chatbot.get(plot_id_clicked, f"Nessun sommario dati specifico disponibile per '{clicked_plot_label}'.")
477
 
478
  if not chat_history_for_this_plot:
479
- initial_insight_msg, suggestions = get_initial_insight_and_suggestions(
480
  plot_id_clicked,
481
  clicked_plot_label,
482
- plot_specific_data_summary # Pass the summary
 
 
 
 
 
 
 
 
 
 
 
483
  )
484
- chat_history_for_this_plot = [initial_insight_msg] # Gradio expects list of dicts
 
 
 
485
  updated_chat_histories = current_chat_histories.copy()
486
  updated_chat_histories[plot_id_clicked] = chat_history_for_this_plot
487
- else:
488
- # If history exists, still get fresh suggestions, but don't overwrite history's first message
489
- _, suggestions = get_initial_insight_and_suggestions(
490
  plot_id_clicked,
491
  clicked_plot_label,
492
- plot_specific_data_summary # Pass summary for context if needed by suggestions
493
  )
494
 
495
-
496
  chatbot_content_update = gr.update(value=chat_history_for_this_plot)
497
  suggestion_1_update = gr.update(value=suggestions[0])
498
  suggestion_2_update = gr.update(value=suggestions[1])
@@ -537,8 +554,8 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
537
  insights_suggestions_row_visible_update, suggestion_1_update, suggestion_2_update, suggestion_3_update,
538
  formula_display_visible_update, formula_content_update,
539
  new_active_action_state_to_set,
540
- new_current_chat_plot_id,
541
- updated_chat_histories
542
  ] + all_button_icon_updates
543
 
544
  return final_updates
@@ -561,7 +578,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
561
  # Retrieve the specific data summary for the current plot
562
  plot_specific_data_summary = current_plot_data_for_chatbot.get(current_plot_id, f"Nessun sommario dati specifico disponibile per '{plot_label}'.")
563
 
564
-
565
  history_for_plot = chat_histories.get(current_plot_id, []).copy()
566
  history_for_plot.append({"role": "user", "content": user_message})
567
 
@@ -573,7 +589,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
573
  user_message,
574
  current_plot_id,
575
  plot_label,
576
- history_for_plot, # This history now includes the initial insight with summary + user message
577
  plot_specific_data_summary # Explicitly pass for this turn if needed by LLM handler logic
578
  )
579
 
@@ -660,7 +676,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
660
  action_panel_outputs_list.append(plot_ui_objects[pid_action]["bomb_button"])
661
  action_panel_outputs_list.append(plot_ui_objects[pid_action]["formula_button"])
662
  else:
663
- action_panel_outputs_list.extend([None, None])
664
 
665
  # Outputs for explore actions
666
  explore_buttons_outputs_list = [explored_plot_id_state]
@@ -670,7 +686,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
670
  explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["panel_component"])
671
  explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["explore_button"])
672
  else:
673
- explore_buttons_outputs_list.extend([None, None])
674
 
675
  # Inputs for panel actions
676
  action_click_inputs = [
@@ -767,16 +783,16 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
767
 
768
  all_updates.extend([
769
  gr.update(visible=False), # global_actions_column_ui
770
- gr.update(value=[], visible=False), # insights_chatbot_ui (value)
771
- gr.update(value="", visible=False), # insights_chat_input_ui (value)
772
  gr.update(visible=False), # insights_suggestions_row_ui
773
- gr.update(value="Suggerimento 1", visible=True), # insights_suggestion_1_btn
774
- gr.update(value="Suggerimento 2", visible=True), # insights_suggestion_2_btn
775
- gr.update(value="Suggerimento 3", visible=True), # insights_suggestion_3_btn
776
  gr.update(value="I dettagli sulla formula/metodologia appariranno qui.", visible=False), # formula_display_markdown_ui
777
  None, # active_panel_action_state
778
  None, # current_chat_plot_id_st
779
- current_chat_histories, # chat_histories_st (preserve or reset as needed, here preserving)
780
  new_plot_data_summaries # NEW: plot_data_for_chatbot_st
781
  ])
782
 
@@ -786,9 +802,9 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
786
  all_updates.append(gr.update(value=BOMB_ICON))
787
  all_updates.append(gr.update(value=FORMULA_ICON))
788
  all_updates.append(gr.update(value=EXPLORE_ICON))
789
- all_updates.append(gr.update(visible=True)) # panel_component visibility
790
  else:
791
- all_updates.extend([None, None, None, None])
792
 
793
  all_updates.append(None) # explored_plot_id_state
794
 
@@ -801,21 +817,21 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
801
  if pid_filter_sync in plot_ui_objects and "plot_component" in plot_ui_objects[pid_filter_sync]:
802
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync]["plot_component"])
803
  else:
804
- apply_filter_and_sync_outputs_list.append(None)
805
 
806
  apply_filter_and_sync_outputs_list.extend([
807
- global_actions_column_ui, # Reset visibility
808
- insights_chatbot_ui, # Reset content & visibility
809
- insights_chat_input_ui, # Reset content & visibility
810
- insights_suggestions_row_ui, # Reset visibility
811
- insights_suggestion_1_btn, # Reset text & visibility
812
  insights_suggestion_2_btn,
813
  insights_suggestion_3_btn,
814
- formula_display_markdown_ui, # Reset content & visibility
815
- active_panel_action_state, # Reset state
816
- current_chat_plot_id_st, # Reset state
817
- chat_histories_st, # Preserve or reset state
818
- plot_data_for_chatbot_st # NEW: Update this state
819
  ])
820
 
821
  for cfg_filter_sync_btns in plot_configs:
@@ -826,7 +842,7 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
826
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["explore_button"])
827
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["panel_component"])
828
  else:
829
- apply_filter_and_sync_outputs_list.extend([None, None, None, None])
830
 
831
  apply_filter_and_sync_outputs_list.append(explored_plot_id_state) # Reset state
832
 
@@ -897,4 +913,4 @@ if __name__ == "__main__":
897
  except ImportError:
898
  logging.warning("Matplotlib non trovato direttamente, ma potrebbe essere usato dai generatori di grafici.")
899
 
900
- 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
5
  import os
 
48
  from formulas import PLOT_FORMULAS
49
 
50
  # --- NEW CHATBOT MODULE IMPORTS ---
51
+ from chatbot_prompts import get_initial_insight_prompt_and_suggestions # MODIFIED IMPORT
52
  from chatbot_handler import generate_llm_response
53
  # --- END NEW CHATBOT MODULE IMPORTS ---
54
 
 
95
  """
96
  data_summaries = {}
97
  date_col_posts = token_state_value.get("config_date_col_posts", "published_at")
98
+
99
  # Ensure date columns are datetime objects for proper formatting and comparison
100
+ # Make copies to avoid SettingWithCopyWarning if dataframes are slices
101
+ if not filtered_merged_posts_df.empty and date_col_posts in filtered_merged_posts_df.columns:
102
+ # Use .loc to ensure modification happens on the correct DataFrame
103
+ filtered_merged_posts_df = filtered_merged_posts_df.copy()
104
  filtered_merged_posts_df[date_col_posts] = pd.to_datetime(filtered_merged_posts_df[date_col_posts], errors='coerce')
105
+
106
+ if not date_filtered_follower_stats_df.empty and 'date' in date_filtered_follower_stats_df.columns:
107
+ date_filtered_follower_stats_df = date_filtered_follower_stats_df.copy()
108
+ date_filtered_follower_stats_df['date'] = pd.to_datetime(date_filtered_follower_stats_df['date'], errors='coerce')
109
  # Add more date conversions as needed for other dataframes
110
 
111
  for plot_cfg in plot_configs_list:
112
  plot_id = plot_cfg["id"]
113
+ summary_text = f"Nessun sommario dati specifico disponibile per '{plot_cfg['label']}' per il periodo selezionato." # Ensure this matches prompt logic
114
 
115
  try:
116
+ if plot_id == "followers_count" and not date_filtered_follower_stats_df.empty and 'follower_gains_monthly' in date_filtered_follower_stats_df.columns and 'date' in date_filtered_follower_stats_df.columns:
117
  df_summary = date_filtered_follower_stats_df[['date', 'follower_gains_monthly']].copy()
118
  df_summary['date'] = df_summary['date'].dt.strftime('%Y-%m-%d')
119
+ summary_text = f"Follower Count (Monthly Gains):\n{df_summary.sort_values(by='date').tail(5).to_string(index=False)}"
120
 
121
+ elif plot_id == "followers_growth_rate" and not date_filtered_follower_stats_df.empty and 'growth_rate_monthly' in date_filtered_follower_stats_df.columns and 'date' in date_filtered_follower_stats_df.columns:
122
+ df_summary = date_filtered_follower_stats_df[['date', 'growth_rate_monthly']].copy()
123
  df_summary['date'] = df_summary['date'].dt.strftime('%Y-%m-%d')
124
+ summary_text = f"Follower Growth Rate (Monthly %):\n{df_summary.sort_values(by='date').tail(5).to_string(index=False)}"
 
 
 
 
 
 
 
 
 
 
 
125
 
126
+ elif plot_id == "followers_by_location" and not raw_follower_stats_df.empty:
127
+ # This assumes 'follower_geo' is a column that needs processing similar to the plot generator
128
+ # For simplicity, if your raw_follower_stats_df might have pre-aggregated 'geo_name' and 'geo_count'
129
+ if 'follower_geo_processed_for_summary' in raw_follower_stats_df.columns: # Placeholder for actual processed data
130
+ # Example: df_summary = raw_follower_stats_df[['geo_name', 'geo_count']].nlargest(5, 'geo_count')
131
+ # summary_text = f"Followers by Location (Top 5):\n{df_summary.to_string(index=False)}"
132
+ pass # Implement actual summary logic based on your data structure
133
+ else: # Fallback if specific processed columns aren't ready for summary
134
+ summary_text = f"Follower location demographic data for '{plot_cfg['label']}' is complex; specific summary requires pre-processing aligned with the chart."
135
 
136
+
137
+ elif plot_id == "engagement_rate" and not filtered_merged_posts_df.empty and 'engagement_rate' in filtered_merged_posts_df.columns and date_col_posts in filtered_merged_posts_df.columns:
138
  df_summary = filtered_merged_posts_df[[date_col_posts, 'engagement_rate']].copy()
139
  df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
140
+ summary_text = f"Engagement Rate Over Time (%):\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
141
 
142
+ elif plot_id == "reach_over_time" and not filtered_merged_posts_df.empty and 'reach' in filtered_merged_posts_df.columns and date_col_posts in filtered_merged_posts_df.columns:
143
  df_summary = filtered_merged_posts_df[[date_col_posts, 'reach']].copy()
144
  df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
145
+ summary_text = f"Reach Over Time:\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
146
 
147
+ elif plot_id == "impressions_over_time" and not filtered_merged_posts_df.empty and 'impressions_sum' in filtered_merged_posts_df.columns and date_col_posts in filtered_merged_posts_df.columns:
148
+ df_summary = filtered_merged_posts_df[[date_col_posts, 'impressions_sum']].copy()
149
  df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
150
+ summary_text = f"Impressions Over Time:\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
151
 
152
  elif plot_id == "comments_sentiment" and not filtered_merged_posts_df.empty and 'comment_sentiment' in filtered_merged_posts_df.columns:
153
  sentiment_counts = filtered_merged_posts_df['comment_sentiment'].value_counts().reset_index()
154
  sentiment_counts.columns = ['Sentiment', 'Count']
155
  summary_text = f"Comments Sentiment Breakdown:\n{sentiment_counts.to_string(index=False)}"
156
+
157
  # Add more elif blocks for other plot_ids, extracting relevant data:
158
  # e.g., likes_over_time, clicks_over_time, shares_over_time, comments_over_time
159
  # post_frequency_cs, content_format_breakdown_cs, content_topic_breakdown_cs
 
224
  try:
225
  # Dinamiche dei Follower (2 plots)
226
  plot_figs.append(generate_followers_count_over_time_plot(date_filtered_follower_stats_df, type_value='follower_gains_monthly'))
227
+ plot_figs.append(generate_followers_growth_rate_plot(date_filtered_follower_stats_df, type_value='follower_gains_monthly')) # Assuming this uses 'follower_gains_monthly' to calculate rate
228
 
229
  # Demografia Follower (4 plots)
230
  plot_figs.append(generate_followers_by_demographics_plot(raw_follower_stats_df, type_value='follower_geo', plot_title="Follower per Località"))
 
235
  # Approfondimenti Performance Post (4 plots)
236
  plot_figs.append(generate_engagement_rate_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts))
237
  plot_figs.append(generate_reach_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts))
238
+ plot_figs.append(generate_impressions_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts)) # Ensure 'impressions_sum' or equivalent is used by this func
239
  plot_figs.append(generate_likes_over_time_plot(filtered_merged_posts_df, date_column=date_column_posts))
240
 
241
  # Engagement Dettagliato Post nel Tempo (4 plots)
 
295
  placeholder_figs_general = [create_placeholder_plot(title=plot_titles_for_errors[i] if i < len(plot_titles_for_errors) else f"Grafico {i+1}", message=str(e_general)) for i in range(num_expected_plots)]
296
  for p_cfg in current_plot_configs: # Ensure summaries dict is populated on error
297
  if p_cfg["id"] not in plot_data_summaries_for_chatbot:
298
+ plot_data_summaries_for_chatbot[p_cfg["id"]] = f"Errore generale grafici: {e_general}"
299
  return [error_msg] + placeholder_figs_general + [plot_data_summaries_for_chatbot]
300
 
301
 
302
  # --- Gradio UI Blocks ---
303
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
304
+ title="LinkedIn Organization Dashboard") as app:
305
  token_state = gr.State(value={
306
  "token": None, "client_id": None, "org_urn": None,
307
  "bubble_posts_df": pd.DataFrame(), "bubble_post_stats_df": pd.DataFrame(),
 
368
  outputs=[custom_start_date_picker, custom_end_date_picker]
369
  )
370
 
 
 
371
  plot_configs = [
372
  {"label": "Numero di Follower nel Tempo", "id": "followers_count", "section": "Dinamiche dei Follower"},
373
  {"label": "Tasso di Crescita Follower", "id": "followers_growth_rate", "section": "Dinamiche dei Follower"},
 
435
  if not clicked_plot_config:
436
  logging.error(f"Configurazione non trovata per plot_id {plot_id_clicked}")
437
  num_button_updates = 2 * len(plot_configs) # insights, formula buttons
438
+ error_updates = [gr.update(visible=False)] * 7 # action_col, chatbot, input, suggestions_row, 3x sugg_btn
439
+ error_updates.append(gr.update(visible=False, value="")) # formula_md (visibility and value)
440
  error_updates.extend([current_active_action_from_state, current_chat_plot_id, current_chat_histories])
441
  error_updates.extend([gr.update()] * num_button_updates)
442
  return error_updates
 
454
  insights_suggestions_row_visible_update = gr.update(visible=False)
455
  formula_display_visible_update = gr.update(visible=False)
456
 
457
+ chatbot_content_update = gr.update() # No change by default
458
  suggestion_1_update = gr.update()
459
  suggestion_2_update = gr.update()
460
  suggestion_3_update = gr.update()
 
478
  new_current_chat_plot_id = plot_id_clicked
479
  chat_history_for_this_plot = current_chat_histories.get(plot_id_clicked, [])
480
 
 
481
  plot_specific_data_summary = current_plot_data_for_chatbot.get(plot_id_clicked, f"Nessun sommario dati specifico disponibile per '{clicked_plot_label}'.")
482
 
483
  if not chat_history_for_this_plot:
484
+ initial_llm_prompt, suggestions = get_initial_insight_prompt_and_suggestions(
485
  plot_id_clicked,
486
  clicked_plot_label,
487
+ plot_specific_data_summary
488
+ )
489
+ # History for LLM's first turn: the system's prompt as a user message
490
+ history_for_llm_first_turn = [{"role": "user", "content": initial_llm_prompt}]
491
+
492
+ logging.info(f"Generating initial LLM insight for {plot_id_clicked}...")
493
+ initial_bot_response_text = await generate_llm_response(
494
+ user_message=initial_llm_prompt, # For context/logging in handler
495
+ plot_id=plot_id_clicked,
496
+ plot_label=clicked_plot_label,
497
+ chat_history_for_plot=history_for_llm_first_turn,
498
+ plot_data_summary=plot_specific_data_summary
499
  )
500
+ logging.info(f"LLM initial insight received for {plot_id_clicked}.")
501
+
502
+ # History for Gradio display starts with the assistant's response
503
+ chat_history_for_this_plot = [{"role": "assistant", "content": initial_bot_response_text}]
504
  updated_chat_histories = current_chat_histories.copy()
505
  updated_chat_histories[plot_id_clicked] = chat_history_for_this_plot
506
+ else: # History exists, get fresh suggestions
507
+ _, suggestions = get_initial_insight_prompt_and_suggestions(
 
508
  plot_id_clicked,
509
  clicked_plot_label,
510
+ plot_specific_data_summary
511
  )
512
 
 
513
  chatbot_content_update = gr.update(value=chat_history_for_this_plot)
514
  suggestion_1_update = gr.update(value=suggestions[0])
515
  suggestion_2_update = gr.update(value=suggestions[1])
 
554
  insights_suggestions_row_visible_update, suggestion_1_update, suggestion_2_update, suggestion_3_update,
555
  formula_display_visible_update, formula_content_update,
556
  new_active_action_state_to_set,
557
+ new_current_chat_plot_id,
558
+ updated_chat_histories
559
  ] + all_button_icon_updates
560
 
561
  return final_updates
 
578
  # Retrieve the specific data summary for the current plot
579
  plot_specific_data_summary = current_plot_data_for_chatbot.get(current_plot_id, f"Nessun sommario dati specifico disponibile per '{plot_label}'.")
580
 
 
581
  history_for_plot = chat_histories.get(current_plot_id, []).copy()
582
  history_for_plot.append({"role": "user", "content": user_message})
583
 
 
589
  user_message,
590
  current_plot_id,
591
  plot_label,
592
+ history_for_plot, # This history now includes the user message
593
  plot_specific_data_summary # Explicitly pass for this turn if needed by LLM handler logic
594
  )
595
 
 
676
  action_panel_outputs_list.append(plot_ui_objects[pid_action]["bomb_button"])
677
  action_panel_outputs_list.append(plot_ui_objects[pid_action]["formula_button"])
678
  else:
679
+ action_panel_outputs_list.extend([gr.update(), gr.update()]) # Use gr.update() as placeholder
680
 
681
  # Outputs for explore actions
682
  explore_buttons_outputs_list = [explored_plot_id_state]
 
686
  explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["panel_component"])
687
  explore_buttons_outputs_list.append(plot_ui_objects[pid_explore]["explore_button"])
688
  else:
689
+ explore_buttons_outputs_list.extend([gr.update(), gr.update()])
690
 
691
  # Inputs for panel actions
692
  action_click_inputs = [
 
783
 
784
  all_updates.extend([
785
  gr.update(visible=False), # global_actions_column_ui
786
+ gr.update(value=[], visible=False), # insights_chatbot_ui (value & visibility)
787
+ gr.update(value="", visible=False), # insights_chat_input_ui (value & visibility)
788
  gr.update(visible=False), # insights_suggestions_row_ui
789
+ gr.update(value="Suggerimento 1"), # insights_suggestion_1_btn (reset value, visibility handled by row)
790
+ gr.update(value="Suggerimento 2"), # insights_suggestion_2_btn
791
+ gr.update(value="Suggerimento 3"), # insights_suggestion_3_btn
792
  gr.update(value="I dettagli sulla formula/metodologia appariranno qui.", visible=False), # formula_display_markdown_ui
793
  None, # active_panel_action_state
794
  None, # current_chat_plot_id_st
795
+ {}, # chat_histories_st (reset chat histories on filter change)
796
  new_plot_data_summaries # NEW: plot_data_for_chatbot_st
797
  ])
798
 
 
802
  all_updates.append(gr.update(value=BOMB_ICON))
803
  all_updates.append(gr.update(value=FORMULA_ICON))
804
  all_updates.append(gr.update(value=EXPLORE_ICON))
805
+ all_updates.append(gr.update(visible=True)) # panel_component visibility
806
  else:
807
+ all_updates.extend([gr.update(), gr.update(), gr.update(), gr.update()])
808
 
809
  all_updates.append(None) # explored_plot_id_state
810
 
 
817
  if pid_filter_sync in plot_ui_objects and "plot_component" in plot_ui_objects[pid_filter_sync]:
818
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync]["plot_component"])
819
  else:
820
+ apply_filter_and_sync_outputs_list.append(gr.update())
821
 
822
  apply_filter_and_sync_outputs_list.extend([
823
+ global_actions_column_ui, # Reset visibility
824
+ insights_chatbot_ui, # Reset content & visibility
825
+ insights_chat_input_ui, # Reset content & visibility
826
+ insights_suggestions_row_ui, # Reset visibility
827
+ insights_suggestion_1_btn, # Reset text & visibility
828
  insights_suggestion_2_btn,
829
  insights_suggestion_3_btn,
830
+ formula_display_markdown_ui, # Reset content & visibility
831
+ active_panel_action_state, # Reset state
832
+ current_chat_plot_id_st, # Reset state
833
+ chat_histories_st, # Preserve or reset state (resetting via refresh_all_analytics_ui_elements)
834
+ plot_data_for_chatbot_st # NEW: Update this state
835
  ])
836
 
837
  for cfg_filter_sync_btns in plot_configs:
 
842
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["explore_button"])
843
  apply_filter_and_sync_outputs_list.append(plot_ui_objects[pid_filter_sync_btns]["panel_component"])
844
  else:
845
+ apply_filter_and_sync_outputs_list.extend([gr.update(), gr.update(), gr.update(), gr.update()])
846
 
847
  apply_filter_and_sync_outputs_list.append(explored_plot_id_state) # Reset state
848
 
 
913
  except ImportError:
914
  logging.warning("Matplotlib non trovato direttamente, ma potrebbe essere usato dai generatori di grafici.")
915
 
916
+ app.launch(server_name="0.0.0.0", server_port=7860, debug=True)