GuglielmoTor commited on
Commit
ddd95f0
Β·
verified Β·
1 Parent(s): fe8e3bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +260 -101
app.py CHANGED
@@ -20,7 +20,8 @@ from ui_generators import (
20
  display_main_dashboard,
21
  run_mentions_tab_display,
22
  run_follower_stats_tab_display,
23
- build_analytics_tab_plot_area # Import the updated UI builder
 
24
  )
25
  # Corrected import for analytics_data_processing
26
  from analytics_data_processing import prepare_filtered_analytics_data
@@ -114,7 +115,7 @@ def update_analytics_plots_figures(token_state_value, date_filter_option, custom
114
 
115
  final_plot_figs = []
116
  for i, p_fig in enumerate(plot_figs):
117
- if p_fig is not None and not isinstance(p_fig, str): # Check if it's a Matplotlib figure
118
  final_plot_figs.append(p_fig)
119
  else:
120
  logging.warning(f"Plot figure generation failed or returned unexpected type for slot {i}, using placeholder. Figure: {p_fig}")
@@ -175,11 +176,11 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
175
 
176
  with gr.TabItem("2️⃣ Analytics", id="tab_analytics"):
177
  gr.Markdown("## πŸ“ˆ LinkedIn Performance Analytics")
178
- gr.Markdown("Select a date range. Click πŸ’£ for insights.")
179
 
180
  analytics_status_md = gr.Markdown("Analytics status...")
181
 
182
- with gr.Row(): # Filters row
183
  date_filter_selector = gr.Radio(
184
  ["All Time", "Last 7 Days", "Last 30 Days", "Custom Range"],
185
  label="Select Date Range", value="Last 30 Days", scale=3
@@ -200,7 +201,6 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
200
  outputs=[custom_start_date_picker, custom_end_date_picker]
201
  )
202
 
203
- # --- Define plot configurations (Order must match figure generation) ---
204
  plot_configs = [
205
  {"label": "Posts Activity Over Time", "id": "posts_activity", "section": "Posts & Engagement Overview"},
206
  {"label": "Post Engagement Types", "id": "engagement_type", "section": "Posts & Engagement Overview"},
@@ -228,65 +228,219 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
228
  ]
229
  assert len(plot_configs) == 23, "Mismatch in plot_configs and expected plots."
230
 
231
- # --- Main layout for Analytics Tab: Plots Area and Global Insights Column ---
232
- with gr.Row(equal_height=False): # Main row for plots area and insights column
233
- with gr.Column(scale=8): # Column to hold all plot rows and section headers
234
- # Build the plot area (section headers and rows of plot panels)
235
- # This function is defined in ui_generators.py
236
- # It will create gr.Markdown for sections and gr.Row for plot pairs
 
 
237
  plot_ui_objects = build_analytics_tab_plot_area(plot_configs)
238
 
239
- # Global Insights Column (initially hidden)
240
- with gr.Column(scale=4, visible=False) as global_insights_column_ui:
241
- gr.Markdown("### πŸ’‘ Generated Insights")
242
- global_insights_markdown_ui = gr.Markdown("Click πŸ’£ on a plot to see insights here.")
243
 
244
- active_insight_plot_id_state = gr.State(None)
245
 
246
- # --- Bomb Button Click Handler ---
247
- def handle_bomb_click(plot_id_clicked, current_active_plot_id, token_state_val): # Added token_state_val
248
- logging.info(f"Bomb clicked for: {plot_id_clicked}. Currently active: {current_active_plot_id}")
249
 
250
- # Retrieve the label for the clicked plot
251
- clicked_plot_label = "Selected Plot" # Default
252
- if plot_id_clicked and plot_id_clicked in plot_ui_objects:
253
- clicked_plot_label = plot_ui_objects[plot_id_clicked]["label"]
254
-
255
- if plot_id_clicked == current_active_plot_id: # Toggle off
256
- new_active_id = None
257
- insight_text_update = f"Insights for {clicked_plot_label} hidden. Click πŸ’£ to show."
258
- insights_col_visible = False
259
- logging.info(f"Closing insights for {plot_id_clicked}")
260
- else: # Activate new one or switch
261
- new_active_id = plot_id_clicked
262
- # TODO: Implement actual insight generation logic here using plot_id_clicked and token_state_val
263
- insight_text_update = f"**Insights for: {clicked_plot_label}**\n\n"
264
- insight_text_update += f"Plot ID: `{plot_id_clicked}`.\n"
265
- insight_text_update += "This is where detailed, AI-generated insights for this specific chart would appear, based on its data and trends.\n"
266
- insight_text_update += "For instance, if this were 'Post Engagement Types', we might analyze which type is dominant and suggest content strategies."
267
- insights_col_visible = True
268
- logging.info(f"Opening insights for {plot_id_clicked}")
269
 
270
- return gr.update(visible=insights_col_visible), gr.update(value=insight_text_update), new_active_id
271
-
272
- # --- Connect Bomb Buttons ---
273
- # Outputs for each bomb click: global insights column visibility, its markdown content, and the state
274
- bomb_click_outputs = [global_insights_column_ui, global_insights_markdown_ui, active_insight_plot_id_state]
275
-
276
- for config in plot_configs:
277
- plot_id = config["id"]
278
- if plot_id in plot_ui_objects: # Ensure the UI object was created
279
- components_dict = plot_ui_objects[plot_id]
280
- components_dict["bomb_button"].click(
281
- fn=handle_bomb_click,
282
- inputs=[gr.State(value=plot_id), active_insight_plot_id_state, token_state], # Pass token_state
283
- outputs=bomb_click_outputs,
284
- api_name=f"show_insights_{plot_id}"
285
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
 
287
- # --- Function to Refresh All Analytics UI (Plots + Reset Global Insights) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  def refresh_all_analytics_ui_elements(current_token_state, date_filter_val, custom_start_val, custom_end_val):
289
- logging.info("Refreshing all analytics UI elements and resetting insights.")
290
  plot_generation_results = update_analytics_plots_figures(
291
  current_token_state, date_filter_val, custom_start_val, custom_end_val
292
  )
@@ -296,53 +450,67 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
296
 
297
  all_updates = [status_message_update] # For analytics_status_md
298
 
299
- # Plot figure updates - iterate based on plot_configs to ensure order
300
  for i, config in enumerate(plot_configs):
301
  p_id_key = config["id"]
302
- if p_id_key in plot_ui_objects: # Check if plot UI exists
303
  if i < len(generated_plot_figures):
304
  all_updates.append(generated_plot_figures[i])
305
  else:
306
- logging.error(f"Mismatch: Expected figure for {p_id_key} but not enough figures generated.")
307
  all_updates.append(create_placeholder_plot("Figure Error", f"No figure for {p_id_key}"))
308
  else:
309
- # This case should ideally not happen if plot_configs and plot_ui_objects are in sync
310
- logging.warning(f"Plot UI object for id {p_id_key} not found during refresh. Skipping its figure update.")
311
-
312
 
313
- # Reset Global Insights Column
314
- all_updates.append(gr.update(visible=False)) # Hide global_insights_column_ui
315
- all_updates.append(gr.update(value="Click πŸ’£ on a plot to see insights here.")) # Reset global_insights_markdown_ui
316
- all_updates.append(None) # Reset active_insight_plot_id_state
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
 
318
  logging.info(f"Prepared {len(all_updates)} updates for analytics refresh.")
319
  return all_updates
320
 
321
  # --- Define outputs for the apply_filter_btn and sync.then() ---
322
  apply_filter_and_sync_outputs = [analytics_status_md]
323
- # Add plot components (must be in the order of plot_configs)
324
- for config in plot_configs:
325
- p_id_key = config["id"]
326
- if p_id_key in plot_ui_objects:
327
- apply_filter_and_sync_outputs.append(plot_ui_objects[p_id_key]["plot_component"])
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  else:
329
- # Add a placeholder None if a plot component wasn't created, to maintain output list length.
330
- # This helps prevent errors if plot_ui_objects somehow doesn't contain an expected key.
331
- apply_filter_and_sync_outputs.append(None)
332
- logging.error(f"Plot component for {p_id_key} missing in plot_ui_objects for apply_filter_outputs.")
333
 
334
 
335
- # Add global insights components and state
336
- apply_filter_and_sync_outputs.extend([
337
- global_insights_column_ui,
338
- global_insights_markdown_ui,
339
- active_insight_plot_id_state
340
- ])
341
 
342
  logging.info(f"Total outputs for apply_filter/sync: {len(apply_filter_and_sync_outputs)}")
343
 
344
-
345
- # --- Connect Apply Filter Button ---
346
  apply_filter_btn.click(
347
  fn=refresh_all_analytics_ui_elements,
348
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker],
@@ -351,9 +519,8 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
351
  )
352
 
353
  with gr.TabItem("3️⃣ Mentions", id="tab_mentions"):
354
- # ... (Mentions tab content remains the same) ...
355
- refresh_mentions_display_btn = gr.Button("πŸ”„ Refresh Mentions Display (from local data)", variant="secondary")
356
- mentions_html = gr.HTML("Mentions data loads from Bubble after sync. Click refresh to view current local data.")
357
  mentions_sentiment_dist_plot = gr.Plot(label="Mention Sentiment Distribution")
358
  refresh_mentions_display_btn.click(
359
  fn=run_mentions_tab_display, inputs=[token_state],
@@ -362,9 +529,8 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
362
  )
363
 
364
  with gr.TabItem("4️⃣ Follower Stats", id="tab_follower_stats"):
365
- # ... (Follower Stats tab content remains the same) ...
366
- refresh_follower_stats_btn = gr.Button("πŸ”„ Refresh Follower Stats Display (from local data)", variant="secondary")
367
- follower_stats_html = gr.HTML("Follower statistics load from Bubble after sync. Click refresh to view current local data.")
368
  with gr.Row():
369
  fs_plot_monthly_gains = gr.Plot(label="Monthly Follower Gains")
370
  with gr.Row():
@@ -377,31 +543,23 @@ with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue", secondary_hue="sky"),
377
  show_progress="full"
378
  )
