Spaces:
Sleeping
Sleeping
File size: 7,088 Bytes
ef78361 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | """๊ฐ์ฑ๋ถ์ 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("๊ฒ์ฆ ๋ฐ์ดํฐ ์์")
|