Spaces:
Sleeping
Sleeping
| """ | |
| Export Utilities - ํตํฉ ๋ด๋ณด๋ด๊ธฐ ์ปดํฌ๋ํธ | |
| ๊ณตํต ๋ด๋ณด๋ด๊ธฐ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค: | |
| - CSV/Excel ๋ณํ | |
| - ํํฐ๋ง ์ต์ | |
| - ์ ์ฒด ๋ต๋ณ ํฌํจ ์ต์ | |
| """ | |
| import pandas as pd | |
| import streamlit as st | |
| from io import BytesIO | |
| from typing import Callable | |
| from .supabase_client import get_sentiment_data_for_export | |
| from .athena_client import fetch_full_answers_batch | |
| # openpyxl ์ค์น ์ฌ๋ถ ํ์ธ (Excel export์ฉ) | |
| try: | |
| import openpyxl | |
| EXCEL_AVAILABLE = True | |
| except ImportError: | |
| EXCEL_AVAILABLE = False | |
| # LLM ๊ฒ์ฆ ์ํ ๋ผ๋ฒจ (์ฉ์ด ํต์ผ) | |
| LLM_STATUS_LABELS = { | |
| "all": "์ ์ฒด", | |
| "verified": "๊ฒ์ฆ์๋ฃ", | |
| "false_positive": "์คํ (๋ถ์ โ๋น๋ถ์ )", # ๋ถ์ ์๋ | |
| "true_negative": "์ ํ (๋ถ์ ํ์ )", # ๋ถ์ ํ์ | |
| "unverified": "๋ฏธ๊ฒ์ฆ", | |
| } | |
| POLARITY_LABELS = { | |
| "all": "์ ์ฒด", | |
| "negative": "๋ถ์ ", | |
| "positive": "๊ธ์ ", | |
| "neutral": "์ค๋ฆฝ", | |
| } | |
| def prepare_dataframe_for_export( | |
| data: list[dict], | |
| include_full_answers: bool = False, | |
| ) -> pd.DataFrame: | |
| """๋ฐ์ดํฐ๋ฅผ DataFrame์ผ๋ก ๋ณํํ๊ณ ๋ด๋ณด๋ด๊ธฐ์ฉ์ผ๋ก ์ ๋ฆฌํฉ๋๋ค. | |
| Args: | |
| data: ๋ด๋ณด๋ผ ๋ฐ์ดํฐ ๋ฆฌ์คํธ | |
| include_full_answers: ์ ์ฒด ๋ต๋ณ ํฌํจ ์ฌ๋ถ | |
| Returns: | |
| ์ ๋ฆฌ๋ DataFrame | |
| """ | |
| if not data: | |
| return pd.DataFrame() | |
| df = pd.DataFrame(data) | |
| # ๋ฆฌ์คํธ ์ปฌ๋ผ์ ๋ฌธ์์ด๋ก ๋ณํ | |
| list_columns = ['in_house_brands', 'mentioned_brands', 'llm_evidence_spans'] | |
| for col in list_columns: | |
| if col in df.columns: | |
| df[col] = df[col].apply( | |
| lambda x: ', '.join(x) if isinstance(x, list) else str(x) if x else '' | |
| ) | |
| # ์ปฌ๋ผ ์์ ์ ๋ฆฌ - answer_full์ answer_preview ๋ค์์ ๋ฐฐ์น | |
| if 'answer_full' in df.columns and 'answer_preview' in df.columns: | |
| cols = list(df.columns) | |
| cols.remove('answer_full') | |
| idx = cols.index('answer_preview') + 1 | |
| cols.insert(idx, 'answer_full') | |
| df = df[cols] | |
| return df | |
| def export_to_csv(df: pd.DataFrame) -> bytes: | |
| """DataFrame์ CSV ๋ฐ์ดํธ๋ก ๋ณํํฉ๋๋ค.""" | |
| return df.to_csv(index=False).encode('utf-8-sig') | |
| def export_to_excel(df: pd.DataFrame) -> bytes | None: | |
| """DataFrame์ Excel ๋ฐ์ดํธ๋ก ๋ณํํฉ๋๋ค. | |
| Returns: | |
| Excel ๋ฐ์ดํธ ๋ฐ์ดํฐ, ๋๋ openpyxl์ด ์์ผ๋ฉด None | |
| """ | |
| if not EXCEL_AVAILABLE: | |
| return None | |
| output = BytesIO() | |
| with pd.ExcelWriter(output, engine='openpyxl') as writer: | |
| df.to_excel(writer, index=False, sheet_name='Data') | |
| return output.getvalue() | |
| def render_export_component( | |
| campaign_id: int, | |
| key_prefix: str, | |
| title: str = "๐ฅ ๋ฐ์ดํฐ ๋ด๋ณด๋ด๊ธฐ", | |
| show_polarity_filter: bool = True, | |
| show_llm_filter: bool = True, | |
| default_polarity: str = "negative", | |
| default_llm_status: str = "all", | |
| in_house_only: bool = True, | |
| ): | |
| """ํตํฉ ๋ด๋ณด๋ด๊ธฐ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํฉ๋๋ค. | |
| Args: | |
| campaign_id: ์บ ํ์ธ ID | |
| key_prefix: Streamlit ์์ ฏ ํค ์ ๋์ฌ (์ค๋ณต ๋ฐฉ์ง) | |
| title: ์น์ ์ ๋ชฉ | |
| show_polarity_filter: ๊ฐ์ ํํฐ ํ์ ์ฌ๋ถ | |
| show_llm_filter: LLM ์ํ ํํฐ ํ์ ์ฌ๋ถ | |
| default_polarity: ๊ธฐ๋ณธ ๊ฐ์ ํํฐ ๊ฐ | |
| default_llm_status: ๊ธฐ๋ณธ LLM ์ํ ํํฐ ๊ฐ | |
| in_house_only: ์์ฌ ๋ธ๋๋๋ง ํํฐ๋ง | |
| """ | |
| with st.expander(title, expanded=False): | |
| # ํํฐ ์ต์ | |
| filter_col1, filter_col2 = st.columns(2) | |
| with filter_col1: | |
| if show_polarity_filter: | |
| polarity_options = list(POLARITY_LABELS.keys()) | |
| polarity_labels = list(POLARITY_LABELS.values()) | |
| default_idx = polarity_options.index(default_polarity) if default_polarity in polarity_options else 0 | |
| selected_polarity = st.selectbox( | |
| "๊ฐ์ ํํฐ", | |
| options=polarity_options, | |
| format_func=lambda x: POLARITY_LABELS[x], | |
| index=default_idx, | |
| key=f"{key_prefix}_polarity" | |
| ) | |
| else: | |
| selected_polarity = default_polarity | |
| with filter_col2: | |
| if show_llm_filter: | |
| llm_options = list(LLM_STATUS_LABELS.keys()) | |
| default_idx = llm_options.index(default_llm_status) if default_llm_status in llm_options else 0 | |
| selected_llm_status = st.selectbox( | |
| "LLM ๊ฒ์ฆ ์ํ", | |
| options=llm_options, | |
| format_func=lambda x: LLM_STATUS_LABELS[x], | |
| index=default_idx, | |
| key=f"{key_prefix}_llm_status" | |
| ) | |
| else: | |
| selected_llm_status = default_llm_status | |
| # ๋ด๋ณด๋ด๊ธฐ ์ต์ | |
| opt_col1, opt_col2 = st.columns(2) | |
| with opt_col1: | |
| include_full_answers = st.checkbox( | |
| "์ ์ฒด ๋ต๋ณ ํฌํจ", | |
| value=False, | |
| help="Athena์์ ์ ์ฒด ๋ต๋ณ์ ๊ฐ์ ธ์ต๋๋ค (ํ์ผ ํฌ๊ธฐ ์ฆ๊ฐ)", | |
| key=f"{key_prefix}_full_answers" | |
| ) | |
| with opt_col2: | |
| include_evidence = st.checkbox( | |
| "LLM ๊ทผ๊ฑฐ ํฌํจ", | |
| value=False, | |
| help="LLM ํ๋จ ๊ทผ๊ฑฐ(reasoning, evidence_spans)๋ฅผ ํฌํจํฉ๋๋ค", | |
| key=f"{key_prefix}_evidence" | |
| ) | |
| st.markdown("---") | |
| # ๋ค์ด๋ก๋ ๋ฒํผ | |
| btn_col1, btn_col2, btn_col3 = st.columns([1, 1, 2]) | |
| # ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ | |
| data = get_sentiment_data_for_export( | |
| campaign_id=campaign_id, | |
| polarity=selected_polarity if selected_polarity != "all" else None, | |
| llm_status=selected_llm_status if selected_llm_status != "all" else None, | |
| in_house_only=in_house_only, | |
| include_full_answers=include_full_answers, | |
| include_evidence=include_evidence, | |
| ) | |
| if data: | |
| df = prepare_dataframe_for_export(data, include_full_answers) | |
| count = len(df) | |
| with btn_col1: | |
| csv_data = export_to_csv(df) | |
| st.download_button( | |
| label=f"๐ฅ CSV ({count}๊ฑด)", | |
| data=csv_data, | |
| file_name=f"campaign_{campaign_id}_export_{count}๊ฑด.csv", | |
| mime="text/csv", | |
| key=f"{key_prefix}_csv_download" | |
| ) | |
| with btn_col2: | |
| if EXCEL_AVAILABLE: | |
| excel_data = export_to_excel(df) | |
| st.download_button( | |
| label=f"๐ฅ Excel ({count}๊ฑด)", | |
| data=excel_data, | |
| file_name=f"campaign_{campaign_id}_export_{count}๊ฑด.xlsx", | |
| mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| key=f"{key_prefix}_excel_download" | |
| ) | |
| else: | |
| st.caption("Excel: openpyxl ํ์") | |
| with btn_col3: | |
| st.caption(f"์ด {count}๊ฑด | ํํฐ: {POLARITY_LABELS.get(selected_polarity, '์ ์ฒด')} / {LLM_STATUS_LABELS.get(selected_llm_status, '์ ์ฒด')}") | |
| else: | |
| st.info("๋ด๋ณด๋ผ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.") | |