Spaces:
Sleeping
Sleeping
| """๊ฐ์ฑ๋ถ์ Feature ์์ฝ ์นด๋. | |
| ๊ธฐ์กด sections/executive_summary.py + quick_overview.py + KPI ํตํฉ. | |
| """ | |
| import streamlit as st | |
| from core.charts import ( | |
| create_confidence_tier_pie_chart, | |
| create_nudge_by_cej_bar_chart, | |
| create_platform_bar_chart, | |
| ) | |
| def get_risk_grade(high_nudges: int) -> tuple[str, str, str]: | |
| """Get risk grade based on HIGH tier nudge count.""" | |
| if high_nudges == 0: | |
| return "A", "grade-a", "์ฐ์ (๋ถ์ ์ธ๊ธ ์์)" | |
| elif high_nudges <= 10: | |
| return "B", "grade-b", "์ํธ" | |
| elif high_nudges <= 30: | |
| return "C", "grade-c", "์ฃผ์ ํ์" | |
| else: | |
| return "D", "grade-d", "์ฆ์ ๋์" | |
| def render_summary(data: dict): | |
| """๊ฐ์ฑ๋ถ์ ์์ฝ ์นด๋ ๋ ๋๋ง.""" | |
| total_nudge = data.get("total_nudge", 0) | |
| high_count = data.get("high_count", 0) | |
| medium_count = data.get("medium_count", 0) | |
| risk_score = data.get("risk_score", 0.0) | |
| tier_stats = data.get("tier_stats") or {} | |
| platform_stats = data.get("platform_stats") or {} | |
| cej_stats = data.get("cej_stats") or {} | |
| candidates = data.get("candidates") or [] | |
| campaign_overview = data.get("campaign_overview") or {} | |
| # --- Sentiment Summary Card --- | |
| grade, grade_class, grade_desc = get_risk_grade(high_count) | |
| if total_nudge == 0: | |
| nudge_insight = "๋ถ์ ์ธ๊ธ ์์" | |
| elif high_count == 0: | |
| nudge_insight = f"์ ์ฌ ๋ฆฌ์คํฌ {total_nudge}๊ฑด (ํ์ ๋ ๋ฎ์)" | |
| else: | |
| nudge_insight = f"HIGH {high_count}๊ฑด / ์ด {total_nudge}๊ฑด" | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.markdown(f""" | |
| <div style="background: linear-gradient(135deg, #EEF2FF 0%, #E0E7FF 100%); | |
| padding: 16px; border-radius: 12px; min-height: 120px;"> | |
| <div style="font-size: 13px; color: #6B7280;">๊ฑด๊ฐ ๋ฑ๊ธ</div> | |
| <div style="font-size: 32px; font-weight: bold; color: #4338CA;">{grade}</div> | |
| <div style="font-size: 12px; color: #4B5563;">{grade_desc}</div> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col2: | |
| st.metric("๐ด HIGH", f"{high_count}๊ฑด", help="โฅ85% ํ์ ๋ - ์ฆ์ ๋์ ๊ถ์ฅ") | |
| with col3: | |
| st.metric("๋ฆฌ์คํฌ ์ ์", f"{risk_score:.1f}", help="๊ฐ์ค ํ๊ท ์ ์") | |
| with col4: | |
| citation_total = sum(c.get("citation_count", 0) or 0 for c in candidates) | |
| st.metric("์ด ์ธ์ฉ ์์ค", f"{citation_total}๊ฐ") | |
| # --- Pipeline Overview (collapsible) --- | |
| with st.expander("๐ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ์์ธ", expanded=False): | |
| _render_pipeline_overview(campaign_overview) | |
| # --- Quick Charts --- | |
| st.markdown("---") | |
| chart_col1, chart_col2, chart_col3, chart_col4 = st.columns(4) | |
| with chart_col1: | |
| st.markdown("##### Confidence Tier ๋ถํฌ") | |
| if tier_stats and sum(tier_stats.values()) > 0: | |
| fig = create_confidence_tier_pie_chart(tier_stats) | |
| st.plotly_chart(fig, use_container_width=True, config={"displayModeBar": False}) | |
| else: | |
| st.info("๋ถ์ ์ธ๊ธ์ด ์์ต๋๋ค") | |
| with chart_col2: | |
| st.markdown("##### ํ๋ซํผ๋ณ ๋ถํฌ") | |
| if platform_stats and sum(platform_stats.values()) > 0: | |
| fig = create_platform_bar_chart(platform_stats) | |
| st.plotly_chart(fig, use_container_width=True, config={"displayModeBar": False}) | |
| else: | |
| st.info("ํ๋ซํผ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค") | |
| with chart_col3: | |
| st.markdown("##### CEJ ๋จ๊ณ๋ณ ๋ถํฌ") | |
| if cej_stats and sum(cej_stats.values()) > 0: | |
| fig = create_nudge_by_cej_bar_chart(cej_stats) | |
| st.plotly_chart(fig, use_container_width=True, config={"displayModeBar": False}) | |
| else: | |
| st.info("CEJ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค") | |
| with chart_col4: | |
| _render_llm_verification_summary(campaign_overview) | |
| def _render_pipeline_overview(campaign_overview: dict): | |
| """๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ํํฉ.""" | |
| overview_total = campaign_overview.get("total_answers", 0) | |
| overview_ih_neg = campaign_overview.get("in_house_negative_count", 0) | |
| overview_llm_done = campaign_overview.get("llm_verified_in_house", 0) | |
| overview_llm_pending = campaign_overview.get("llm_pending", 0) | |
| overview_llm_confirmed = campaign_overview.get("llm_confirmed_negative", 0) | |
| pipe1, pipe2, pipe3, pipe4, pipe5 = st.columns(5) | |
| with pipe1: | |
| st.metric( | |
| label="์ ์ฒด AI ๋ต๋ณ", | |
| value=f"{overview_total:,}๊ฑด", | |
| help="๊ฐ์ฑ ๋ถ์์ด ์๋ฃ๋ ์ ์ฒด AI ๋ต๋ณ ์", | |
| ) | |
| with pipe2: | |
| ih_rate = (overview_ih_neg / overview_total * 100) if overview_total > 0 else 0 | |
| st.metric( | |
| label="1์ฐจ ๋ถ์ ๊ฐ์ง (DeBERTa)", | |
| value=f"{overview_ih_neg:,}๊ฑด", | |
| delta=f"{ih_rate:.1f}%", | |
| delta_color="inverse", | |
| help="์์ฌ ๋ธ๋๋์ ๋ํ ๋ถ์ ๊ฐ์ฑ์ด ๊ฐ์ง๋ ๋ต๋ณ (ABSA ๊ธฐ๋ฐ)", | |
| ) | |
| with pipe3: | |
| verify_rate = (overview_llm_done / overview_ih_neg * 100) if overview_ih_neg > 0 else 0 | |
| st.metric( | |
| label="2์ฐจ ๊ฒ์ฆ ์๋ฃ (LLM)", | |
| value=f"{overview_llm_done:,}๊ฑด", | |
| delta=f"{verify_rate:.0f}% ์๋ฃ", | |
| delta_color="normal" if verify_rate >= 90 else "off", | |
| help="LLM 2์ฐจ ๊ฒ์ฆ์ด ์๋ฃ๋ ๊ฑด์", | |
| ) | |
| with pipe4: | |
| st.metric( | |
| label="2์ฐจ ๊ฒ์ฆ ๋๊ธฐ", | |
| value=f"{overview_llm_pending:,}๊ฑด", | |
| help="์์ง LLM 2์ฐจ ๊ฒ์ฆ์ด ์ ๋ ๊ฑด์", | |
| ) | |
| with pipe5: | |
| confirm_rate = (overview_llm_confirmed / overview_llm_done * 100) if overview_llm_done > 0 else 0 | |
| st.metric( | |
| label="์ต์ข ์ ํ", | |
| value=f"{overview_llm_confirmed:,}๊ฑด", | |
| delta=f"์ ํ๋ฅ {confirm_rate:.1f}%", | |
| help="1์ฐจ + 2์ฐจ ๊ฒ์ฆ ๋ชจ๋์์ ๋ถ์ ์ผ๋ก ํ์ ๋ ๊ฑด์", | |
| ) | |
| def _render_llm_verification_summary(campaign_overview: dict): | |
| """LLM 2์ฐจ ๊ฒ์ฆ ์์ฝ.""" | |
| st.markdown("##### ๐ค LLM 2์ฐจ ๊ฒ์ฆ") | |
| overview_llm_done = campaign_overview.get("llm_verified_in_house", 0) | |
| overview_llm_pending = campaign_overview.get("llm_pending", 0) | |
| overview_llm_confirmed = campaign_overview.get("llm_confirmed_negative", 0) | |
| if overview_llm_done > 0: | |
| fp_count = overview_llm_done - overview_llm_confirmed | |
| fp_rate = (fp_count / overview_llm_done * 100) if overview_llm_done > 0 else 0 | |
| st.metric( | |
| label="๊ฒ์ฆ ์๋ฃ", | |
| value=f"{overview_llm_done}๊ฑด", | |
| delta=f"์คํ {fp_count}๊ฑด ({fp_rate:.0f}%)", | |
| delta_color="inverse", | |
| ) | |
| st.caption(f"โ ์ ํ: {overview_llm_confirmed}๊ฑด | โ ์คํ: {fp_count}๊ฑด") | |
| if overview_llm_pending > 0: | |
| st.caption(f"โณ ๋๊ธฐ: {overview_llm_pending}๊ฑด") | |
| elif overview_llm_pending > 0: | |
| st.info(f"โณ {overview_llm_pending}๊ฑด ๊ฒ์ฆ ๋๊ธฐ ์ค") | |
| else: | |
| st.info("๊ฒ์ฆ ๋ฐ์ดํฐ ์์") | |