GuglielmoTor commited on
Commit
4e82b79
·
verified ·
1 Parent(s): 266ae82

Update ui_generators.py

Browse files
Files changed (1) hide show
  1. ui_generators.py +97 -19
ui_generators.py CHANGED
@@ -1,11 +1,13 @@
1
  # ui_generators.py
2
  """
3
- Generates HTML content and Matplotlib plots for the Gradio UI tabs.
 
4
  """
5
  import pandas as pd
6
  import logging
7
  import matplotlib.pyplot as plt
8
  import matplotlib # To ensure backend is switched before any plt import from other modules if app structure changes
 
9
 
10
  # Switch backend for Matplotlib to Agg for Gradio compatibility
11
  matplotlib.use('Agg')
@@ -18,6 +20,10 @@ from config import (
18
  FOLLOWER_STATS_PAID_COLUMN, FOLLOWER_STATS_CATEGORY_COLUMN_DT, UI_DATE_FORMAT, UI_MONTH_FORMAT
19
  )
20
 
 
 
 
 
21
  def display_main_dashboard(token_state):
22
  """Generates HTML for the main dashboard display using data from token_state."""
23
  if not token_state or not token_state.get("token"):
@@ -37,8 +43,11 @@ def display_main_dashboard(token_state):
37
  display_df_posts = posts_df.copy()
38
  if BUBBLE_POST_DATE_COLUMN_NAME in display_df_posts.columns:
39
  try:
40
- display_df_posts[BUBBLE_POST_DATE_COLUMN_NAME] = pd.to_datetime(display_df_posts[BUBBLE_POST_DATE_COLUMN_NAME], errors='coerce').dt.strftime(UI_DATE_FORMAT)
 
41
  display_df_posts = display_df_posts.sort_values(by=BUBBLE_POST_DATE_COLUMN_NAME, ascending=False)
 
 
42
  except Exception as e:
43
  logging.error(f"Error formatting post dates for display: {e}")
44
  html_parts.append("<p>Error formatting post dates.</p>")
@@ -58,8 +67,9 @@ def display_main_dashboard(token_state):
58
  display_df_mentions = mentions_df.copy()
59
  if BUBBLE_MENTIONS_DATE_COLUMN_NAME in display_df_mentions.columns:
60
  try:
61
- display_df_mentions[BUBBLE_MENTIONS_DATE_COLUMN_NAME] = pd.to_datetime(display_df_mentions[BUBBLE_MENTIONS_DATE_COLUMN_NAME], errors='coerce').dt.strftime(UI_DATE_FORMAT)
62
  display_df_mentions = display_df_mentions.sort_values(by=BUBBLE_MENTIONS_DATE_COLUMN_NAME, ascending=False)
 
63
  except Exception as e:
64
  logging.error(f"Error formatting mention dates for display: {e}")
65
  html_parts.append("<p>Error formatting mention dates.</p>")
@@ -76,15 +86,13 @@ def display_main_dashboard(token_state):
76
  if not monthly_gains.empty and FOLLOWER_STATS_CATEGORY_COLUMN in monthly_gains.columns and \
77
  FOLLOWER_STATS_ORGANIC_COLUMN in monthly_gains.columns and FOLLOWER_STATS_PAID_COLUMN in monthly_gains.columns:
78
  try:
79
- # FOLLOWER_STATS_CATEGORY_COLUMN for monthly gains is 'YYYY-MM-DD'
80
  monthly_gains.loc[:, FOLLOWER_STATS_CATEGORY_COLUMN_DT] = pd.to_datetime(monthly_gains[FOLLOWER_STATS_CATEGORY_COLUMN], errors='coerce')
81
- # Format original date column for display after sorting by datetime
82
  monthly_gains_display = monthly_gains.sort_values(by=FOLLOWER_STATS_CATEGORY_COLUMN_DT, ascending=False)
83
- latest_gain = monthly_gains_display.head(1).copy() # Work with a copy for modification
84
  if not latest_gain.empty:
