Spaces:
Sleeping
Sleeping
| """Chart components for Gen3 Nudge Detection Dashboard.""" | |
| import plotly.graph_objects as go | |
| import pandas as pd | |
| # ============================================================================= | |
| # Color Constants | |
| # ============================================================================= | |
| CONFIDENCE_TIER_COLORS = { | |
| "HIGH": "#EF4444", # Red | |
| "MEDIUM": "#F59E0B", # Amber | |
| "LOW": "#10B981", # Green | |
| } | |
| PLATFORM_COLORS = { | |
| "CHATGPT": "#10A37F", | |
| "GOOGLE_AI": "#4285F4", | |
| "GOOGLE_OVERVIEW": "#34A853", | |
| "PERPLEXITY": "#6366F1", | |
| "GEMINI": "#8B5CF6", | |
| "BING": "#00A4EF", | |
| "CLAUDE": "#D97706", | |
| } | |
| POLARITY_COLORS = { | |
| "positive": "#10B981", | |
| "negative": "#EF4444", | |
| "neutral": "#6B7280", | |
| } | |
| # English to Korean emotion mapping | |
| EMOTION_KO = { | |
| "admiration": "๊ฐํ", | |
| "amusement": "์ฌ๋ฏธ", | |
| "anger": "๋ถ๋ ธ", | |
| "annoyance": "์ง์ฆ", | |
| "approval": "์ธ์ ", | |
| "caring": "๋ฐฐ๋ ค", | |
| "confusion": "ํผ๋", | |
| "curiosity": "ํธ๊ธฐ์ฌ", | |
| "desire": "์๊ตฌ", | |
| "disappointment": "์ค๋ง", | |
| "disapproval": "๋ฐ๋", | |
| "disgust": "ํ์ค", | |
| "embarrassment": "๋นํน", | |
| "excitement": "ํฅ๋ถ", | |
| "fear": "๋๋ ค์", | |
| "gratitude": "๊ฐ์ฌ", | |
| "grief": "์ฌํ", | |
| "joy": "๊ธฐ์จ", | |
| "love": "์ฌ๋", | |
| "nervousness": "๋ถ์", | |
| "optimism": "๋๊ด", | |
| "pride": "์๋ถ์ฌ", | |
| "realization": "๊นจ๋ฌ์", | |
| "relief": "์๋", | |
| "remorse": "ํํ", | |
| "sadness": "์ฌํ", | |
| "surprise": "๋๋ผ์", | |
| "neutral": "์ค๋ฆฝ", | |
| "trust": "์ ๋ขฐ", | |
| "anticipation": "๊ธฐ๋", | |
| "interest": "๊ด์ฌ", | |
| "satisfaction": "๋ง์กฑ", | |
| "frustration": "์ข์ ", | |
| "hope": "ํฌ๋ง", | |
| "worry": "๊ฑฑ์ ", | |
| } | |
| # CEJ Korean labels | |
| CEJ_LABELS = { | |
| "VERIFICATION": "๊ฒ์ฆ ์ง๋ฌธ", | |
| "INFORMATION_DISCOVERY": "์ ๋ณด ํ์", | |
| "HOW_TO": "์ฌ์ฉ๋ฒ", | |
| "WHERE_TO_BUY": "๊ตฌ๋งค์ฒ", | |
| "RECOMMENDATION": "์ถ์ฒ ์์ฒญ", | |
| "SIDE_EFFECT": "๋ถ์์ฉ", | |
| "MARKET_TRENDS": "์์ฅ ๋ํฅ", | |
| "RESULT_EFFECTIVENESS": "ํจ๊ณผ/๊ฒฐ๊ณผ", | |
| "COMPARISON": "๋น๊ต", | |
| "INGREDIENT": "์ฑ๋ถ", | |
| "AWARENESS_COMPARISON": "์ธ์ง/๋น๊ต", | |
| "PURCHASE": "๊ตฌ๋งค", | |
| "POST_PURCHASE": "๊ตฌ๋งค ํ", | |
| } | |
| # ============================================================================= | |
| # Gen3 Nudge Charts | |
| # ============================================================================= | |
| def create_confidence_tier_pie_chart(tier_stats: dict) -> go.Figure: | |
| """Create pie chart for confidence tier distribution (HIGH/MEDIUM/LOW).""" | |
| if not tier_stats: | |
| return go.Figure() | |
| labels = list(tier_stats.keys()) | |
| values = list(tier_stats.values()) | |
| colors = [CONFIDENCE_TIER_COLORS.get(k, "#6B7280") for k in labels] | |
| label_map = { | |
| "HIGH": "๐ด HIGH", | |
| "MEDIUM": "๐ก MEDIUM", | |
| "LOW": "๐ข LOW", | |
| } | |
| display_labels = [label_map.get(k, k) for k in labels] | |
| fig = go.Figure(data=[go.Pie( | |
| labels=display_labels, | |
| values=values, | |
| marker=dict(colors=colors), | |
| hole=0.4, | |
| textinfo="percent+value", | |
| textposition="outside", | |
| )]) | |
| fig.update_layout( | |
| title="", | |
| showlegend=True, | |
| legend=dict(orientation="h", yanchor="bottom", y=-0.2, xanchor="center", x=0.5), | |
| margin=dict(t=20, b=60, l=20, r=20), | |
| height=280, | |
| ) | |
| return fig | |
| def create_platform_bar_chart(platform_stats: dict) -> go.Figure: | |
| """Create horizontal bar chart for platform distribution.""" | |
| if not platform_stats: | |
| return go.Figure() | |
| sorted_items = sorted(platform_stats.items(), key=lambda x: x[1], reverse=True) | |
| platforms = [item[0] for item in sorted_items] | |
| counts = [item[1] for item in sorted_items] | |
| colors = [PLATFORM_COLORS.get(p, "#6B7280") for p in platforms] | |
| fig = go.Figure(data=[ | |
| go.Bar( | |
| y=platforms, | |
| x=counts, | |
| orientation="h", | |
| marker_color=colors, | |
| text=[f"{c}๊ฑด" for c in counts], | |
| textposition="auto", | |
| ) | |
| ]) | |
| fig.update_layout( | |
| title="", | |
| xaxis_title="๋์ง ํ๋ณด ์", | |
| yaxis=dict(autorange="reversed"), | |
| margin=dict(t=20, b=40, l=100, r=20), | |
| height=max(200, len(platforms) * 35), | |
| ) | |
| return fig | |
| def create_nudge_by_cej_bar_chart(cej_counts: dict) -> go.Figure: | |
| """Create horizontal bar chart for nudge distribution by CEJ stage.""" | |
| if not cej_counts: | |
| return go.Figure() | |
| sorted_items = sorted(cej_counts.items(), key=lambda x: x[1], reverse=True) | |
| cej_stages = [item[0] for item in sorted_items] | |
| counts = [item[1] for item in sorted_items] | |
| display_labels = [CEJ_LABELS.get(cej, cej) for cej in cej_stages] | |
| fig = go.Figure(data=[ | |
| go.Bar( | |
| y=display_labels, | |
| x=counts, | |
| orientation="h", | |
| marker_color="#6366F1", | |
| text=[f"{c}๊ฑด" for c in counts], | |
| textposition="auto", | |
| ) | |
| ]) | |
| fig.update_layout( | |
| title="", | |
| xaxis_title="๋์ง ํ๋ณด ์", | |
| yaxis=dict(autorange="reversed"), | |
| margin=dict(t=20, b=40, l=100, r=20), | |
| height=max(200, len(cej_stages) * 35), | |
| ) | |
| return fig | |
| def create_brand_sentiment_chart(brands: list[dict]) -> go.Figure: | |
| """Create horizontal stacked bar chart for brand sentiment comparison.""" | |
| if not brands: | |
| return go.Figure() | |
| # Sort by total mentions | |
| sorted_brands = sorted(brands, key=lambda x: x.get("total_mentions", 0), reverse=True)[:10] | |
| brand_names = [] | |
| for b in sorted_brands: | |
| name = b.get("brand_name", "Unknown") | |
| brand_type = b.get("brand_type", "") | |
| prefix = "๐ " if brand_type == "IN_HOUSE" else "๐ข " | |
| brand_names.append(f"{prefix}{name}") | |
| positive_rates = [b.get("positive_rate", 0) for b in sorted_brands] | |
| negative_rates = [b.get("negative_rate", 0) for b in sorted_brands] | |
| neutral_rates = [100 - p - n for p, n in zip(positive_rates, negative_rates)] | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| y=brand_names, | |
| x=positive_rates, | |
| name="๊ธ์ ", | |
| orientation="h", | |
| marker_color="#10B981", | |
| text=[f"{v:.1f}%" for v in positive_rates], | |
| textposition="inside", | |
| )) | |
| fig.add_trace(go.Bar( | |
| y=brand_names, | |
| x=neutral_rates, | |
| name="์ค๋ฆฝ", | |
| orientation="h", | |
| marker_color="#E5E7EB", | |
| text=[f"{v:.1f}%" for v in neutral_rates], | |
| textposition="inside", | |
| )) | |
| fig.add_trace(go.Bar( | |
| y=brand_names, | |
| x=negative_rates, | |
| name="๋ถ์ ", | |
| orientation="h", | |
| marker_color="#EF4444", | |
| text=[f"{v:.1f}%" for v in negative_rates], | |
| textposition="inside", | |
| )) | |
| fig.update_layout( | |
| title="", | |
| barmode="stack", | |
| xaxis_title="๋น์จ (%)", | |
| legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), | |
| margin=dict(t=50, b=50, l=150, r=20), | |
| height=max(300, len(sorted_brands) * 40), | |
| yaxis=dict(autorange="reversed"), | |
| ) | |
| return fig | |
| def create_domain_bar_chart(domain_counts: dict) -> go.Figure: | |
| """Create horizontal bar chart for citation domain distribution.""" | |
| if not domain_counts: | |
| return go.Figure() | |
| # Take top 15 domains | |
| sorted_items = list(domain_counts.items())[:15] | |
| domains = [item[0] for item in sorted_items] | |
| counts = [item[1] for item in sorted_items] | |
| # Truncate long domain names | |
| display_domains = [d[:40] + "..." if len(d) > 40 else d for d in domains] | |
| fig = go.Figure(data=[ | |
| go.Bar( | |
| y=display_domains, | |
| x=counts, | |
| orientation="h", | |
| marker_color="#3B82F6", | |
| text=[f"{c}ํ" for c in counts], | |
| textposition="auto", | |
| ) | |
| ]) | |
| fig.update_layout( | |
| title="", | |
| xaxis_title="์ธ์ฉ ํ์", | |
| yaxis=dict(autorange="reversed"), | |
| margin=dict(t=20, b=40, l=200, r=20), | |
| height=max(300, len(sorted_items) * 30), | |
| ) | |
| return fig | |
| def create_bit_quadrant_chart(bit_stats: dict) -> go.Figure: | |
| """Create bar chart for BIT quadrant distribution.""" | |
| if not bit_stats: | |
| return go.Figure() | |
| BIT_LABELS = { | |
| "neutral": "์ค๋ฆฝ", | |
| "product_satisfaction": "์ ํ ๋ง์กฑ", | |
| "unmet_expectations": "๊ธฐ๋ ๋ฏธ์ถฉ์กฑ", | |
| "brand_trust": "๋ธ๋๋ ์ ๋ขฐ", | |
| } | |
| sorted_items = sorted(bit_stats.items(), key=lambda x: x[1], reverse=True) | |
| quadrants = [BIT_LABELS.get(item[0], item[0]) for item in sorted_items] | |
| counts = [item[1] for item in sorted_items] | |
| colors = ["#6366F1", "#8B5CF6", "#A855F7", "#D946EF"] | |
| fig = go.Figure(data=[ | |
| go.Bar( | |
| x=quadrants, | |
| y=counts, | |
| marker_color=colors[:len(quadrants)], | |
| text=[f"{c}๊ฑด" for c in counts], | |
| textposition="outside", | |
| ) | |
| ]) | |
| fig.update_layout( | |
| title="", | |
| yaxis_title="๋์ง ํ๋ณด ์", | |
| margin=dict(t=20, b=50, l=50, r=20), | |
| height=280, | |
| ) | |
| return fig | |
| def create_emotion_distribution_chart(emotion_stats: dict) -> go.Figure: | |
| """Create bar chart for emotion distribution in nudge candidates.""" | |
| if not emotion_stats: | |
| return go.Figure() | |
| sorted_items = sorted(emotion_stats.items(), key=lambda x: x[1], reverse=True)[:8] | |
| emotions = [EMOTION_KO.get(item[0], item[0]) for item in sorted_items] | |
| counts = [item[1] for item in sorted_items] | |
| colors = ["#6366F1", "#8B5CF6", "#A855F7", "#D946EF", "#EC4899", "#F43F5E", "#F97316", "#FBBF24"] | |
| fig = go.Figure(data=[ | |
| go.Bar( | |
| x=emotions, | |
| y=counts, | |
| marker_color=colors[:len(emotions)], | |
| text=[f"{c}๊ฑด" for c in counts], | |
| textposition="outside", | |
| ) | |
| ]) | |
| fig.update_layout( | |
| title="", | |
| yaxis_title="๋์ง ํ๋ณด ์", | |
| xaxis_tickangle=-30, | |
| margin=dict(t=20, b=80, l=50, r=20), | |
| height=280, | |
| ) | |
| return fig | |