379
 
380
- # --- Define the full sync_click_event chain HERE ---
381
  sync_event_part1 = sync_data_btn.click(
382
  fn=sync_all_linkedin_data_orchestrator,
383
- inputs=[token_state],
384
- outputs=[sync_status_html_output, token_state],
385
- show_progress="full"
386
  )
387
  sync_event_part2 = sync_event_part1.then(
388
  fn=process_and_store_bubble_token,
389
  inputs=[url_user_token_display, org_urn_display, token_state],
390
- outputs=[status_box, token_state, sync_data_btn],
391
- show_progress=False
392
  )
393
  sync_event_part3 = sync_event_part2.then(
394
  fn=display_main_dashboard,
395
- inputs=[token_state],
396
- outputs=[dashboard_display_html],
397
- show_progress=False
398
  )
399
- # Connect to refresh analytics UI after sync
400
  sync_event_final = sync_event_part3.then(
401
  fn=refresh_all_analytics_ui_elements,
402
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker],
403
- outputs=apply_filter_and_sync_outputs,
404
- show_progress="full"
405
  )
406
 
407
  if __name__ == "__main__":
@@ -418,3 +576,4 @@ if __name__ == "__main__":
418
  logging.error("Matplotlib is not installed. Plots will not be generated.")
419
 
