"""Dashboard card components.""" import html import streamlit as st from core.charts import CONFIDENCE_TIER_COLORS from core.styles import TIER_BORDER_COLORS from core.utils import get_confidence_tier, truncate_text, format_brands_list def render_nudge_card( item: dict, tier: str, emoji: str, tier_desc: str, confidence: float, ) -> None: """Render nudge candidate card with summary info. Args: item: Nudge candidate data dict tier: Confidence tier (HIGH/MEDIUM/LOW) emoji: Tier emoji tier_desc: Tier description confidence: Confidence score (0-1) """ cej_stage = item.get("cej_depth2") or item.get("cej_depth1") or "N/A" platform = item.get("platform", "N/A") in_house = item.get("in_house_brands", []) mentioned = item.get("mentioned_brands", []) question = item.get("question_content", "") answer = item.get("answer_preview", "") tier_color = CONFIDENCE_TIER_COLORS.get(tier, "#6B7280") border_color = TIER_BORDER_COLORS.get(tier, "#6B7280") question_display = html.escape(truncate_text(question, 200)) answer_short = html.escape(truncate_text(answer, 150)) in_house_display = html.escape(format_brands_list(in_house)) mentioned_display = html.escape(format_brands_list(mentioned)) header_html = f"""
๐Ÿ“ CEJ: {cej_stage} {emoji} ๋‹ต๋ณ€ ์ „์ฒด: {tier} ({confidence:.0%})
๐Ÿ’ฌ ์งˆ๋ฌธ
{question_display}
{answer_short}...
๐Ÿท๏ธ {in_house_display} ๐Ÿ“ข {mentioned_display} ๐Ÿ–ฅ๏ธ {platform}
""" st.markdown(header_html, unsafe_allow_html=True) def render_brand_card( brand_name: str, brand_type: str, sentiment_data: dict, ) -> None: """Render brand mention card. Args: brand_name: Brand name brand_type: 'in_house' or 'competitor' sentiment_data: Dict with sentiment, confidence, count """ sentiment = sentiment_data.get("sentiment", "neutral") confidence = sentiment_data.get("confidence", 0) mention_count = sentiment_data.get("count", 0) type_badge = "๐Ÿ  ์ž์‚ฌ" if brand_type == "in_house" else "๐Ÿข ๊ฒฝ์Ÿ์‚ฌ" type_bg = "#DBEAFE" if brand_type == "in_house" else "#FEE2E2" sentiment_colors = { "positive": "#10B981", "negative": "#EF4444", "neutral": "#6B7280", } sent_color = sentiment_colors.get(sentiment, "#6B7280") sentiment_ko = {"positive": "๊ธ์ •", "negative": "๋ถ€์ •", "neutral": "์ค‘๋ฆฝ"}.get(sentiment, sentiment) card_html = f"""
{html.escape(brand_name)} {type_badge}
{sentiment_ko} ์‹ ๋ขฐ๋„: {confidence:.0%} ์–ธ๊ธ‰: {mention_count}ํšŒ
""" st.markdown(card_html, unsafe_allow_html=True) def render_verification_item(item: dict, is_false_positive: bool = True) -> None: """Render LLM verification item info (used inside expander). Args: item: Verification result dict is_false_positive: True for FP, False for TN """ st.markdown(f"**์งˆ๋ฌธ**: {item.get('question_content', 'N/A')}") st.markdown(f"**๋‹ต๋ณ€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ**: {item.get('answer_preview', 'N/A')}") st.markdown("---") info_col1, info_col2, info_col3 = st.columns(3) with info_col1: st.markdown(f"**ํ”Œ๋žซํผ**: {item.get('platform', 'N/A')}") st.markdown(f"**CEJ**: {item.get('cej_depth1', 'N/A')} / {item.get('cej_depth2', 'N/A')}") with info_col2: st.markdown(f"**1์ฐจ ํŒ์ •**: {item.get('routing_tier', 'N/A')}") st.markdown(f"**1์ฐจ ๊ฐ์„ฑ**: {item.get('overall_polarity', 'N/A')}") with info_col3: llm_conf = item.get('llm_confidence', 0) or 0 st.markdown(f"**LLM ์‹ ๋ขฐ๋„**: {llm_conf:.1%}") st.markdown(f"**LLM ์กฐ์ • Tier**: {item.get('llm_adjusted_tier', 'N/A')}") # LLM reasoning if item.get('llm_reasoning'): st.markdown("**LLM ํŒ๋‹จ ๊ทผ๊ฑฐ**:") if is_false_positive: st.info(item.get('llm_reasoning')) else: st.warning(item.get('llm_reasoning')) # Evidence spans if item.get('llm_evidence_spans'): label = "**๊ทผ๊ฑฐ ๋ฌธ์žฅ**:" if is_false_positive else "**๋ถ€์ • ๊ทผ๊ฑฐ ๋ฌธ์žฅ**:" st.markdown(label) for span in (item.get('llm_evidence_spans') or []): st.markdown(f"- _{span}_") # Brands in_house = item.get('in_house_brands', []) or [] mentioned = item.get('mentioned_brands', []) or [] if in_house or mentioned: st.markdown(f"**์ž์‚ฌ ๋ธŒ๋žœ๋“œ**: {', '.join(in_house) if in_house else 'N/A'}") st.markdown(f"**์–ธ๊ธ‰ ๋ธŒ๋žœ๋“œ**: {', '.join(mentioned) if mentioned else 'N/A'}") def render_polarity_item(item: dict) -> None: """Render polarity (sentiment) item info (used inside expander). Args: item: Sentiment summary dict """ confidence = item.get('overall_confidence', 0) or 0 st.markdown(f"**์งˆ๋ฌธ**: {item.get('question_content', 'N/A')}") st.markdown(f"**๋‹ต๋ณ€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ**: {item.get('answer_preview', 'N/A')}") st.markdown("---") info_col1, info_col2, info_col3 = st.columns(3) with info_col1: st.markdown(f"**๊ฐ์„ฑ**: {item.get('overall_polarity', 'N/A')}") st.markdown(f"**์‹ ๋ขฐ๋„**: {confidence:.1%}") with info_col2: st.markdown(f"**ํ”Œ๋žซํผ**: {item.get('platform', 'N/A')}") st.markdown(f"**CEJ**: {item.get('cej_depth1', 'N/A')} / {item.get('cej_depth2', 'N/A')}") with info_col3: tier = item.get('routing_tier', 'N/A') st.markdown(f"**๋ผ์šฐํŒ… Tier**: {tier}") emotion = item.get('dominant_emotion', 'N/A') st.markdown(f"**๊ฐ์ •**: {emotion}") # Brands in_house = item.get('in_house_brands', []) or [] mentioned = item.get('mentioned_brands', []) or [] if in_house or mentioned: st.markdown(f"**์ž์‚ฌ ๋ธŒ๋žœ๋“œ**: {', '.join(in_house) if in_house else 'N/A'}") st.markdown(f"**์–ธ๊ธ‰ ๋ธŒ๋žœ๋“œ**: {', '.join(mentioned) if mentioned else 'N/A'}")