85
- latest_gain.loc[:, FOLLOWER_STATS_CATEGORY_COLUMN] = latest_gain[FOLLOWER_STATS_CATEGORY_COLUMN_DT].dt.strftime(UI_DATE_FORMAT) # or UI_MONTH_FORMAT
86
- html_parts.append("<h5>Latest Monthly Follower Gain:</h5>")
87
- html_parts.append(latest_gain[[FOLLOWER_STATS_CATEGORY_COLUMN, FOLLOWER_STATS_ORGANIC_COLUMN, FOLLOWER_STATS_PAID_COLUMN]].to_html(escape=True, index=False, classes="table table-sm"))
88
  else:
89
  html_parts.append("<p>No valid monthly follower gain data to display after processing.</p>")
90
  except Exception as e:
@@ -120,8 +128,9 @@ def run_mentions_tab_display(token_state):
120
  mentions_df_display = mentions_df.copy()
121
  if BUBBLE_MENTIONS_DATE_COLUMN_NAME in mentions_df_display.columns:
122
  try:
123
- mentions_df_display[BUBBLE_MENTIONS_DATE_COLUMN_NAME] = pd.to_datetime(mentions_df_display[BUBBLE_MENTIONS_DATE_COLUMN_NAME], errors='coerce').dt.strftime(UI_DATE_FORMAT)
124
  mentions_df_display = mentions_df_display.sort_values(by=BUBBLE_MENTIONS_DATE_COLUMN_NAME, ascending=False)
 
125
  except Exception as e:
126
  logging.error(f"Error formatting mention dates for tab display: {e}")
127
  html_parts.append("<p>Error formatting mention dates.</p>")
@@ -132,7 +141,7 @@ def run_mentions_tab_display(token_state):
132
  html_parts.append(mentions_df_display[display_columns].head(20).to_html(escape=False, index=False, classes="table table-sm"))
133
 
134
  mentions_html_output = "\n".join(html_parts)
135
- fig = None
136
  if not mentions_df.empty and "sentiment_label" in mentions_df.columns:
137
  try:
138
  fig_plot, ax = plt.subplots(figsize=(6,4))
@@ -142,13 +151,17 @@ def run_mentions_tab_display(token_state):
142
  ax.set_ylabel("Count")
143
  plt.xticks(rotation=45, ha='right')
144
  plt.tight_layout()
145
- fig = fig_plot
146
  logging.info("Mentions tab: Sentiment distribution plot generated.")
147
  except Exception as e:
148
  logging.error(f"Error generating mentions plot: {e}", exc_info=True)
149
- fig = None
150
- else:
151
- logging.info("Mentions tab: Not enough data or 'sentiment_label' column missing for plot.")
 
 
 
 
152
 
153
  return mentions_html_output, fig
154
 
@@ -187,17 +200,15 @@ def run_follower_stats_tab_display(token_state):
187
 
188
  html_parts.append("<h4>Monthly Follower Gains (Last 13 Months):</h4>")
189
  table_display_df = monthly_gains_df_sorted_table.copy()
190
- table_display_df.loc[:,FOLLOWER_STATS_CATEGORY_COLUMN] = table_display_df[FOLLOWER_STATS_CATEGORY_COLUMN_DT].dt.strftime(UI_MONTH_FORMAT) # Use YYYY-MM for table
191
  html_parts.append(table_display_df[[FOLLOWER_STATS_CATEGORY_COLUMN, FOLLOWER_STATS_ORGANIC_COLUMN, FOLLOWER_STATS_PAID_COLUMN]].head(13).to_html(escape=True, index=False, classes="table table-sm"))
192
 
193
  monthly_gains_df_sorted_plot = monthly_gains_df.sort_values(by=FOLLOWER_STATS_CATEGORY_COLUMN_DT, ascending=True).copy()