420
  app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
 
 
20
  display_main_dashboard,
21
  run_mentions_tab_display,
22
  run_follower_stats_tab_display,
23
+ build_analytics_tab_plot_area, # Import the updated UI builder
24
+ BOMB_ICON, EXPLORE_ICON, FORMULA_ICON, ACTIVE_ICON # Import icons
25
  )
26
  # Corrected import for analytics_data_processing
27
  from analytics_data_processing import prepare_filtered_analytics_data
 
115
 
116
  final_plot_figs = []
117
  for i, p_fig in enumerate(plot_figs):
118
+ if p_fig is not None and not isinstance(p_fig, str):
119
  final_plot_figs.append(p_fig)
120
  else:
121
  logging.warning(f"Plot figure generation failed or returned unexpected type for slot {i}, using placeholder. Figure: {p_fig}")
 
176
 
177
  with gr.TabItem("2️⃣ Analytics", id="tab_analytics"):
178
  gr.Markdown("## πŸ“ˆ LinkedIn Performance Analytics")
179
+ gr.Markdown("Select a date range. Click buttons for actions.")
180
 
181
  analytics_status_md = gr.Markdown("Analytics status...")
182
 
183
+ with gr.Row():
184
  date_filter_selector = gr.Radio(
185
  ["All Time", "Last 7 Days", "Last 30 Days", "Custom Range"],
186
  label="Select Date Range", value="Last 30 Days", scale=3
 
201
  outputs=[custom_start_date_picker, custom_end_date_picker]
202
  )
