GuglielmoTor commited on
Commit
6923fc7
·
verified ·
1 Parent(s): 63031db

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -343
app.py CHANGED
@@ -82,350 +82,7 @@ PLOT_ID_TO_FORMULA_KEY_MAP = {
82
  "mention_analysis_sentiment": "mention_sentiment"
83
  }
84
 
85
- # --- Helper function to generate textual data summaries for chatbot ---
86
- def generate_chatbot_data_summaries(
87
- plot_configs_list,
88
- filtered_merged_posts_df,
89
- filtered_mentions_df,
90
- date_filtered_follower_stats_df, # Expected to contain 'follower_gains_monthly'
91
- raw_follower_stats_df, # Expected to contain other demographics like 'follower_geo', 'follower_industry'
92
- token_state_value
93
- ):
94
- """
95
- Generates textual summaries for each plot ID to be used by the chatbot,
96
- based on the corrected understanding of DataFrame structures and follower count columns.
97
- """
98
- data_summaries = {}
99
-
100
- # --- Date and Config Columns from token_state ---
101
- # For Posts
102
- date_col_posts = token_state_value.get("config_date_col_posts", "published_at")
103
- media_type_col_name = token_state_value.get("config_media_type_col", "media_type")
104
- eb_labels_col_name = token_state_value.get("config_eb_labels_col", "li_eb_label")
105
- # For Mentions
106
- date_col_mentions = token_state_value.get("config_date_col_mentions", "date")
107
- mentions_sentiment_col = "sentiment_label" # As per user's mention df structure
108
-
109
- # For Follower Stats - Actual column names provided by user
110
- follower_count_organic_col = "follower_count_organic"
111
- follower_count_paid_col = "follower_count_paid"
112
-
113
- # For Follower Stats (Demographics from raw_follower_stats_df)
114
- follower_demographics_type_col = "follower_count_type" # Column indicating 'follower_geo', 'follower_industry'
115
- follower_demographics_category_col = "category_name" # Column indicating 'USA', 'Technology'
116
-
117
- # For Follower Gains/Growth (from date_filtered_follower_stats_df)
118
- follower_gains_type_col = "follower_count_type" # Should be 'follower_gains_monthly'
119
- follower_gains_date_col = "category_name" # This is 'YYYY-MM-DD'
120
-
121
- # --- Helper: Safely convert to datetime ---
122
- def safe_to_datetime(series, errors='coerce'):
123
- return pd.to_datetime(series, errors=errors)
124
-
125
- # --- Prepare DataFrames (copy and convert dates) ---
126
- if filtered_merged_posts_df is not None and not filtered_merged_posts_df.empty:
127
- posts_df = filtered_merged_posts_df.copy()
128
- if date_col_posts in posts_df.columns:
129
- posts_df[date_col_posts] = safe_to_datetime(posts_df[date_col_posts])
130
- else:
131
- logging.warning(f"Date column '{date_col_posts}' not found in posts_df for chatbot summary.")
132
- else:
133
- posts_df = pd.DataFrame()
134
-
135
- if filtered_mentions_df is not None and not filtered_mentions_df.empty:
136
- mentions_df = filtered_mentions_df.copy()
137
- if date_col_mentions in mentions_df.columns:
138
- mentions_df[date_col_mentions] = safe_to_datetime(mentions_df[date_col_mentions])
139
- else:
140
- logging.warning(f"Date column '{date_col_mentions}' not found in mentions_df for chatbot summary.")
141
- else:
142
- mentions_df = pd.DataFrame()
143
-
144
- # For date_filtered_follower_stats_df (monthly gains)
145
- if date_filtered_follower_stats_df is not None and not date_filtered_follower_stats_df.empty:
146
- follower_monthly_df = date_filtered_follower_stats_df.copy()
147
- if follower_gains_type_col in follower_monthly_df.columns:
148
- follower_monthly_df = follower_monthly_df[follower_monthly_df[follower_gains_type_col] == 'follower_gains_monthly'].copy()
149
-
150
- if follower_gains_date_col in follower_monthly_df.columns:
151
- follower_monthly_df['datetime_obj'] = safe_to_datetime(follower_monthly_df[follower_gains_date_col])
152
- follower_monthly_df = follower_monthly_df.dropna(subset=['datetime_obj'])
153
-
154
- # Calculate total gains
155
- if follower_count_organic_col in follower_monthly_df.columns and follower_count_paid_col in follower_monthly_df.columns:
156
- follower_monthly_df[follower_count_organic_col] = pd.to_numeric(follower_monthly_df[follower_count_organic_col], errors='coerce').fillna(0)
157
- follower_monthly_df[follower_count_paid_col] = pd.to_numeric(follower_monthly_df[follower_count_paid_col], errors='coerce').fillna(0)
158
- follower_monthly_df['total_monthly_gains'] = follower_monthly_df[follower_count_organic_col] + follower_monthly_df[follower_count_paid_col]
159
- elif follower_count_organic_col in follower_monthly_df.columns: # Only organic exists
160
- follower_monthly_df[follower_count_organic_col] = pd.to_numeric(follower_monthly_df[follower_count_organic_col], errors='coerce').fillna(0)
161
- follower_monthly_df['total_monthly_gains'] = follower_monthly_df[follower_count_organic_col]
162
- elif follower_count_paid_col in follower_monthly_df.columns: # Only paid exists
163
- follower_monthly_df[follower_count_paid_col] = pd.to_numeric(follower_monthly_df[follower_count_paid_col], errors='coerce').fillna(0)
164
- follower_monthly_df['total_monthly_gains'] = follower_monthly_df[follower_count_paid_col]
165
- else:
166
- logging.warning(f"Neither '{follower_count_organic_col}' nor '{follower_count_paid_col}' found in follower_monthly_df for total gains calculation.")
167
- follower_monthly_df['total_monthly_gains'] = 0 # Avoid KeyError later
168
- else:
169
- logging.warning(f"Date column '{follower_gains_date_col}' (from category_name) not found in follower_monthly_df for chatbot summary.")
170
- if 'datetime_obj' not in follower_monthly_df.columns:
171
- follower_monthly_df['datetime_obj'] = pd.NaT
172
- if 'total_monthly_gains' not in follower_monthly_df.columns:
173
- follower_monthly_df['total_monthly_gains'] = 0
174
- else:
175
- follower_monthly_df = pd.DataFrame(columns=[follower_gains_date_col, 'total_monthly_gains', 'datetime_obj'])
176
-
177
-
178
- if raw_follower_stats_df is not None and not raw_follower_stats_df.empty:
179
- follower_demographics_df = raw_follower_stats_df.copy()
180
- # Calculate total followers for demographics
181
- if follower_count_organic_col in follower_demographics_df.columns and follower_count_paid_col in follower_demographics_df.columns:
182
- follower_demographics_df[follower_count_organic_col] = pd.to_numeric(follower_demographics_df[follower_count_organic_col], errors='coerce').fillna(0)
183
- follower_demographics_df[follower_count_paid_col] = pd.to_numeric(follower_demographics_df[follower_count_paid_col], errors='coerce').fillna(0)
184
- follower_demographics_df['total_follower_count'] = follower_demographics_df[follower_count_organic_col] + follower_demographics_df[follower_count_paid_col]
185
- elif follower_count_organic_col in follower_demographics_df.columns:
186
- follower_demographics_df[follower_count_organic_col] = pd.to_numeric(follower_demographics_df[follower_count_organic_col], errors='coerce').fillna(0)
187
- follower_demographics_df['total_follower_count'] = follower_demographics_df[follower_count_organic_col]
188
- elif follower_count_paid_col in follower_demographics_df.columns:
189
- follower_demographics_df[follower_count_paid_col] = pd.to_numeric(follower_demographics_df[follower_count_paid_col], errors='coerce').fillna(0)
190
- follower_demographics_df['total_follower_count'] = follower_demographics_df[follower_count_paid_col]
191
- else:
192
- logging.warning(f"Neither '{follower_count_organic_col}' nor '{follower_count_paid_col}' found in follower_demographics_df for total count calculation.")
193
- if 'total_follower_count' not in follower_demographics_df.columns:
194
- follower_demographics_df['total_follower_count'] = 0
195
- else:
196
- follower_demographics_df = pd.DataFrame()
197
-
198
-
199
- for plot_cfg in plot_configs_list:
200
- plot_id = plot_cfg["id"]
201
- plot_label = plot_cfg["label"]
202
- summary_text = f"No specific data summary available for '{plot_label}' for the selected period."
203
-
204
- try:
205
- # --- FOLLOWER STATS ---
206
- if plot_id == "followers_count": # Uses follower_monthly_df
207
- if not follower_monthly_df.empty and 'total_monthly_gains' in follower_monthly_df.columns and 'datetime_obj' in follower_monthly_df.columns and not follower_monthly_df['datetime_obj'].isnull().all():
208
- df_summary = follower_monthly_df[['datetime_obj', 'total_monthly_gains']].copy()
209
- df_summary['datetime_obj'] = df_summary['datetime_obj'].dt.strftime('%Y-%m-%d')
210
- df_summary.rename(columns={'datetime_obj': 'Date', 'total_monthly_gains': 'Total Monthly Gains'}, inplace=True)
211
- summary_text = f"Follower Count (Total Monthly Gains):\n{df_summary.sort_values(by='Date').tail(5).to_string(index=False)}"
212
- else:
213
- summary_text = f"Follower count data (total monthly gains) is unavailable or incomplete for '{plot_label}'."
214
-
215
- elif plot_id == "followers_growth_rate": # Uses follower_monthly_df
216
- if not follower_monthly_df.empty and 'total_monthly_gains' in follower_monthly_df.columns and 'datetime_obj' in follower_monthly_df.columns and not follower_monthly_df['datetime_obj'].isnull().all():
217
- df_calc = follower_monthly_df.sort_values(by='datetime_obj').copy()
218
- # Growth rate is calculated on the total monthly gains (which are changes, not cumulative counts)
219
- # To calculate growth rate of followers, we'd need cumulative follower count.
220
- # The plot logic also uses pct_change on the gains themselves.
221
- # If 'total_monthly_gains' represents the *change* in followers, then pct_change on this is rate of change of gains.
222
- # If it represents the *cumulative* followers at that point, then pct_change is follower growth rate.
223
- # Assuming 'total_monthly_gains' is the *change* for the month, like the plot logic.
224
- df_calc['total_monthly_gains'] = pd.to_numeric(df_calc['total_monthly_gains'], errors='coerce')
225
- if len(df_calc) >= 2:
226
- # Calculate cumulative sum to get follower count if 'total_monthly_gains' are indeed just gains
227
- # If your 'total_monthly_gains' already IS the total follower count at end of month, remove next line
228
- # For now, assuming it's GAINS, so we need cumulative for growth rate of total followers.
229
- # However, the original plot logic applies pct_change directly to 'follower_gains_monthly'.
230
- # Let's stick to pct_change on the gains/count column for consistency with plot.
231
-
232
- # If 'total_monthly_gains' is the actual follower count for that month:
233
- df_calc['growth_rate_monthly'] = df_calc['total_monthly_gains'].pct_change() * 100
234
- df_calc['growth_rate_monthly'] = df_calc['growth_rate_monthly'].round(2)
235
- df_calc.replace([np.inf, -np.inf], np.nan, inplace=True) # Handle division by zero if a gain was 0
236
-
237
- df_summary = df_calc[['datetime_obj', 'growth_rate_monthly']].dropna().copy()
238
- df_summary['datetime_obj'] = df_summary['datetime_obj'].dt.strftime('%Y-%m-%d')
239
- df_summary.rename(columns={'datetime_obj': 'Date', 'growth_rate_monthly': 'Growth Rate (%)'}, inplace=True)
240
- if not df_summary.empty:
241
- summary_text = f"Follower Growth Rate (Monthly % based on Total Follower Count/Gains):\n{df_summary.sort_values(by='Date').tail(5).to_string(index=False)}"
242
- else:
243
- summary_text = f"Not enough data points or valid transitions to calculate follower growth rate for '{plot_label}'."
244
- else:
245
- summary_text = f"Not enough data points (need at least 2) to calculate follower growth rate for '{plot_label}'."
246
- else:
247
- summary_text = f"Follower growth rate data (total monthly gains) is unavailable or incomplete for '{plot_label}'."
248
-
249
- elif plot_id in ["followers_by_location", "followers_by_role", "followers_by_industry", "followers_by_seniority"]:
250
- demographic_type_map = {
251
- "followers_by_location": "follower_geo",
252
- "followers_by_role": "follower_function",
253
- "followers_by_industry": "follower_industry",
254
- "followers_by_seniority": "follower_seniority"
255
- }
256
- current_demographic_type = demographic_type_map.get(plot_id)
257
- if not follower_demographics_df.empty and \
258
- follower_demographics_type_col in follower_demographics_df.columns and \
259
- follower_demographics_category_col in follower_demographics_df.columns and \
260
- 'total_follower_count' in follower_demographics_df.columns: # Check for the calculated total
261
-
262
- df_filtered_demographics = follower_demographics_df[
263
- follower_demographics_df[follower_demographics_type_col] == current_demographic_type
264
- ].copy()
265
-
266
- if not df_filtered_demographics.empty:
267
- df_summary = df_filtered_demographics.groupby(follower_demographics_category_col)['total_follower_count'].sum().reset_index()
268
- df_summary.rename(columns={follower_demographics_category_col: 'Category', 'total_follower_count': 'Total Follower Count'}, inplace=True)
269
- top_5 = df_summary.nlargest(5, 'Total Follower Count')
270
- summary_text = f"Top 5 {plot_label} (Total Followers):\n{top_5.to_string(index=False)}"
271
- else:
272
- summary_text = f"No data available for demographic type '{current_demographic_type}' in '{plot_label}'."
273
- else:
274
- summary_text = f"Follower demographic data columns (including total_follower_count) are missing or incomplete for '{plot_label}'."
275
 