194
- # For plotting, group by month string to ensure unique x-ticks if multiple entries exist for a month (though unlikely for this data type)
195
  monthly_gains_df_sorted_plot.loc[:, '_plot_month'] = monthly_gains_df_sorted_plot[FOLLOWER_STATS_CATEGORY_COLUMN_DT].dt.strftime(UI_MONTH_FORMAT)
196
  plot_data = monthly_gains_df_sorted_plot.groupby('_plot_month').agg(
197
  organic=(FOLLOWER_STATS_ORGANIC_COLUMN, 'sum'),
198
  paid=(FOLLOWER_STATS_PAID_COLUMN, 'sum')
199
- ).reset_index().sort_values(by='_plot_month')
200
-
201
 
202
  fig_gains, ax_gains = plt.subplots(figsize=(10,5))
203
  ax_gains.plot(plot_data['_plot_month'], plot_data['organic'], marker='o', linestyle='-', label='Organic Gain')
@@ -214,6 +225,11 @@ def run_follower_stats_tab_display(token_state):
214
  except Exception as e:
215
  logging.error(f"Error processing or plotting monthly gains: {e}", exc_info=True)
216
  html_parts.append("<p>Error displaying monthly follower gain data.</p>")
 
 
 
 
 
217
  else:
218
  html_parts.append("<p>No monthly follower gain data available or required columns missing.</p>")
219
  html_parts.append("<hr/>")
@@ -243,6 +259,11 @@ def run_follower_stats_tab_display(token_state):
243
  except Exception as e:
244
  logging.error(f"Error processing or plotting seniority data: {e}", exc_info=True)
245
  html_parts.append("<p>Error displaying follower seniority data.</p>")
 
 
 
 
 
246
  else:
247
  html_parts.append("<p>No follower seniority data available or required columns missing.</p>")
248
  html_parts.append("<hr/>")
@@ -272,9 +293,66 @@ def run_follower_stats_tab_display(token_state):
272
  except Exception as e:
273
  logging.error(f"Error processing or plotting industry data: {e}", exc_info=True)
274
  html_parts.append("<p>Error displaying follower industry data.</p>")
 
 
 
 
 
 
275
  else:
276
  html_parts.append("<p>No follower industry data available or required columns missing.</p>")
277
 
278
  html_parts.append("</div>")
279
  follower_html_output = "\n".join(html_parts)
280
  return follower_html_output, plot_monthly_gains, plot_seniority_dist, plot_industry_dist
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # ui_generators.py
2
  """
3
+ Generates HTML content and Matplotlib plots for the Gradio UI tabs,
4
+ and UI components for the Analytics tab.
5
  """
6
  import pandas as pd
7
  import logging
8
  import matplotlib.pyplot as plt
9
  import matplotlib # To ensure backend is switched before any plt import from other modules if app structure changes
10
+ import gradio as gr # Added for UI components
11
 
12
  # Switch backend for Matplotlib to Agg for Gradio compatibility
13
  matplotlib.use('Agg')
 
20
  FOLLOWER_STATS_PAID_COLUMN, FOLLOWER_STATS_CATEGORY_COLUMN_DT, UI_DATE_FORMAT, UI_MONTH_FORMAT
21
  )
22
 
23
+ # Configure logging for this module if not already configured at app level
24
+ # logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
25
+
26
+
27
  def display_main_dashboard(token_state):
28
  """Generates HTML for the main dashboard display using data from token_state."""
29
  if not token_state or not token_state.get("token"):
 
43
  display_df_posts = posts_df.copy()
44
  if BUBBLE_POST_DATE_COLUMN_NAME in display_df_posts.columns:
45
  try:
46
+ # Ensure the date column is datetime before formatting
47
+ display_df_posts[BUBBLE_POST_DATE_COLUMN_NAME] = pd.to_datetime(display_df_posts[BUBBLE_POST_DATE_COLUMN_NAME], errors='coerce')
48
  display_df_posts = display_df_posts.sort_values(by=BUBBLE_POST_DATE_COLUMN_NAME, ascending=False)