203
 
 
204
  plot_configs = [
205
  {"label": "Posts Activity Over Time", "id": "posts_activity", "section": "Posts & Engagement Overview"},
206
  {"label": "Post Engagement Types", "id": "engagement_type", "section": "Posts & Engagement Overview"},
 
228
  ]
229
  assert len(plot_configs) == 23, "Mismatch in plot_configs and expected plots."
230
 
231
+ # --- State for Analytics Tab interactivity ---
232
+ # Stores {"plot_id": "action_type"} e.g. {"posts_activity": "insights"} or None
233
+ active_panel_action_state = gr.State(None)
234
+ # Stores plot_id of the currently explored plot, or None
235
+ explored_plot_id_state = gr.State(None)
236
+
237
+ with gr.Row(equal_height=False):
238
+ with gr.Column(scale=8) as plots_area_col:
239
  plot_ui_objects = build_analytics_tab_plot_area(plot_configs)
240
 
241
+ with gr.Column(scale=4, visible=False) as global_actions_column_ui: # Renamed for clarity
242
+ gr.Markdown("### πŸ’‘ Generated Content") # More generic title
243
+ global_actions_markdown_ui = gr.Markdown("Click a button (πŸ’£, Ζ’) on a plot to see content here.")
 
244
 
 
245
 
246
+ # --- Event Handler for Insights and Formula Buttons ---
247
+ def handle_panel_action(plot_id_clicked, action_type, current_active_action, token_state_val):
248
+ logging.info(f"Action '{action_type}' for plot: {plot_id_clicked}. Current active: {current_active_action}")
249
 
