Spaces:
Running
Running
Update ui_generators.py
Browse files- ui_generators.py +52 -58
ui_generators.py
CHANGED
@@ -24,10 +24,11 @@ from config import (
|
|
24 |
# logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
|
25 |
|
26 |
# --- Constants for Button Icons/Text ---
|
|
|
27 |
BOMB_ICON = "💣"
|
28 |
EXPLORE_ICON = "🧭"
|
29 |
FORMULA_ICON = "ƒ"
|
30 |
-
ACTIVE_ICON = "❌ Close"
|
31 |
|
32 |
|
33 |
def display_main_dashboard(token_state):
|
@@ -151,23 +152,23 @@ def run_mentions_tab_display(token_state):
|
|
151 |
fig_plot_local = None
|
152 |
if not mentions_df.empty and "sentiment_label" in mentions_df.columns:
|
153 |
try:
|
154 |
-
fig_plot_local, ax = plt.subplots(figsize=(6,4))
|
155 |
sentiment_counts = mentions_df["sentiment_label"].value_counts()
|
156 |
sentiment_counts.plot(kind='bar', ax=ax, color=['#4CAF50', '#FFC107', '#F44336', '#9E9E9E', '#2196F3'])
|
157 |
-
ax.set_title("Mention Sentiment Distribution", y=1.03)
|
158 |
ax.set_ylabel("Count")
|
159 |
plt.xticks(rotation=45, ha='right')
|
160 |
|
161 |
plt.tight_layout()
|
162 |
-
fig_plot_local.subplots_adjust(top=0.90)
|
163 |
-
# This pushes plot content down from Gradio's label
|
164 |
fig = fig_plot_local
|
165 |
logging.info("Mentions tab: Sentiment distribution plot generated.")
|
166 |
except Exception as e:
|
167 |
logging.error(f"Error generating mentions plot: {e}", exc_info=True)
|
168 |
fig = None
|
169 |
finally:
|
170 |
-
|
|
|
171 |
plt.close(fig_plot_local)
|
172 |
return mentions_html_output, fig
|
173 |
|
@@ -216,14 +217,14 @@ def run_follower_stats_tab_display(token_state):
|
|
216 |
organic=(FOLLOWER_STATS_ORGANIC_COLUMN, 'sum'),
|
217 |
paid=(FOLLOWER_STATS_PAID_COLUMN, 'sum')
|
218 |
).reset_index()
|
219 |
-
plot_data['_plot_month_dt'] = pd.to_datetime(plot_data['_plot_month'], format=UI_MONTH_FORMAT)
|
220 |
plot_data = plot_data.sort_values(by='_plot_month_dt')
|
221 |
|
222 |
|
223 |
-
fig_gains_local, ax_gains = plt.subplots(figsize=(10,5))
|
224 |
ax_gains.plot(plot_data['_plot_month'], plot_data['organic'], marker='o', linestyle='-', label='Organic Gain')
|
225 |
ax_gains.plot(plot_data['_plot_month'], plot_data['paid'], marker='x', linestyle='--', label='Paid Gain')
|
226 |
-
ax_gains.set_title("Monthly Follower Gains Over Time", y=1.03)
|
227 |
ax_gains.set_ylabel("Follower Count")
|
228 |
ax_gains.set_xlabel("Month (YYYY-MM)")
|
229 |
plt.xticks(rotation=45, ha='right')
|
@@ -231,7 +232,7 @@ def run_follower_stats_tab_display(token_state):
|
|
231 |
plt.grid(True, linestyle='--', alpha=0.7)
|
232 |
|
233 |
plt.tight_layout()
|
234 |
-
fig_gains_local.subplots_adjust(top=0.90)
|
235 |
plot_monthly_gains = fig_gains_local
|
236 |
logging.info("Follower stats tab: Monthly gains plot generated.")
|
237 |
else:
|
@@ -259,16 +260,16 @@ def run_follower_stats_tab_display(token_state):
|
|
259 |
html_parts.append("<h4>Followers by Seniority (Top 10 Organic):</h4>")
|
260 |
html_parts.append(seniority_df_sorted[[FOLLOWER_STATS_CATEGORY_COLUMN, FOLLOWER_STATS_ORGANIC_COLUMN, FOLLOWER_STATS_PAID_COLUMN]].head(10).to_html(escape=True, index=False, classes="table table-sm"))
|
261 |
|
262 |
-
fig_seniority_local, ax_seniority = plt.subplots(figsize=(8,5))
|
263 |
top_n_seniority = seniority_df_sorted.nlargest(10, FOLLOWER_STATS_ORGANIC_COLUMN)
|
264 |
ax_seniority.bar(top_n_seniority[FOLLOWER_STATS_CATEGORY_COLUMN], top_n_seniority[FOLLOWER_STATS_ORGANIC_COLUMN], color='skyblue')
|
265 |
-
ax_seniority.set_title("Follower Distribution by Seniority (Top 10 Organic)", y=1.03)
|
266 |
ax_seniority.set_ylabel("Organic Follower Count")
|
267 |
plt.xticks(rotation=45, ha='right')
|
268 |
plt.grid(axis='y', linestyle='--', alpha=0.7)
|
269 |
|
270 |
plt.tight_layout()
|
271 |
-
fig_seniority_local.subplots_adjust(top=0.88)
|
272 |
plot_seniority_dist = fig_seniority_local
|
273 |
logging.info("Follower stats tab: Seniority distribution plot generated.")
|
274 |
else:
|
@@ -295,16 +296,16 @@ def run_follower_stats_tab_display(token_state):
|
|
295 |
html_parts.append("<h4>Followers by Industry (Top 10 Organic):</h4>")
|
296 |
html_parts.append(industry_df_sorted[[FOLLOWER_STATS_CATEGORY_COLUMN, FOLLOWER_STATS_ORGANIC_COLUMN, FOLLOWER_STATS_PAID_COLUMN]].head(10).to_html(escape=True, index=False, classes="table table-sm"))
|
297 |
|
298 |
-
fig_industry_local, ax_industry = plt.subplots(figsize=(8,5))
|
299 |
top_n_industry = industry_df_sorted.nlargest(10, FOLLOWER_STATS_ORGANIC_COLUMN)
|
300 |
ax_industry.bar(top_n_industry[FOLLOWER_STATS_CATEGORY_COLUMN], top_n_industry[FOLLOWER_STATS_ORGANIC_COLUMN], color='lightcoral')
|
301 |
-
ax_industry.set_title("Follower Distribution by Industry (Top 10 Organic)", y=1.03)
|
302 |
ax_industry.set_ylabel("Organic Follower Count")
|
303 |
plt.xticks(rotation=45, ha='right')
|
304 |
plt.grid(axis='y', linestyle='--', alpha=0.7)
|
305 |
|
306 |
plt.tight_layout()
|
307 |
-
fig_industry_local.subplots_adjust(top=0.88)
|
308 |
plot_industry_dist = fig_industry_local
|
309 |
logging.info("Follower stats tab: Industry distribution plot generated.")
|
310 |
else:
|
@@ -322,38 +323,30 @@ def run_follower_stats_tab_display(token_state):
|
|
322 |
follower_html_output = "\n".join(html_parts)
|
323 |
return follower_html_output, plot_monthly_gains, plot_seniority_dist, plot_industry_dist
|
324 |
|
|
|
325 |
def create_analytics_plot_panel(plot_label_str, plot_id_str):
|
326 |
"""
|
327 |
Creates an individual plot panel with its plot component and action buttons.
|
328 |
Plot title and action buttons are on the same row.
|
329 |
Returns the panel (Column), plot component, and button components.
|
330 |
"""
|
331 |
-
#
|
332 |
-
#
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
# For robustness, using string literals if not found.
|
337 |
-
from config import BOMB_ICON, EXPLORE_ICON, FORMULA_ICON
|
338 |
-
except ImportError:
|
339 |
-
logging.warning("Icons BOMB_ICON, EXPLORE_ICON, FORMULA_ICON not found in config for ui_generators, using defaults.")
|
340 |
-
BOMB_ICON = "💣"
|
341 |
-
EXPLORE_ICON = "🧭"
|
342 |
-
FORMULA_ICON = "ƒ"
|
343 |
-
|
344 |
with gr.Column(visible=True) as panel_component: # Main container for this plot
|
345 |
-
with gr.Row(variant="compact"):
|
346 |
-
|
347 |
-
gr.
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
formula_button = gr.Button(value=FORMULA_ICON, variant="secondary", size="sm", min_width=30, elem_id=f"formula_btn_{plot_id_str}")
|
352 |
-
explore_button = gr.Button(value=EXPLORE_ICON, variant="secondary", size="sm", min_width=30, elem_id=f"explore_btn_{plot_id_str}")
|
353 |
|
354 |
-
|
|
|
355 |
|
356 |
-
logging.debug(f"Created analytics panel
|
357 |
return panel_component, plot_component, bomb_button, explore_button, formula_button
|
358 |
|
359 |
|
@@ -366,7 +359,7 @@ def build_analytics_tab_plot_area(plot_configs):
|
|
366 |
- plot_ui_objects (dict): Dictionary of plot UI objects.
|
367 |
- section_titles_map (dict): Dictionary mapping section names to their gr.Markdown title components.
|
368 |
"""
|
369 |
-
logging.info(f"Building plot area for {len(plot_configs)} analytics plots with interleaved section titles
|
370 |
plot_ui_objects = {}
|
371 |
section_titles_map = {}
|
372 |
|
@@ -377,37 +370,35 @@ def build_analytics_tab_plot_area(plot_configs):
|
|
377 |
current_plot_config = plot_configs[idx]
|
378 |
current_section_name = current_plot_config["section"]
|
379 |
|
|
|
380 |
if current_section_name != last_rendered_section:
|
381 |
if current_section_name not in section_titles_map:
|
|
|
382 |
section_md_component = gr.Markdown(f"### {current_section_name}", visible=True)
|
383 |
section_titles_map[current_section_name] = section_md_component
|
384 |
logging.debug(f"Rendered and stored Markdown for section: {current_section_name}")
|
385 |
-
else
|
386 |
-
|
387 |
-
# The component itself is already created.
|
388 |
-
section_titles_map[current_section_name].visible = True
|
389 |
-
logging.debug(f"Ensuring visibility for existing Markdown for section: {current_section_name}")
|
390 |
last_rendered_section = current_section_name
|
391 |
|
392 |
-
with gr.Row(equal_height=False): # Row for one or two plots
|
393 |
# --- Process the first plot in the row (config1) ---
|
394 |
config1 = plot_configs[idx]
|
395 |
-
# Safety check
|
396 |
if config1["section"] != current_section_name:
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
|
405 |
panel_col1, plot_comp1, bomb_btn1, explore_btn1, formula_btn1 = \
|
406 |
-
create_analytics_plot_panel(config1["label"], config1["id"])
|
407 |
plot_ui_objects[config1["id"]] = {
|
408 |
"plot_component": plot_comp1, "bomb_button": bomb_btn1,
|
409 |
"explore_button": explore_btn1, "formula_button": formula_btn1,
|
410 |
-
"label": config1["label"], "panel_component": panel_col1,
|
411 |
"section": config1["section"]
|
412 |
}
|
413 |
logging.debug(f"Created UI panel for plot_id: {config1['id']} in section {config1['section']}")
|
@@ -416,9 +407,10 @@ def build_analytics_tab_plot_area(plot_configs):
|
|
416 |
# --- Process the second plot in the row (config2), if applicable ---
|
417 |
if idx < len(plot_configs):
|
418 |
config2 = plot_configs[idx]
|
419 |
-
|
|
|
420 |
panel_col2, plot_comp2, bomb_btn2, explore_btn2, formula_btn2 = \
|
421 |
-
create_analytics_plot_panel(config2["label"], config2["id"])
|
422 |
plot_ui_objects[config2["id"]] = {
|
423 |
"plot_component": plot_comp2, "bomb_button": bomb_btn2,
|
424 |
"explore_button": explore_btn2, "formula_button": formula_btn2,
|
@@ -427,6 +419,8 @@ def build_analytics_tab_plot_area(plot_configs):
|
|
427 |
}
|
428 |
logging.debug(f"Created UI panel for plot_id: {config2['id']} in same row, section {config2['section']}")
|
429 |
idx += 1
|
|
|
|
|
430 |
|
431 |
logging.info(f"Finished building plot area. Total plot objects: {len(plot_ui_objects)}. Section titles created: {len(section_titles_map)}")
|
432 |
if len(plot_ui_objects) != len(plot_configs):
|
|
|
24 |
# logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
|
25 |
|
26 |
# --- Constants for Button Icons/Text ---
|
27 |
+
# These are also defined/imported in app.py, ensure consistency
|
28 |
BOMB_ICON = "💣"
|
29 |
EXPLORE_ICON = "🧭"
|
30 |
FORMULA_ICON = "ƒ"
|
31 |
+
ACTIVE_ICON = "❌ Close" # Ensure this matches app.py
|
32 |
|
33 |
|
34 |
def display_main_dashboard(token_state):
|
|
|
152 |
fig_plot_local = None
|
153 |
if not mentions_df.empty and "sentiment_label" in mentions_df.columns:
|
154 |
try:
|
155 |
+
fig_plot_local, ax = plt.subplots(figsize=(6,4)) # Keep figsize for aspect ratio
|
156 |
sentiment_counts = mentions_df["sentiment_label"].value_counts()
|
157 |
sentiment_counts.plot(kind='bar', ax=ax, color=['#4CAF50', '#FFC107', '#F44336', '#9E9E9E', '#2196F3'])
|
158 |
+
ax.set_title("Mention Sentiment Distribution", y=1.03)
|
159 |
ax.set_ylabel("Count")
|
160 |
plt.xticks(rotation=45, ha='right')
|
161 |
|
162 |
plt.tight_layout()
|
163 |
+
fig_plot_local.subplots_adjust(top=0.90)
|
|
|
164 |
fig = fig_plot_local
|
165 |
logging.info("Mentions tab: Sentiment distribution plot generated.")
|
166 |
except Exception as e:
|
167 |
logging.error(f"Error generating mentions plot: {e}", exc_info=True)
|
168 |
fig = None
|
169 |
finally:
|
170 |
+
# Ensure plt.close is called on the figure object, not plt itself if it's not the same
|
171 |
+
if fig_plot_local and fig_plot_local is not plt: # Check if fig_plot_local is a Figure object
|
172 |
plt.close(fig_plot_local)
|
173 |
return mentions_html_output, fig
|
174 |
|
|
|
217 |
organic=(FOLLOWER_STATS_ORGANIC_COLUMN, 'sum'),
|
218 |
paid=(FOLLOWER_STATS_PAID_COLUMN, 'sum')
|
219 |
).reset_index()
|
220 |
+
plot_data['_plot_month_dt'] = pd.to_datetime(plot_data['_plot_month'], format=UI_MONTH_FORMAT) # Ensure correct month format
|
221 |
plot_data = plot_data.sort_values(by='_plot_month_dt')
|
222 |
|
223 |
|
224 |
+
fig_gains_local, ax_gains = plt.subplots(figsize=(10,5)) # Keep figsize for aspect ratio
|
225 |
ax_gains.plot(plot_data['_plot_month'], plot_data['organic'], marker='o', linestyle='-', label='Organic Gain')
|
226 |
ax_gains.plot(plot_data['_plot_month'], plot_data['paid'], marker='x', linestyle='--', label='Paid Gain')
|
227 |
+
ax_gains.set_title("Monthly Follower Gains Over Time", y=1.03)
|
228 |
ax_gains.set_ylabel("Follower Count")
|
229 |
ax_gains.set_xlabel("Month (YYYY-MM)")
|
230 |
plt.xticks(rotation=45, ha='right')
|
|
|
232 |
plt.grid(True, linestyle='--', alpha=0.7)
|
233 |
|
234 |
plt.tight_layout()
|
235 |
+
fig_gains_local.subplots_adjust(top=0.90)
|
236 |
plot_monthly_gains = fig_gains_local
|
237 |
logging.info("Follower stats tab: Monthly gains plot generated.")
|
238 |
else:
|
|
|
260 |
html_parts.append("<h4>Followers by Seniority (Top 10 Organic):</h4>")
|
261 |
html_parts.append(seniority_df_sorted[[FOLLOWER_STATS_CATEGORY_COLUMN, FOLLOWER_STATS_ORGANIC_COLUMN, FOLLOWER_STATS_PAID_COLUMN]].head(10).to_html(escape=True, index=False, classes="table table-sm"))
|
262 |
|
263 |
+
fig_seniority_local, ax_seniority = plt.subplots(figsize=(8,5)) # Keep figsize for aspect ratio
|
264 |
top_n_seniority = seniority_df_sorted.nlargest(10, FOLLOWER_STATS_ORGANIC_COLUMN)
|
265 |
ax_seniority.bar(top_n_seniority[FOLLOWER_STATS_CATEGORY_COLUMN], top_n_seniority[FOLLOWER_STATS_ORGANIC_COLUMN], color='skyblue')
|
266 |
+
ax_seniority.set_title("Follower Distribution by Seniority (Top 10 Organic)", y=1.03)
|
267 |
ax_seniority.set_ylabel("Organic Follower Count")
|
268 |
plt.xticks(rotation=45, ha='right')
|
269 |
plt.grid(axis='y', linestyle='--', alpha=0.7)
|
270 |
|
271 |
plt.tight_layout()
|
272 |
+
fig_seniority_local.subplots_adjust(top=0.88)
|
273 |
plot_seniority_dist = fig_seniority_local
|
274 |
logging.info("Follower stats tab: Seniority distribution plot generated.")
|
275 |
else:
|
|
|
296 |
html_parts.append("<h4>Followers by Industry (Top 10 Organic):</h4>")
|
297 |
html_parts.append(industry_df_sorted[[FOLLOWER_STATS_CATEGORY_COLUMN, FOLLOWER_STATS_ORGANIC_COLUMN, FOLLOWER_STATS_PAID_COLUMN]].head(10).to_html(escape=True, index=False, classes="table table-sm"))
|
298 |
|
299 |
+
fig_industry_local, ax_industry = plt.subplots(figsize=(8,5)) # Keep figsize for aspect ratio
|
300 |
top_n_industry = industry_df_sorted.nlargest(10, FOLLOWER_STATS_ORGANIC_COLUMN)
|
301 |
ax_industry.bar(top_n_industry[FOLLOWER_STATS_CATEGORY_COLUMN], top_n_industry[FOLLOWER_STATS_ORGANIC_COLUMN], color='lightcoral')
|
302 |
+
ax_industry.set_title("Follower Distribution by Industry (Top 10 Organic)", y=1.03)
|
303 |
ax_industry.set_ylabel("Organic Follower Count")
|
304 |
plt.xticks(rotation=45, ha='right')
|
305 |
plt.grid(axis='y', linestyle='--', alpha=0.7)
|
306 |
|
307 |
plt.tight_layout()
|
308 |
+
fig_industry_local.subplots_adjust(top=0.88)
|
309 |
plot_industry_dist = fig_industry_local
|
310 |
logging.info("Follower stats tab: Industry distribution plot generated.")
|
311 |
else:
|
|
|
323 |
follower_html_output = "\n".join(html_parts)
|
324 |
return follower_html_output, plot_monthly_gains, plot_seniority_dist, plot_industry_dist
|
325 |
|
326 |
+
|
327 |
def create_analytics_plot_panel(plot_label_str, plot_id_str):
|
328 |
"""
|
329 |
Creates an individual plot panel with its plot component and action buttons.
|
330 |
Plot title and action buttons are on the same row.
|
331 |
Returns the panel (Column), plot component, and button components.
|
332 |
"""
|
333 |
+
# Icons are defined globally or imported. For this function, ensure they are accessible.
|
334 |
+
# If not using from config directly here, you might need to pass them or use fixed strings.
|
335 |
+
# Using fixed strings as a fallback if import fails, though they should be available via app.py's import.
|
336 |
+
local_bomb_icon, local_explore_icon, local_formula_icon = BOMB_ICON, EXPLORE_ICON, FORMULA_ICON
|
337 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
with gr.Column(visible=True) as panel_component: # Main container for this plot
|
339 |
+
with gr.Row(variant="compact"):
|
340 |
+
gr.Markdown(f"#### {plot_label_str}", scale=3) # Plot title (scale might help balance)
|
341 |
+
with gr.Row(elem_classes="plot-actions", scale=1, min_width=150): # Action buttons container, give it some min_width
|
342 |
+
bomb_button = gr.Button(value=local_bomb_icon, variant="secondary", size="sm", min_width=30, elem_id=f"bomb_btn_{plot_id_str}")
|
343 |
+
formula_button = gr.Button(value=local_formula_icon, variant="secondary", size="sm", min_width=30, elem_id=f"formula_btn_{plot_id_str}")
|
344 |
+
explore_button = gr.Button(value=local_explore_icon, variant="secondary", size="sm", min_width=30, elem_id=f"explore_btn_{plot_id_str}")
|
|
|
|
|
345 |
|
346 |
+
# MODIFIED: Added height to gr.Plot for consistent sizing
|
347 |
+
plot_component = gr.Plot(label=plot_label_str, show_label=False, height=350) # Adjust height as needed
|
348 |
|
349 |
+
logging.debug(f"Created analytics panel for: {plot_label_str} (ID: {plot_id_str}) with fixed plot height.")
|
350 |
return panel_component, plot_component, bomb_button, explore_button, formula_button
|
351 |
|
352 |
|
|
|
359 |
- plot_ui_objects (dict): Dictionary of plot UI objects.
|
360 |
- section_titles_map (dict): Dictionary mapping section names to their gr.Markdown title components.
|
361 |
"""
|
362 |
+
logging.info(f"Building plot area for {len(plot_configs)} analytics plots with interleaved section titles.")
|
363 |
plot_ui_objects = {}
|
364 |
section_titles_map = {}
|
365 |
|
|
|
370 |
current_plot_config = plot_configs[idx]
|
371 |
current_section_name = current_plot_config["section"]
|
372 |
|
373 |
+
# Render section title if it's new for this block of plots
|
374 |
if current_section_name != last_rendered_section:
|
375 |
if current_section_name not in section_titles_map:
|
376 |
+
# Create the Markdown component for the section title
|
377 |
section_md_component = gr.Markdown(f"### {current_section_name}", visible=True)
|
378 |
section_titles_map[current_section_name] = section_md_component
|
379 |
logging.debug(f"Rendered and stored Markdown for section: {current_section_name}")
|
380 |
+
# No 'else' needed here for visibility, as it's handled by click handlers if sections are hidden/shown.
|
381 |
+
# The component is created once and its visibility is controlled elsewhere.
|
|
|
|
|
|
|
382 |
last_rendered_section = current_section_name
|
383 |
|
384 |
+
with gr.Row(equal_height=False): # Row for one or two plots. equal_height=False allows plots to define their height.
|
385 |
# --- Process the first plot in the row (config1) ---
|
386 |
config1 = plot_configs[idx]
|
387 |
+
# Safety check for section consistency (should always pass if configs are ordered by section)
|
388 |
if config1["section"] != current_section_name:
|
389 |
+
logging.warning(f"Plot {config1['id']} section mismatch. Expected {current_section_name}, got {config1['section']}. This might affect layout if a new section title was expected.")
|
390 |
+
# If a new section starts unexpectedly, ensure its title is created if missing
|
391 |
+
if config1["section"] not in section_titles_map:
|
392 |
+
sec_md = gr.Markdown(f"### {config1['section']}", visible=True) # Create and make visible
|
393 |
+
section_titles_map[config1['section']] = sec_md
|
394 |
+
last_rendered_section = config1["section"] # Update the current section context
|
|
|
395 |
|
396 |
panel_col1, plot_comp1, bomb_btn1, explore_btn1, formula_btn1 = \
|
397 |
+
create_analytics_plot_panel(config1["label"], config1["id"])
|
398 |
plot_ui_objects[config1["id"]] = {
|
399 |
"plot_component": plot_comp1, "bomb_button": bomb_btn1,
|
400 |
"explore_button": explore_btn1, "formula_button": formula_btn1,
|
401 |
+
"label": config1["label"], "panel_component": panel_col1, # This is the gr.Column containing the plot and its actions
|
402 |
"section": config1["section"]
|
403 |
}
|
404 |
logging.debug(f"Created UI panel for plot_id: {config1['id']} in section {config1['section']}")
|
|
|
407 |
# --- Process the second plot in the row (config2), if applicable ---
|
408 |
if idx < len(plot_configs):
|
409 |
config2 = plot_configs[idx]
|
410 |
+
# Only add to the same row if it's part of the same section
|
411 |
+
if config2["section"] == current_section_name:
|
412 |
panel_col2, plot_comp2, bomb_btn2, explore_btn2, formula_btn2 = \
|
413 |
+
create_analytics_plot_panel(config2["label"], config2["id"])
|
414 |
plot_ui_objects[config2["id"]] = {
|
415 |
"plot_component": plot_comp2, "bomb_button": bomb_btn2,
|
416 |
"explore_button": explore_btn2, "formula_button": formula_btn2,
|
|
|
419 |
}
|
420 |
logging.debug(f"Created UI panel for plot_id: {config2['id']} in same row, section {config2['section']}")
|
421 |
idx += 1
|
422 |
+
# If the next plot is in a new section, it will be handled in the next iteration of the while loop,
|
423 |
+
# starting with a new section title and a new gr.Row.
|
424 |
|
425 |
logging.info(f"Finished building plot area. Total plot objects: {len(plot_ui_objects)}. Section titles created: {len(section_titles_map)}")
|
426 |
if len(plot_ui_objects) != len(plot_configs):
|