49
+ # Format for display after sorting
50
+ display_df_posts[BUBBLE_POST_DATE_COLUMN_NAME] = display_df_posts[BUBBLE_POST_DATE_COLUMN_NAME].dt.strftime(UI_DATE_FORMAT)
51
  except Exception as e:
52
  logging.error(f"Error formatting post dates for display: {e}")
53
  html_parts.append("<p>Error formatting post dates.</p>")
 
67
  display_df_mentions = mentions_df.copy()
68
  if BUBBLE_MENTIONS_DATE_COLUMN_NAME in display_df_mentions.columns:
69
  try:
70
+ display_df_mentions[BUBBLE_MENTIONS_DATE_COLUMN_NAME] = pd.to_datetime(display_df_mentions[BUBBLE_MENTIONS_DATE_COLUMN_NAME], errors='coerce')
71
  display_df_mentions = display_df_mentions.sort_values(by=BUBBLE_MENTIONS_DATE_COLUMN_NAME, ascending=False)
72
+ display_df_mentions[BUBBLE_MENTIONS_DATE_COLUMN_NAME] = display_df_mentions[BUBBLE_MENTIONS_DATE_COLUMN_NAME].dt.strftime(UI_DATE_FORMAT)
73
  except Exception as e:
74
  logging.error(f"Error formatting mention dates for display: {e}")
75
  html_parts.append("<p>Error formatting mention dates.</p>")
 
86
  if not monthly_gains.empty and FOLLOWER_STATS_CATEGORY_COLUMN in monthly_gains.columns and \
87
  FOLLOWER_STATS_ORGANIC_COLUMN in monthly_gains.columns and FOLLOWER_STATS_PAID_COLUMN in monthly_gains.columns:
88
  try:
 
89
  monthly_gains.loc[:, FOLLOWER_STATS_CATEGORY_COLUMN_DT] = pd.to_datetime(monthly_gains[FOLLOWER_STATS_CATEGORY_COLUMN], errors='coerce')
 
90
  monthly_gains_display = monthly_gains.sort_values(by=FOLLOWER_STATS_CATEGORY_COLUMN_DT, ascending=False)
91
+ latest_gain = monthly_gains_display.head(1).copy()
92
  if not latest_gain.empty:
93
+ latest_gain.loc[:, FOLLOWER_STATS_CATEGORY_COLUMN] = latest_gain[FOLLOWER_STATS_CATEGORY_COLUMN_DT].dt.strftime(UI_DATE_FORMAT)
94
+ html_parts.append("<h5>Latest Monthly Follower Gain:</h5>")
95
+ html_parts.append(latest_gain[[FOLLOWER_STATS_CATEGORY_COLUMN, FOLLOWER_STATS_ORGANIC_COLUMN, FOLLOWER_STATS_PAID_COLUMN]].to_html(escape=True, index=False, classes="table table-sm"))
96
  else:
97
  html_parts.append("<p>No valid monthly follower gain data to display after processing.</p>")
98
  except Exception as e:
 
128
  mentions_df_display = mentions_df.copy()
129
  if BUBBLE_MENTIONS_DATE_COLUMN_NAME in mentions_df_display.columns:
130
  try:
131
+ mentions_df_display[BUBBLE_MENTIONS_DATE_COLUMN_NAME] = pd.to_datetime(mentions_df_display[BUBBLE_MENTIONS_DATE_COLUMN_NAME], errors='coerce')
132
  mentions_df_display = mentions_df_display.sort_values(by=BUBBLE_MENTIONS_DATE_COLUMN_NAME, ascending=False)
133
+ mentions_df_display[BUBBLE_MENTIONS_DATE_COLUMN_NAME] = mentions_df_display[BUBBLE_MENTIONS_DATE_COLUMN_NAME].dt.strftime(UI_DATE_FORMAT)
134
  except Exception as e:
135
  logging.error(f"Error formatting mention dates for tab display: {e}")
136
  html_parts.append("<p>Error formatting mention dates.</p>")
 