250
+ clicked_plot_label = plot_ui_objects.get(plot_id_clicked, {}).get("label", "Selected Plot")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
+ # Determine new state for the action panel
253
+ new_active_action_state = {"plot_id": plot_id_clicked, "type": action_type}
254
+ is_toggling_off = current_active_action == new_active_action_state
255
+
256
+ content_text = ""
257
+ action_col_visible = False
258
+
259
+ # Button icon updates - prepare a list of gr.update for all buttons
260
+ button_updates = {} # Dict to hold updates for gr.Button objects
261
+
262
+ if is_toggling_off:
263
+ new_active_action_state = None
264
+ content_text = f"{action_type.capitalize()} for {clicked_plot_label} hidden."
265
+ action_col_visible = False
266
+ logging.info(f"Closing {action_type} panel for {plot_id_clicked}")
267
+ # Reset the clicked button to its original icon
268
+ if action_type == "insights":
269
+ button_updates[plot_ui_objects[plot_id_clicked]["bomb_button"]] = gr.update(value=BOMB_ICON)
270
+ elif action_type == "formula":
271
+ button_updates[plot_ui_objects[plot_id_clicked]["formula_button"]] = gr.update(value=FORMULA_ICON)
272
+ else: # Activating or switching
273
+ action_col_visible = True
274
+ if action_type == "insights":
275
+ # TODO: Implement actual insight generation
276
+ content_text = f"**Insights for: {clicked_plot_label}**\n\nPlot ID: `{plot_id_clicked}`.\nDetailed AI insights here."
277
+ button_updates[plot_ui_objects[plot_id_clicked]["bomb_button"]] = gr.update(value=ACTIVE_ICON)
278
+ elif action_type == "formula":
279
+ # TODO: Implement actual formula display
280
+ content_text = f"**Formula/Methodology for: {clicked_plot_label}**\n\nPlot ID: `{plot_id_clicked}`.\nMethodology details here."
281
+ button_updates[plot_ui_objects[plot_id_clicked]["formula_button"]] = gr.update(value=ACTIVE_ICON)
282
+
283
+ logging.info(f"Opening {action_type} panel for {plot_id_clicked}")
284
+
285
+ # Reset other action buttons for the *same* plot if a new action is chosen for it
286
+ # And reset all buttons if switching plots or toggling off
287
+ for pid, ui_obj in plot_ui_objects.items():
288
+ is_current_plot_active_bomb = new_active_action_state == {"plot_id": pid, "type": "insights"}
289
+ is_current_plot_active_formula = new_active_action_state == {"plot_id": pid, "type": "formula"}
290
+
291
+ if ui_obj["bomb_button"] not in button_updates: # Avoid overwriting the primary clicked button's update
292
+ button_updates[ui_obj["bomb_button"]] = gr.update(value=BOMB_ICON if not is_current_plot_active_bomb else ACTIVE_ICON)
293
+ if ui_obj["formula_button"] not in button_updates:
294
+ button_updates[ui_obj["formula_button"]] = gr.update(value=FORMULA_ICON if not is_current_plot_active_formula else ACTIVE_ICON)
295
+
296
+ # Construct the list of updates for Gradio
297
+ # Order: global_actions_column_ui, global_actions_markdown_ui, active_panel_action_state, then all button updates
298
+ final_updates = [
299
+ gr.update(visible=action_col_visible),
300
+ gr.update(value=content_text),
301
+ new_active_action_state
302
+ ]
303
+ # Add button updates in a consistent order (e.g., order of plot_configs)
304
+ for cfg in plot_configs:
305
+ p_id = cfg["id"]
306
+ if p_id in plot_ui_objects:
307
+ # Append updates for bomb, explore, formula buttons for this plot_id
308
+ # Ensure explore button is handled by its own handler or reset here if needed
309
+ if plot_ui_objects[p_id]["bomb_button"] in button_updates:
310
+ final_updates.append(button_updates[plot_ui_objects[p_id]["bomb_button"]])
311
+ else: # Default reset if not specifically handled
312
+ final_updates.append(gr.update(value=BOMB_ICON))
313
+
314
+ # Explore button is handled separately, but ensure it's in the output list
315
+ # For now, we'll let its own handler manage its state, or reset it if another action occurs.
316
+ # This part needs careful thought for interactions between explore and other actions.
317
+ # Let's assume for now that activating insights/formula doesn't auto-close explore.
318
+ # We will need to add explore_button to the output list.
319
+ # For now, this handler focuses on insights/formula buttons.
320
+ # We'll need to expand the output list.
321
+
322
+ if plot_ui_objects[p_id]["formula_button"] in button_updates:
323
+ final_updates.append(button_updates[plot_ui_objects[p_id]["formula_button"]])
324
+ else: # Default reset
325
+ final_updates.append(gr.update(value=FORMULA_ICON))
326
+
327
+ return final_updates
328
+
329
+ # --- Event Handler for Explore Button ---
330
+ def handle_explore_click(plot_id_clicked, current_explored_plot_id):
331
+ logging.info(f"Explore clicked for: {plot_id_clicked}. Currently explored: {current_explored_plot_id}")
332
+
333
+ panel_visibility_updates = {} # {panel_component: gr.update}
334
+ button_icon_updates = {} # {button_component: gr.update}
335
+ new_explored_id = None
336
+
337
+ is_toggling_off = (plot_id_clicked == current_explored_plot_id)
338
+
339
+ if is_toggling_off:
340
+ new_explored_id = None
341
+ button_icon_updates[plot_ui_objects[plot_id_clicked]["explore_button"]] = gr.update(value=EXPLORE_ICON)
342
+ logging.info(f"Un-exploring plot: {plot_id_clicked}")
343
+ else:
344
+ new_explored_id = plot_id_clicked
345
+ button_icon_updates[plot_ui_objects[plot_id_clicked]["explore_button"]] = gr.update(value=ACTIVE_ICON)
346
+ # If another plot was explored, reset its button
347
+ if current_explored_plot_id and current_explored_plot_id != plot_id_clicked:
348
+ button_icon_updates[plot_ui_objects[current_explored_plot_id]["explore_button"]] = gr.update(value=EXPLORE_ICON)
349
+ logging.info(f"Exploring plot: {plot_id_clicked}")
350
+
351
+ # Determine visibility for all panels
352
+ for i in range(0, len(plot_configs), 2):
353
+ config1_id = plot_configs[i]["id"]
354
+ panel1 = plot_ui_objects[config1_id]["panel_component"]
355
+
356
+ show_panel1 = True
357
+ if new_explored_id: # If any plot is explored
358
+ show_panel1 = (config1_id == new_explored_id)
359
+ panel_visibility_updates[panel1] = gr.update(visible=show_panel1)
360
+
361
+ if i + 1 < len(plot_configs):
362
+ config2_id = plot_configs[i+1]["id"]
363
+ # Only process second panel if it's in the same section as the first
364
+ if plot_configs[i+1]["section"] == plot_configs[i]["section"]:
365
+ panel2 = plot_ui_objects[config2_id]["panel_component"]
366
+ show_panel2 = True
367
+ if new_explored_id: # If any plot is explored
368
+ show_panel2 = (config2_id == new_explored_id)
369
+ # If panel1 is explored, panel2 in the same row should be hidden unless panel2 is the one being explored
370
+ if config1_id == new_explored_id and config2_id != new_explored_id :
371
+ show_panel2 = False
372
+ elif config2_id == new_explored_id and config1_id != new_explored_id:
373
+ show_panel1 = False # Re-evaluate panel1 if panel2 is explored
374
+ panel_visibility_updates[panel1] = gr.update(visible=show_panel1)
375
+
376
+
377
+ panel_visibility_updates[panel2] = gr.update(visible=show_panel2)
378
+ # else: panel2 is in a new section, will be handled when its turn comes as config1_id
379
+
380
+ # Construct final updates list
381
+ # Order: explored_plot_id_state, then all panel visibility, then all explore button icons
382
+ final_updates = [new_explored_id]
383
+ for cfg in plot_configs:
384
+ p_id = cfg["id"]
385
+ if p_id in plot_ui_objects:
386
+ panel = plot_ui_objects[p_id]["panel_component"]
387
+ final_updates.append(panel_visibility_updates.get(panel, gr.update())) # Default no change if not in updates
388
+
389
+ explore_btn = plot_ui_objects[p_id]["explore_button"]
390
+ final_updates.append(button_icon_updates.get(explore_btn, gr.update(value=EXPLORE_ICON))) # Default to original icon
391
+
392
+ return final_updates
393
+
394
+ # --- Connect Action Buttons ---
395
+ # Outputs for Insights/Formula: global_actions_column_ui, global_actions_markdown_ui, active_panel_action_state, + all bomb_buttons, + all formula_buttons
396
+ action_buttons_outputs = [
397
+ global_actions_column_ui,
398
+ global_actions_markdown_ui,
399
+ active_panel_action_state
400
+ ]
401
+ # Add all bomb and formula buttons to the output list for icon updates
402
+ for cfg in plot_configs:
403
+ pid = cfg["id"]
404
+ if pid in plot_ui_objects:
405
+ action_buttons_outputs.append(plot_ui_objects[pid]["bomb_button"])
406
+ action_buttons_outputs.append(plot_ui_objects[pid]["formula_button"])
407
 
