Spaces:
Sleeping
Sleeping
Fix chart scaling and component update handling: 1) Improve default scaling of plotly charts with fixed dimensions and better auto-scaling 2) Fix component update mechanism in progressive_ui.py to properly handle different component types
e30d4c0
""" | |
Progressive UI module for the Tibetan Text Metrics app. | |
This module provides UI components and helper functions for progressive loading | |
and updating of metrics as they are computed. | |
""" | |
import gradio as gr | |
import pandas as pd | |
from typing import Dict, List, Any, Callable | |
from .progressive_loader import ProgressiveResult, MetricType | |
from .visualize import generate_visualizations, generate_word_count_chart | |
import logging | |
logger = logging.getLogger(__name__) | |
class ProgressiveUI: | |
""" | |
Manages progressive UI updates for the Tibetan Text Metrics app. | |
This class handles the incremental updates of UI components as metrics | |
are computed, allowing for a more responsive user experience. | |
""" | |
def __init__(self, | |
metrics_preview: gr.Dataframe, | |
word_count_plot: gr.Plot, | |
jaccard_heatmap: gr.Plot, | |
lcs_heatmap: gr.Plot, | |
fuzzy_heatmap: gr.Plot, | |
semantic_heatmap: gr.Plot, | |
warning_box: gr.Markdown, | |
progress_container: gr.Row, | |
heatmap_titles: Dict[str, str], | |
structural_btn=None): | |
""" | |
Initialize the ProgressiveUI. | |
Args: | |
metrics_preview: Gradio Dataframe component for metrics preview | |
word_count_plot: Gradio Plot component for word count visualization | |
jaccard_heatmap: Gradio Plot component for Jaccard similarity heatmap | |
lcs_heatmap: Gradio Plot component for LCS similarity heatmap | |
fuzzy_heatmap: Gradio Plot component for fuzzy similarity heatmap | |
semantic_heatmap: Gradio Plot component for semantic similarity heatmap | |
warning_box: Gradio Markdown component for warnings | |
progress_container: Gradio Row component to hold progress indicators | |
heatmap_titles: Dictionary mapping metric names to descriptive titles | |
""" | |
self.metrics_preview = metrics_preview | |
self.word_count_plot = word_count_plot | |
self.jaccard_heatmap = jaccard_heatmap | |
self.lcs_heatmap = lcs_heatmap | |
self.fuzzy_heatmap = fuzzy_heatmap | |
self.semantic_heatmap = semantic_heatmap | |
self.warning_box = warning_box | |
self.progress_container = progress_container | |
self.heatmap_titles = heatmap_titles | |
self.structural_btn = structural_btn | |
# Create progress indicators for each metric | |
with self.progress_container: | |
self.jaccard_progress = gr.Markdown("π **Jaccard Similarity:** Waiting...", elem_id="jaccard_progress") | |
self.lcs_progress = gr.Markdown("π **Normalized LCS:** Waiting...", elem_id="lcs_progress") | |
self.fuzzy_progress = gr.Markdown("π **Fuzzy Similarity:** Waiting...", elem_id="fuzzy_progress") | |
self.semantic_progress = gr.Markdown("π **Semantic Similarity:** Waiting...", elem_id="semantic_progress") | |
self.word_count_progress = gr.Markdown("π **Word Counts:** Waiting...", elem_id="word_count_progress") | |
# Track which components have been updated | |
self.updated_components = set() | |
def update(self, result: ProgressiveResult) -> Dict[gr.components.Component, Any]: | |
""" | |
Update UI components based on progressive results. | |
Args: | |
result: ProgressiveResult object containing the current state of computation | |
Returns: | |
Dictionary mapping Gradio components to their updated values | |
""" | |
updates = {} | |
# Always update metrics preview if we have data | |
if not result.metrics_df.empty: | |
updates[self.metrics_preview] = result.metrics_df.head(10) | |
# Update warning if present | |
if result.warning: | |
warning_md = f"**β οΈ Warning:** {result.warning}" if result.warning else "" | |
updates[self.warning_box] = gr.update(value=warning_md, visible=True) | |
# Generate visualizations for completed metrics | |
if not result.metrics_df.empty: | |
# Generate heatmaps for available metrics | |
heatmaps_data = generate_visualizations( | |
result.metrics_df, descriptive_titles=self.heatmap_titles | |
) | |
# Update heatmaps and progress indicators for completed metrics | |
for metric_type in result.completed_metrics: | |
if metric_type == MetricType.JACCARD: | |
# Update progress indicator | |
updates[self.jaccard_progress] = "β **Jaccard Similarity:** Complete" | |
# Update heatmap if not already updated | |
if self.jaccard_heatmap not in self.updated_components: | |
if "Jaccard Similarity (%)" in heatmaps_data: | |
updates[self.jaccard_heatmap] = heatmaps_data["Jaccard Similarity (%)"] | |
self.updated_components.add(self.jaccard_heatmap) | |
elif metric_type == MetricType.LCS: | |
# Update progress indicator | |
updates[self.lcs_progress] = "β **Normalized LCS:** Complete" | |
# Update heatmap if not already updated | |
if self.lcs_heatmap not in self.updated_components: | |
if "Normalized LCS" in heatmaps_data: | |
updates[self.lcs_heatmap] = heatmaps_data["Normalized LCS"] | |
self.updated_components.add(self.lcs_heatmap) | |
elif metric_type == MetricType.FUZZY: | |
# Update progress indicator | |
updates[self.fuzzy_progress] = "β **Fuzzy Similarity:** Complete" | |
# Update heatmap if not already updated | |
if self.fuzzy_heatmap not in self.updated_components: | |
if "Fuzzy Similarity" in heatmaps_data: | |
updates[self.fuzzy_heatmap] = heatmaps_data["Fuzzy Similarity"] | |
self.updated_components.add(self.fuzzy_heatmap) | |
elif metric_type == MetricType.SEMANTIC: | |
# Update progress indicator | |
updates[self.semantic_progress] = "β **Semantic Similarity:** Complete" | |
# Update heatmap if not already updated | |
if self.semantic_heatmap not in self.updated_components: | |
if "Semantic Similarity" in heatmaps_data: | |
updates[self.semantic_heatmap] = heatmaps_data["Semantic Similarity"] | |
self.updated_components.add(self.semantic_heatmap) | |
# Generate word count chart if we have data | |
if not result.word_counts_df.empty: | |
# Update progress indicator | |
updates[self.word_count_progress] = "β **Word Counts:** Complete" | |
# Update chart if not already updated | |
if self.word_count_plot not in self.updated_components: | |
updates[self.word_count_plot] = generate_word_count_chart(result.word_counts_df) | |
self.updated_components.add(self.word_count_plot) | |
# Update progress indicators for metrics in progress | |
if not result.is_complete: | |
# Update progress indicators for metrics that are still in progress | |
if MetricType.JACCARD not in result.completed_metrics: | |
updates[self.jaccard_progress] = "β³ **Jaccard Similarity:** In progress..." | |
if MetricType.LCS not in result.completed_metrics: | |
updates[self.lcs_progress] = "β³ **Normalized LCS:** In progress..." | |
if MetricType.FUZZY not in result.completed_metrics: | |
updates[self.fuzzy_progress] = "β³ **Fuzzy Similarity:** In progress..." | |
if MetricType.SEMANTIC not in result.completed_metrics: | |
updates[self.semantic_progress] = "β³ **Semantic Similarity:** In progress..." | |
if self.word_count_plot not in self.updated_components: | |
updates[self.word_count_progress] = "β³ **Word Counts:** In progress..." | |
else: | |
# If computation is complete, enable structural button if available | |
if self.structural_btn is not None: | |
updates[self.structural_btn] = gr.update(interactive=True) | |
logger.info("Enabling structural analysis button via progressive UI") | |
return updates | |
def create_progressive_callback(progressive_ui: ProgressiveUI) -> Callable: | |
""" | |
Create a callback function for progressive updates. | |
Args: | |
progressive_ui: ProgressiveUI instance to handle updates | |
Returns: | |
Callback function that can be passed to process_texts | |
""" | |
def callback(metrics_df: pd.DataFrame, | |
word_counts_df: pd.DataFrame, | |
completed_metrics: List[MetricType], | |
warning: str, | |
is_complete: bool) -> None: | |
""" | |
Callback function for progressive updates. | |
Args: | |
metrics_df: DataFrame with current metrics | |
word_counts_df: DataFrame with word counts | |
completed_metrics: List of completed metric types | |
warning: Warning message | |
is_complete: Whether computation is complete | |
""" | |
result = ProgressiveResult( | |
metrics_df=metrics_df, | |
word_counts_df=word_counts_df, | |
completed_metrics=completed_metrics, | |
warning=warning, | |
is_complete=is_complete | |
) | |
# Get updates for UI components | |
updates = progressive_ui.update(result) | |
# Apply updates to UI components | |
for component, value in updates.items(): | |
try: | |
# Handle different component types appropriately | |
if isinstance(component, gr.Markdown): | |
# For Markdown components, directly set the value | |
component.value = value | |
elif isinstance(value, gr.update): | |
# For gr.update objects (like button state updates) | |
component.update(**value.kwargs) | |
elif isinstance(component, (gr.Plot, gr.Dataframe, gr.HTML, gr.File)): | |
# For Plot, Dataframe, HTML, and File components, use the update method with the value | |
if value is not None: # Only update if we have a value | |
component.update(value=value) | |
elif hasattr(component, 'update'): | |
# For other components with update method | |
component.update(value=value) | |
else: | |
logger.warning(f"Cannot update component of type {type(component)}") | |
except Exception as e: | |
logger.warning(f"Error updating component: {e}") | |
return callback | |