141
  html_parts.append(mentions_df_display[display_columns].head(20).to_html(escape=False, index=False, classes="table table-sm"))
142
 
143
  mentions_html_output = "\n".join(html_parts)
144
+ fig = None # Initialize fig to None
145
  if not mentions_df.empty and "sentiment_label" in mentions_df.columns:
146
  try:
147
  fig_plot, ax = plt.subplots(figsize=(6,4))
 
151
  ax.set_ylabel("Count")
152
  plt.xticks(rotation=45, ha='right')
153
  plt.tight_layout()
154
+ fig = fig_plot # Assign the figure object
155
  logging.info("Mentions tab: Sentiment distribution plot generated.")
156
  except Exception as e:
157
  logging.error(f"Error generating mentions plot: {e}", exc_info=True)
158
+ # fig remains None
159
+ finally:
160
+ if fig is not None and fig is not plt: # Ensure we don't close the global plt if fig is just an alias
161
+ plt.close(fig) # Close the specific figure to free memory
162
+ elif fig is None and plt.get_fignums(): # If fig is None but some global figure might exist from error
163
+ plt.close('all')
164
+
165
 
166
  return mentions_html_output, fig
167
 
 
200
 
201
  html_parts.append("<h4>Monthly Follower Gains (Last 13 Months):</h4>")
202
  table_display_df = monthly_gains_df_sorted_table.copy()
203
+ table_display_df.loc[:,FOLLOWER_STATS_CATEGORY_COLUMN] = table_display_df[FOLLOWER_STATS_CATEGORY_COLUMN_DT].dt.strftime(UI_MONTH_FORMAT)
204
  html_parts.append(table_display_df[[FOLLOWER_STATS_CATEGORY_COLUMN, FOLLOWER_STATS_ORGANIC_COLUMN, FOLLOWER_STATS_PAID_COLUMN]].head(13).to_html(escape=True, index=False, classes="table table-sm"))
205
 
206
  monthly_gains_df_sorted_plot = monthly_gains_df.sort_values(by=FOLLOWER_STATS_CATEGORY_COLUMN_DT, ascending=True).copy()
 
207
  monthly_gains_df_sorted_plot.loc[:, '_plot_month'] = monthly_gains_df_sorted_plot[FOLLOWER_STATS_CATEGORY_COLUMN_DT].dt.strftime(UI_MONTH_FORMAT)
208
  plot_data = monthly_gains_df_sorted_plot.groupby('_plot_month').agg(
209
  organic=(FOLLOWER_STATS_ORGANIC_COLUMN, 'sum'),
210
  paid=(FOLLOWER_STATS_PAID_COLUMN, 'sum')
211
+ ).reset_index().sort_values(by='_plot_month') # Ensure month order for plot
 
212
 
213
  fig_gains, ax_gains = plt.subplots(figsize=(10,5))
214
  ax_gains.plot(plot_data['_plot_month'], plot_data['organic'], marker='o', linestyle='-', label='Organic Gain')
 
225
  except Exception as e:
226
  logging.error(f"Error processing or plotting monthly gains: {e}", exc_info=True)
227
  html_parts.append("<p>Error displaying monthly follower gain data.</p>")
228
+ finally:
229
+ if plot_monthly_gains is not None and plot_monthly_gains is not plt:
230
+ plt.close(plot_monthly_gains)
231
+ elif plot_monthly_gains is None and plt.get_fignums():
232
+ plt.close('all')
233
  else:
234
  html_parts.append("<p>No monthly follower gain data available or required columns missing.</p>")
235
  html_parts.append("<hr/>")
 
259
  except Exception as e:
260
  logging.error(f"Error processing or plotting seniority data: {e}", exc_info=True)
261
  html_parts.append("<p>Error displaying follower seniority data.</p>")
262
+ finally:
263
+ if plot_seniority_dist is not None and plot_seniority_dist is not plt:
264
+ plt.close(plot_seniority_dist)
265
+ elif plot_seniority_dist is None and plt.get_fignums():
266
+ plt.close('all')
267
  else:
268
  html_parts.append("<p>No follower seniority data available or required columns missing.</p>")
269
  html_parts.append("<hr/>")
 
293
  except Exception as e:
294
  logging.error(f"Error processing or plotting industry data: {e}", exc_info=True)
295
  html_parts.append("<p>Error displaying follower industry data.</p>")
296
+ finally:
297
+ if plot_industry_dist is not None and plot_industry_dist is not plt:
298
+ plt.close(plot_industry_dist)
299
+ elif plot_industry_dist is None and plt.get_fignums():
300
+ plt.close('all')
301
+
302
  else:
303
  html_parts.append("<p>No follower industry data available or required columns missing.</p>")
304
 
305
  html_parts.append("</div>")
306
  follower_html_output = "\n".join(html_parts)
307
  return follower_html_output, plot_monthly_gains, plot_seniority_dist, plot_industry_dist
308
+
309
+
310
+ # --- NEW UI GENERATION LOGIC FOR ANALYTICS TAB ---
311
+ def create_analytics_plot_ui_row(label, plot_id_str):
312
+ """
313
+ Creates a Gradio Row with a Plot component, a Bomb (insights) button,
314
+ and a hidden Column for insights.
315
+ """
316
+ # This function will be called within a gr.Blocks() context in app.py
317
+ with gr.Row(equal_height=False):
318
+ with gr.Column(scale=7): # Main column for the plot
319
+ plot_component = gr.Plot(label=label)
320
+ with gr.Column(scale=1, min_width=60): # Smaller column for the bomb button
321
+ bomb_button = gr.Button("💣", variant="secondary", size="sm", elem_id=f"bomb_{plot_id_str}")
322
+ # Insights column, initially hidden, appears to the right
323
+ with gr.Column(scale=4, visible=False, elem_id=f"insights_col_{plot_id_str}") as insights_col_ui:
324
+ gr.Markdown(f"##### Insights: {label}") # Title for the insights panel
325
+ insights_markdown_ui = gr.Markdown(f"Click 💣 for insights on {label}...") # Placeholder text
326
+ return plot_component, bomb_button, insights_col_ui, insights_markdown_ui
327
+
328
+ def build_analytics_tab_ui_components(plot_configs):
329
+ """
330
+ Builds and returns all UI components for the analytics plots based on plot_configs.
331
+ This function should be called within the "Analytics" TabItem context in app.py.
332
+ It will create the section headers and the plot rows.
333
+ """
334
+ logging.info(f"Building UI components for {len(plot_configs)} analytics plots.")
335
+ plot_ui_objects = {} # Stores {"plot_id": {"plot": gr.Plot, "bomb": gr.Button, "insights_col": gr.Column, "insights_md": gr.Markdown, "label": str}}
336
+
337
+ current_section_title = ""
338
+ for config in plot_configs:
339
+ if config["section"] != current_section_title:
340
+ gr.Markdown(f"### {config['section']}") # Create section header
341
+ current_section_title = config["section"]
342
+
343
+ # Create the plot row using the helper
344
+ plot_component, bomb_button, insights_col_ui, insights_markdown_ui = \
345
+ create_analytics_plot_ui_row(config["label"], config["id"])
346
+
347
+ # Store the created components in the dictionary
348
+ plot_ui_objects[config["id"]] = {
349
+ "plot": plot_component,
350
+ "bomb": bomb_button,
351
+ "insights_col": insights_col_ui,
352
+ "insights_md": insights_markdown_ui,
353
+ "label": config["label"] # Store label for easy access, e.g., for insights text
354
+ }
355
+ logging.debug(f"Created UI for plot_id: {config['id']} in section: {config['section']}")
356
+
357
+ logging.info(f"Finished building UI components for analytics tab. Total objects: {len(plot_ui_objects)}")
358
+ return plot_ui_objects