408
+ # Outputs for Explore: explored_plot_id_state, + all panel_components, + all explore_buttons
409
+ explore_buttons_outputs = [explored_plot_id_state]
410
+ for cfg in plot_configs:
411
+ pid = cfg["id"]
412
+ if pid in plot_ui_objects:
413
+ explore_buttons_outputs.append(plot_ui_objects[pid]["panel_component"])
414
+ explore_buttons_outputs.append(plot_ui_objects[pid]["explore_button"])
415
+
416
+
417
+ for config_item in plot_configs:
418
+ plot_id = config_item["id"]
419
+ if plot_id in plot_ui_objects:
420
+ ui_obj = plot_ui_objects[plot_id]
421
+
422
+ ui_obj["bomb_button"].click(
423
+ fn=lambda p_id=plot_id: handle_panel_action(p_id, "insights", active_panel_action_state.value, token_state.value), # Use .value for state in lambda
424
+ inputs=None, # Inputs are passed via lambda closure and state.value
425
+ outputs=action_buttons_outputs,
426
+ api_name=f"action_insights_{plot_id}"
427
+ )
428
+ ui_obj["formula_button"].click(
429
+ fn=lambda p_id=plot_id: handle_panel_action(p_id, "formula", active_panel_action_state.value, token_state.value),
430
+ inputs=None,
431
+ outputs=action_buttons_outputs,
432
+ api_name=f"action_formula_{plot_id}"
433
+ )
434
+ ui_obj["explore_button"].click(
435
+ fn=lambda p_id=plot_id: handle_explore_click(p_id, explored_plot_id_state.value),
436
+ inputs=None,
437
+ outputs=explore_buttons_outputs,
438
+ api_name=f"action_explore_{plot_id}"
439
+ )
440
+
441
+ # --- Function to Refresh All Analytics UI ---
442
  def refresh_all_analytics_ui_elements(current_token_state, date_filter_val, custom_start_val, custom_end_val):