276
- # --- POSTS STATS ---
277
- elif plot_id == "engagement_rate":
278
- if not posts_df.empty and 'engagement' in posts_df.columns and date_col_posts in posts_df.columns and not posts_df[date_col_posts].isnull().all():
279
- df_resampled = posts_df.set_index(date_col_posts)['engagement'].resample('W').mean().reset_index()
280
- df_resampled['engagement'] = pd.to_numeric(df_resampled['engagement'], errors='coerce').round(2)
281
- df_summary = df_resampled[[date_col_posts, 'engagement']].dropna().copy()
282
- df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
283
- summary_text = f"Engagement Rate Over Time (Weekly Avg %):\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
284
- else:
285
- summary_text = f"Engagement rate data is unavailable for '{plot_label}'."
286
-
287
- elif plot_id == "reach_over_time":
288
- if not posts_df.empty and 'reach' in posts_df.columns and date_col_posts in posts_df.columns and not posts_df[date_col_posts].isnull().all():
289
- df_resampled = posts_df.set_index(date_col_posts)['reach'].resample('W').sum().reset_index()
290
- df_resampled['reach'] = pd.to_numeric(df_resampled['reach'], errors='coerce')
291
- df_summary = df_resampled[[date_col_posts, 'reach']].dropna().copy()
292
- df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
293
- summary_text = f"Reach Over Time (Weekly Sum):\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
294
- else:
295
- summary_text = f"Reach data is unavailable for '{plot_label}'."
296
-
297
- elif plot_id == "impressions_over_time":
298
- if not posts_df.empty and 'impressionCount' in posts_df.columns and date_col_posts in posts_df.columns and not posts_df[date_col_posts].isnull().all():
299
- df_resampled = posts_df.set_index(date_col_posts)['impressionCount'].resample('W').sum().reset_index()
300
- df_resampled['impressionCount'] = pd.to_numeric(df_resampled['impressionCount'], errors='coerce')
301
- df_summary = df_resampled[[date_col_posts, 'impressionCount']].dropna().copy()
302
- df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
303
- df_summary.rename(columns={'impressionCount': 'Impressions'}, inplace=True)
304
- summary_text = f"Impressions Over Time (Weekly Sum):\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
305
- else:
306
- summary_text = f"Impressions data is unavailable for '{plot_label}'."
307
-
308
- elif plot_id == "likes_over_time":
309
- if not posts_df.empty and 'likeCount' in posts_df.columns and date_col_posts in posts_df.columns and not posts_df[date_col_posts].isnull().all():
310
- df_resampled = posts_df.set_index(date_col_posts)['likeCount'].resample('W').sum().reset_index()
311
- df_resampled['likeCount'] = pd.to_numeric(df_resampled['likeCount'], errors='coerce')
312
- df_summary = df_resampled[[date_col_posts, 'likeCount']].dropna().copy()
313
- df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
314
- df_summary.rename(columns={'likeCount': 'Likes'}, inplace=True)
315
- summary_text = f"Likes Over Time (Weekly Sum):\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
316
- else:
317
- summary_text = f"Likes data is unavailable for '{plot_label}'."
318
-
319
- elif plot_id == "clicks_over_time":
320
- if not posts_df.empty and 'clickCount' in posts_df.columns and date_col_posts in posts_df.columns and not posts_df[date_col_posts].isnull().all():
321
- df_resampled = posts_df.set_index(date_col_posts)['clickCount'].resample('W').sum().reset_index()
322
- df_resampled['clickCount'] = pd.to_numeric(df_resampled['clickCount'], errors='coerce')
323
- df_summary = df_resampled[[date_col_posts, 'clickCount']].dropna().copy()
324
- df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
325
- df_summary.rename(columns={'clickCount': 'Clicks'}, inplace=True)
326
- summary_text = f"Clicks Over Time (Weekly Sum):\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
327
- else:
328
- summary_text = f"Clicks data is unavailable for '{plot_label}'."
329
-
330
- elif plot_id == "shares_over_time":
331
- if not posts_df.empty and 'shareCount' in posts_df.columns and date_col_posts in posts_df.columns and not posts_df[date_col_posts].isnull().all():
332
- df_resampled = posts_df.set_index(date_col_posts)['shareCount'].resample('W').sum().reset_index()
333
- df_resampled['shareCount'] = pd.to_numeric(df_resampled['shareCount'], errors='coerce')
334
- df_summary = df_resampled[[date_col_posts, 'shareCount']].dropna().copy()
335
- df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
336
- df_summary.rename(columns={'shareCount': 'Shares'}, inplace=True)
337
- summary_text = f"Shares Over Time (Weekly Sum):\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
338
- elif 'shareCount' not in posts_df.columns and not posts_df.empty : # Check if posts_df is not empty before assuming column is the only issue
339
- summary_text = f"Shares data column ('shareCount') not found for '{plot_label}'."
340
- else:
341
- summary_text = f"Shares data is unavailable for '{plot_label}'."
342
-
343
- elif plot_id == "comments_over_time":
344
- if not posts_df.empty and 'commentCount' in posts_df.columns and date_col_posts in posts_df.columns and not posts_df[date_col_posts].isnull().all():
345
- df_resampled = posts_df.set_index(date_col_posts)['commentCount'].resample('W').sum().reset_index()
346
- df_resampled['commentCount'] = pd.to_numeric(df_resampled['commentCount'], errors='coerce')
347
- df_summary = df_resampled[[date_col_posts, 'commentCount']].dropna().copy()
348
- df_summary[date_col_posts] = df_summary[date_col_posts].dt.strftime('%Y-%m-%d')
349
- df_summary.rename(columns={'commentCount': 'Comments'}, inplace=True)
350
- summary_text = f"Comments Over Time (Weekly Sum):\n{df_summary.sort_values(by=date_col_posts).tail(5).to_string(index=False)}"
351
- else:
352
- summary_text = f"Comments data is unavailable for '{plot_label}'."
353
-
354
- elif plot_id == "comments_sentiment":
355
- comment_sentiment_col_posts = "sentiment"
356
- if not posts_df.empty and comment_sentiment_col_posts in posts_df.columns:
357
- sentiment_counts = posts_df[comment_sentiment_col_posts].value_counts().reset_index()
358
- sentiment_counts.columns = ['Sentiment', 'Count']
359
- summary_text = f"Comments Sentiment Breakdown (Posts Data):\n{sentiment_counts.to_string(index=False)}"
360
- else:
361
- summary_text = f"Comment sentiment data ('{comment_sentiment_col_posts}') is unavailable for '{plot_label}'."
362
-
363
- elif plot_id == "post_frequency_cs":
364
- if not posts_df.empty and date_col_posts in posts_df.columns and not posts_df[date_col_posts].isnull().all():
365
- post_counts_weekly = posts_df.set_index(date_col_posts).resample('W').size().reset_index(name='post_count')
366
- post_counts_weekly.rename(columns={date_col_posts: 'Week', 'post_count': 'Posts'}, inplace=True)
367
- post_counts_weekly['Week'] = post_counts_weekly['Week'].dt.strftime('%Y-%m-%d (Week of)')
368
- summary_text = f"Post Frequency (Weekly):\n{post_counts_weekly.sort_values(by='Week').tail(5).to_string(index=False)}"
369
- else:
370
- summary_text = f"Post frequency data is unavailable for '{plot_label}'."
371
-
372
- elif plot_id == "content_format_breakdown_cs":
373
- if not posts_df.empty and media_type_col_name in posts_df.columns:
374
- format_counts = posts_df[media_type_col_name].value_counts().reset_index()
375
- format_counts.columns = ['Format', 'Count']
376
- summary_text = f"Content Format Breakdown:\n{format_counts.nlargest(5, 'Count').to_string(index=False)}"
377
- else:
378
- summary_text = f"Content format data ('{media_type_col_name}') is unavailable for '{plot_label}'."
379
-
380
- elif plot_id == "content_topic_breakdown_cs":
381
- if not posts_df.empty and eb_labels_col_name in posts_df.columns:
382
- try:
383
- # Ensure the column is not all NaN before trying to check for lists or explode
384
- if posts_df[eb_labels_col_name].notna().any():
385
- if posts_df[eb_labels_col_name].apply(lambda x: isinstance(x, list)).any():
386
- topic_counts = posts_df.explode(eb_labels_col_name)[eb_labels_col_name].value_counts().reset_index()
387
- else:
388
- topic_counts = posts_df[eb_labels_col_name].value_counts().reset_index()
389
- topic_counts.columns = ['Topic', 'Count']
390
- summary_text = f"Content Topic Breakdown (Top 5):\n{topic_counts.nlargest(5, 'Count').to_string(index=False)}"
391
- else:
392
- summary_text = f"Content topic data ('{eb_labels_col_name}') contains no valid topics for '{plot_label}'."
393
- except Exception as e_topic:
394
- logging.warning(f"Could not process topic breakdown for '{eb_labels_col_name}': {e_topic}")
395
- summary_text = f"Content topic data ('{eb_labels_col_name}') could not be processed for '{plot_label}'."
396
- else:
397
- summary_text = f"Content topic data ('{eb_labels_col_name}') is unavailable for '{plot_label}'."
398
-
399
- # --- MENTIONS STATS ---
400
- elif plot_id == "mention_analysis_volume":
401
- if not mentions_df.empty and date_col_mentions in mentions_df.columns and not mentions_df[date_col_mentions].isnull().all():
402
- mentions_over_time = mentions_df.set_index(date_col_mentions).resample('W').size().reset_index(name='mention_count')
403
- mentions_over_time.rename(columns={date_col_mentions: 'Week', 'mention_count': 'Mentions'}, inplace=True)
404
- mentions_over_time['Week'] = mentions_over_time['Week'].dt.strftime('%Y-%m-%d (Week of)')
405
- if not mentions_over_time.empty:
406
- summary_text = f"Mentions Volume (Weekly):\n{mentions_over_time.sort_values(by='Week').tail(5).to_string(index=False)}"
407
- else:
408
- summary_text = f"No mention activity found for '{plot_label}' in the selected period."
409
- else:
410
- summary_text = f"Mentions volume data is unavailable for '{plot_label}'."
411
-
412
- elif plot_id == "mention_analysis_sentiment":
413
- if not mentions_df.empty and mentions_sentiment_col in mentions_df.columns:
414
- sentiment_counts = mentions_df[mentions_sentiment_col].value_counts().reset_index()
415
- sentiment_counts.columns = ['Sentiment', 'Count']
416
- summary_text = f"Mentions Sentiment Breakdown:\n{sentiment_counts.to_string(index=False)}"
417
- else:
418
- summary_text = f"Mention sentiment data ('{mentions_sentiment_col}') is unavailable for '{plot_label}'."
419
-
420
- data_summaries[plot_id] = summary_text
421
- except KeyError as e:
422
- logging.warning(f"KeyError generating summary for {plot_id} ('{plot_label}'): {e}. Using default summary.")
423
- data_summaries[plot_id] = f"Data summary generation error for '{plot_label}' (missing column: {e})."
424
- except Exception as e:
425
- logging.error(f"Error generating summary for {plot_id} ('{plot_label}'): {e}", exc_info=True)
426
- data_summaries[plot_id] = f"Error generating data summary for '{plot_label}'."
427
-
428
- return data_summaries
429
  # --- Analytics Tab: Plot Figure Generation Function ---
430
  def update_analytics_plots_figures(token_state_value, date_filter_option, custom_start_date, custom_end_date, current_plot_configs):
431
  logging.info(f"Updating analytics plot figures. Filter: {date_filter_option}, Custom Start: {custom_start_date}, Custom End: {custom_end_date}")
 
82
  "mention_analysis_sentiment": "mention_sentiment"
83
  }
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  # --- Analytics Tab: Plot Figure Generation Function ---
87
  def update_analytics_plots_figures(token_state_value, date_filter_option, custom_start_date, custom_end_date, current_plot_configs):
88
  logging.info(f"Updating analytics plot figures. Filter: {date_filter_option}, Custom Start: {custom_start_date}, Custom End: {custom_end_date}")