443
+ logging.info("Refreshing all analytics UI elements and resetting actions.")
444
  plot_generation_results = update_analytics_plots_figures(
445
  current_token_state, date_filter_val, custom_start_val, custom_end_val
446
  )
 
450
 
451
  all_updates = [status_message_update] # For analytics_status_md
452
 
453
+ # Plot figure updates
454
  for i, config in enumerate(plot_configs):
455
  p_id_key = config["id"]
456
+ if p_id_key in plot_ui_objects:
457
  if i < len(generated_plot_figures):
458
  all_updates.append(generated_plot_figures[i])
459
  else:
 
460
  all_updates.append(create_placeholder_plot("Figure Error", f"No figure for {p_id_key}"))
461
  else:
462
+ all_updates.append(None) # Placeholder if UI object doesn't exist
 
 
463
 
464
+ # Reset Global Action Column
465
+ all_updates.append(gr.update(visible=False))
466
+ all_updates.append(gr.update(value="Click a button (πŸ’£, Ζ’) on a plot..."))
467
+ all_updates.append(None) # Reset active_panel_action_state
468
+
469
+ # Reset all button icons (bomb, formula, explore) and panel visibility for explore
470
+ for cfg in plot_configs:
471
+ pid = cfg["id"]
472
+ if pid in plot_ui_objects:
473
+ all_updates.append(gr.update(value=BOMB_ICON)) # bomb_button
474
+ all_updates.append(gr.update(value=FORMULA_ICON)) # formula_button
475
+ all_updates.append(gr.update(value=EXPLORE_ICON)) # explore_button
476
+ all_updates.append(gr.update(visible=True)) # panel_component (for explore reset)
477
+ else: # Add placeholders if UI object doesn't exist to keep list length
478
+ all_updates.extend([None,None,None,None])
479
+
480
+
481
+ all_updates.append(None) # Reset explored_plot_id_state
482
 
483
  logging.info(f"Prepared {len(all_updates)} updates for analytics refresh.")
484
  return all_updates
485
 
486
  # --- Define outputs for the apply_filter_btn and sync.then() ---
487
  apply_filter_and_sync_outputs = [analytics_status_md]
488
+ for config in plot_configs: # Plot components
489
+ pid = config["id"]
490
+ apply_filter_and_sync_outputs.append(plot_ui_objects[pid]["plot_component"] if pid in plot_ui_objects else None)
491
+
492
+ apply_filter_and_sync_outputs.extend([ # Global action column and its state
493
+ global_actions_column_ui,
494
+ global_actions_markdown_ui,
495
+ active_panel_action_state
496
+ ])
497
+
498
+ # Add all button components and panel components for reset
499
+ for cfg in plot_configs:
500
+ pid = cfg["id"]
501
+ if pid in plot_ui_objects:
502
+ apply_filter_and_sync_outputs.append(plot_ui_objects[pid]["bomb_button"])
503
+ apply_filter_and_sync_outputs.append(plot_ui_objects[pid]["formula_button"])
504
+ apply_filter_and_sync_outputs.append(plot_ui_objects[pid]["explore_button"])
505
+ apply_filter_and_sync_outputs.append(plot_ui_objects[pid]["panel_component"])
506
  else:
507
+ apply_filter_and_sync_outputs.extend([None,None,None,None])
 
 
 
508
 
509
 
510
+ apply_filter_and_sync_outputs.append(explored_plot_id_state)
 
 
 
 
 
511
 
512
  logging.info(f"Total outputs for apply_filter/sync: {len(apply_filter_and_sync_outputs)}")
513
 
 
 
514
  apply_filter_btn.click(
515
  fn=refresh_all_analytics_ui_elements,
516
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker],
 
519
  )
520
 
521
  with gr.TabItem("3️⃣ Mentions", id="tab_mentions"):
522
+ refresh_mentions_display_btn = gr.Button("πŸ”„ Refresh Mentions Display", variant="secondary")
523
+ mentions_html = gr.HTML("Mentions data...")
 
524
  mentions_sentiment_dist_plot = gr.Plot(label="Mention Sentiment Distribution")
525
  refresh_mentions_display_btn.click(
526
  fn=run_mentions_tab_display, inputs=[token_state],
 
529
  )
530
 
531
  with gr.TabItem("4️⃣ Follower Stats", id="tab_follower_stats"):
532
+ refresh_follower_stats_btn = gr.Button("πŸ”„ Refresh Follower Stats Display", variant="secondary")
533
+ follower_stats_html = gr.HTML("Follower statistics...")
 
534
  with gr.Row():
535
  fs_plot_monthly_gains = gr.Plot(label="Monthly Follower Gains")
536
  with gr.Row():
 
543
  show_progress="full"
544
  )
545
 
 
546
  sync_event_part1 = sync_data_btn.click(
547
  fn=sync_all_linkedin_data_orchestrator,
548
+ inputs=[token_state], outputs=[sync_status_html_output, token_state], show_progress="full"
 
 
549
  )
550
  sync_event_part2 = sync_event_part1.then(
551
  fn=process_and_store_bubble_token,
552
  inputs=[url_user_token_display, org_urn_display, token_state],
553
+ outputs=[status_box, token_state, sync_data_btn], show_progress=False
 
554
  )
555
  sync_event_part3 = sync_event_part2.then(
556
  fn=display_main_dashboard,
557
+ inputs=[token_state], outputs=[dashboard_display_html], show_progress=False
 
 
558
  )
 
559
  sync_event_final = sync_event_part3.then(
560
  fn=refresh_all_analytics_ui_elements,
561
  inputs=[token_state, date_filter_selector, custom_start_date_picker, custom_end_date_picker],
562
+ outputs=apply_filter_and_sync_outputs, show_progress="full"
 
563
  )
564
 
565
  if __name__ == "__main__":
 
576
  logging.error("Matplotlib is not installed. Plots will not be generated.")
577
 
578
  app.launch(server_name="0.0.0.0", server_port=7860, debug=True)
579
+