Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import pandas as pd | |
| import numpy as np | |
| from pptx import Presentation | |
| from pptx.enum.shapes import MSO_SHAPE_TYPE | |
| import re | |
| import io | |
| import time | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| # CSS ์คํ์ผ ์ ์ | |
| def local_css(): | |
| st.markdown( | |
| """ | |
| <style> | |
| .main .block-container { | |
| max-width: 1200px; # ์ต๋ ๋๋น ์ค์ | |
| padding: 1rem 2rem; # ์ข์ฐ ์ฌ๋ฐฑ ์ถ๊ฐ | |
| margin: 0 auto; # ์ค์ ์ ๋ ฌ | |
| } | |
| # ๊ธฐ์กด CSS ์คํ์ผ ์ ์ง | |
| .sidebar-option { | |
| padding: 10px 15px; | |
| margin: 5px 0; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| transition: background-color 0.3s; | |
| } | |
| .sidebar-option:hover { | |
| background-color: #ff4b4b20; | |
| } | |
| .sidebar-option.selected { | |
| background-color: #ff4b4b; | |
| color: white; | |
| } | |
| div[data-testid="stSidebar"] ul { | |
| padding-left: 0; | |
| } | |
| .stProgress .st-bo { | |
| background-color: #ff4b4b; | |
| } | |
| button[kind="secondary"] { | |
| background-color: transparent; | |
| color: #ff4b4b; | |
| border: 1px solid #ff4b4b; | |
| } | |
| button[kind="secondary"]:hover { | |
| border-color: #ff2b2b; | |
| color: #ff2b2b; | |
| } | |
| div.stButton > button:first-child { | |
| width: 100%; | |
| margin-bottom: 10px; | |
| } | |
| .custom-subheader { | |
| font-size: 1.2rem; | |
| color: #0068c9; | |
| margin-bottom: 1rem; | |
| } | |
| </style> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| # ์ฌ์ด๋๋ฐ ๋ฉ๋ด ์์ฑ | |
| def sidebar_menu(): | |
| """์ฌ์ด๋๋ฐ ๋ฉ๋ด ์์ฑ ๋ฐ ์ํ ๊ด๋ฆฌ""" | |
| st.sidebar.title("๋ฉ๋ด") | |
| menu_options = ["1) ๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํตํฉ", "2) ๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํต๊ณ", "3) PPT ์ฃผ์์๋ต ์ถ์ถ"] | |
| # ์ธ์ ์ํ ์ด๊ธฐํ | |
| if "current_menu" not in st.session_state: | |
| st.session_state.current_menu = menu_options[0] | |
| # ๊ฐ ๋ฉ๋ด ์ต์ ์ ๋ํ ๋ฒํผ ์์ฑ | |
| for option in menu_options: | |
| button_style = ( | |
| "secondary" if st.session_state.current_menu != option else "primary" | |
| ) | |
| if st.sidebar.button( | |
| option, | |
| key=f"btn_{option}", | |
| help=f"{option} ํ์ด์ง๋ก ์ด๋", | |
| use_container_width=True, | |
| type=button_style, | |
| ): | |
| st.session_state.current_menu = option | |
| st.rerun() | |
| return st.session_state.current_menu | |
| # ํ์ผ ์ฝ๊ธฐ ํจ์ | |
| def read_uploaded_file(uploaded_file, show_success=False): | |
| """ํฅ์๋ ํ์ผ ์ฝ๊ธฐ ํจ์""" | |
| try: | |
| # ํ์ผ ๊ฐ์ฒด ๊ฒ์ฆ | |
| if uploaded_file is None: | |
| return None | |
| # ํ์ผ ํฌ๊ธฐ ํ์ธ (1GB ์ ํ) | |
| MAX_FILE_SIZE = 1024 * 1024 * 1024 # 1GB in bytes | |
| if uploaded_file.size > MAX_FILE_SIZE: | |
| st.error(f"ํ์ผ ํฌ๊ธฐ๊ฐ ๋๋ฌด ํฝ๋๋ค. ์ต๋ 1GB๊น์ง ํ์ฉ๋ฉ๋๋ค.") | |
| return None | |
| file_type = uploaded_file.name.split(".")[-1].lower() | |
| # ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ธ ํ์ผ ์ฝ๊ธฐ | |
| if file_type == "csv": | |
| try: | |
| df = pd.read_csv(uploaded_file, low_memory=True) | |
| except Exception as e: | |
| st.error(f"CSV ํ์ผ ์ฝ๊ธฐ ์ค๋ฅ: {str(e)}") | |
| return None | |
| else: | |
| try: | |
| df = pd.read_excel(uploaded_file, engine="openpyxl") | |
| except Exception as e: | |
| st.error(f"Excel ํ์ผ ์ฝ๊ธฐ ์ค๋ฅ: {str(e)}") | |
| return None | |
| if show_success: | |
| st.success(f"ํ์ผ '{uploaded_file.name}' ๋ก๋ ์๋ฃ") | |
| return df | |
| except Exception as e: | |
| st.error(f"ํ์ผ ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| return None | |
| # ์ค๋ณต ํค ํ์ธ ํจ์ | |
| def check_duplicates(df, key_columns, context=""): | |
| """๋ฐ์ดํฐํ๋ ์์ ์ค๋ณต ํค ํ์ธ""" | |
| duplicates = df[df.duplicated(subset=key_columns, keep=False)] | |
| if not duplicates.empty: | |
| st.warning(f"{context}์์ ์ค๋ณต ํค๊ฐ ๋ฐ๊ฒฌ๋์์ต๋๋ค:") | |
| st.dataframe(duplicates[key_columns]) | |
| return True | |
| return False | |
| # ์์ ๋ค์ด๋ก๋ ๋ฐ์ดํฐ ์์ฑ | |
| def create_excel_download(df): | |
| """์์ ๋ค์ด๋ก๋์ฉ ๋ฐ์ดํฐ ์์ฑ""" | |
| output = io.BytesIO() | |
| with pd.ExcelWriter(output, engine="openpyxl") as writer: | |
| df.to_excel(writer, index=False) | |
| processed_data = output.getvalue() | |
| return processed_data | |
| def clean_column_names(df): | |
| """์ปฌ๋ผ๋ช ์ ๋ฆฌ""" | |
| clean_cols = {} | |
| for col in df.columns: | |
| # ์์ชฝ ๋ฒํธ์ ์ ์ ๊ฑฐ | |
| clean_col = re.sub(r"^\d+\.\s*", "", str(col)) | |
| clean_col = clean_col.strip() | |
| clean_cols[col] = clean_col | |
| return clean_cols | |
| # ํ์ฌ ๋ณ์ ์์ ํ์ ํจ์๋ฅผ ์์ | |
| def display_current_order(): | |
| """๋ณ์ ์์ ํ์ ํจ์""" | |
| st.write("### ํ์ฌ ๋ณ์ ๊ตฌ์ฑ") | |
| # ํ์ฌ ์์ ๊ฐ์ ธ์ค๊ธฐ (์์ ์์๊ฐ ์์ผ๋ฉด ์ฌ์ฉ) | |
| current_order = ( | |
| st.session_state.temp_column_order | |
| if "temp_column_order" in st.session_state | |
| else st.session_state.column_order | |
| ) | |
| # ํ์ฌ ๊ทธ๋ฃน ์์์ ๋ฐ๋ผ ์ปฌ๋ผ ๋ฐฐ์น | |
| if st.session_state.current_group_order[0] == "๊ฐ์ธ์ ๋ณด ๋ณ์": | |
| col1_title = "๐ ๊ฐ์ธ์ ๋ณด ๋ณ์" | |
| col2_title = "๐ ์๋ฒ ์ด ์๋ต ๋ณ์" | |
| col1_cols = [ | |
| c for c in current_order if c in st.session_state.personal_info_cols | |
| ] | |
| col2_cols = [ | |
| c for c in current_order if c not in st.session_state.personal_info_cols | |
| ] | |
| else: | |
| col1_title = "๐ ์๋ฒ ์ด ์๋ต ๋ณ์" | |
| col2_title = "๐ ๊ฐ์ธ์ ๋ณด ๋ณ์" | |
| col1_cols = [ | |
| c for c in current_order if c not in st.session_state.personal_info_cols | |
| ] | |
| col2_cols = [ | |
| c for c in current_order if c in st.session_state.personal_info_cols | |
| ] | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.write(f"##### {col1_title}") | |
| for i, col in enumerate(col1_cols, 1): | |
| st.write(f"{i}. {col}") | |
| st.info(f"์ด {len(col1_cols)}๊ฐ ๋ณ์") | |
| with col2: | |
| st.write(f"##### {col2_title}") | |
| for i, col in enumerate(col2_cols, 1): | |
| st.write(f"{i}. {col}") | |
| st.info(f"์ด {len(col2_cols)}๊ฐ ๋ณ์") | |
| # ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ์ ํจ์ | |
| def display_data_preview(): | |
| """๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ์ ํจ์""" | |
| st.write("### ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| if st.session_state.combined_survey is not None: | |
| # ํ์ฌ ์ปฌ๋ผ ์์ ์ฌ์ฉ (temp_column_order๊ฐ ์์ผ๋ฉด ์ฐ์ ์ฌ์ฉ) | |
| current_columns = ( | |
| st.session_state.temp_column_order | |
| if "temp_column_order" in st.session_state | |
| else st.session_state.final_column_order | |
| if "final_column_order" in st.session_state | |
| else st.session_state.column_order | |
| ) | |
| st.dataframe(st.session_state.combined_survey[current_columns].head()) | |
| # ๋งค์นญ ํค ๋ฐ์ดํฐ ์ ๋ฆฌ ํจ์ ์์ | |
| def clean_matching_key(df, key_columns): | |
| """๋งค์นญ ํค ๋ฐ์ดํฐ ์ ๋ฆฌ""" | |
| try: | |
| # ๋จผ์ key_columns์ด df์ ์๋์ง ํ์ธ | |
| missing_cols = [col for col in key_columns if col not in df.columns] | |
| if missing_cols: | |
| st.error(f"๋ค์ ์ปฌ๋ผ์ด ๋ฐ์ดํฐ์ ์กด์ฌํ์ง ์์ต๋๋ค: {', '.join(missing_cols)}") | |
| return None | |
| df_clean = df.copy() | |
| # ํค ์ปฌ๋ผ ๋ฐ์ดํฐ ์ ๋ฆฌ | |
| for col in key_columns: | |
| # null ๊ฐ ์ฒ๋ฆฌ | |
| df_clean[col] = df_clean[col].fillna("") | |
| # ๋ฌธ์์ด๋ก ๋ณํ | |
| df_clean[col] = df_clean[col].astype(str) | |
| # ๊ณต๋ฐฑ ์ ๊ฑฐ | |
| df_clean[col] = df_clean[col].str.strip() | |
| # ํน์๋ฌธ์ ์ ๊ฑฐ | |
| df_clean[col] = df_clean[col].str.replace(r"[^\w\s]", "", regex=True) | |
| # ๋์๋ฌธ์ ํต์ผ | |
| df_clean[col] = df_clean[col].str.upper() | |
| return df_clean | |
| except Exception as e: | |
| st.error(f"ํค ๋ฐ์ดํฐ ์ ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| return None | |
| # ๋งค์นญ ํค ์ฑ ํจ์ ์์ | |
| def create_match_key(df, key_columns): | |
| """๋งค์นญ ํค ์์ฑ""" | |
| try: | |
| # null ์ฒดํฌ | |
| if df[key_columns].isnull().any().any(): | |
| st.warning("๋งค์นญ ํค ์ปฌ๋ผ์ ๋๋ฝ ๊ฐ์ด ์์ต๋๋ค.") | |
| # ํค ์ปฌ๋ผ๋ค์ ๊ฒฐํฉํ์ฌ ํ๋์ ๋งค์นญ ํค ์์ฑ | |
| return ( | |
| df[key_columns].fillna("").astype(str).apply(lambda x: "_".join(x), axis=1) | |
| ) | |
| except Exception as e: | |
| st.error(f"๋งค์นญ ํค ์์ฑ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| return None | |
| def survey_integration(): | |
| """๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํตํฉ ๊ธฐ๋ฅ""" | |
| try: | |
| st.header("๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํตํฉ") | |
| # ์ธ์ ์ํ ์ด๊ธฐํ | |
| if "survey_files_processed" not in st.session_state: | |
| st.session_state.survey_files_processed = False | |
| if "combined_survey" not in st.session_state: | |
| st.session_state.combined_survey = None | |
| if "personal_info_cols" not in st.session_state: | |
| st.session_state.personal_info_cols = [] | |
| if "first_df_columns" not in st.session_state: | |
| st.session_state.first_df_columns = None | |
| if "column_order" not in st.session_state: | |
| st.session_state.column_order = [] | |
| if "final_column_order" not in st.session_state: | |
| st.session_state.final_column_order = [] | |
| # ์ธ์ ์ํ ์ด๊ธฐํ ๋ถ๋ถ์ show_target_section ์ถ๊ฐ | |
| if "show_target_section" not in st.session_state: | |
| st.session_state.show_target_section = False | |
| # 1. ๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํ์ผ ํตํฉ | |
| st.markdown( | |
| '<p class="custom-subheader">Daily Survey File Upload</p>', | |
| unsafe_allow_html=True, | |
| ) | |
| st.info("๋์ผํ ํ์์ Daily Survey ํ์ผ์ ์ผ์ฐจ๋ณ๋ก ์ ํํด์ฃผ์ธ์") | |
| survey_files = st.file_uploader( | |
| "๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํ์ผ๋ค์ ์ ๋ก๋ํ์ธ์", | |
| type=["xlsx", "xls", "csv"], | |
| accept_multiple_files=True, | |
| key="survey_files_upload", | |
| ) | |
| if survey_files: # ํ์ผ์ด ์ ๋ก๋๋ ๊ฒฝ์ฐ | |
| first_df = read_uploaded_file(survey_files[0]) # ์ฒซ ๋ฒ์งธ ํ์ผ ์ฝ๊ธฐ | |
| if first_df is not None: | |
| # ์ฒซ๋ฒ์งธ ํ์ผ ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ | |
| st.write("### First Data Preview") | |
| st.dataframe(first_df.head()) | |
| st.write("### ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ ์ ํ") | |
| # ์ปฌ๋ผ ์ ํ ์ํ ์ด๊ธฐํ | |
| if "temp_selected_cols" not in st.session_state: | |
| st.session_state.temp_selected_cols = [] | |
| col1, col2 = st.columns([3, 1]) | |
| with col1: | |
| st.info("์ง๋ฌธ์ ์ ์ธํ๊ณ '๋ฐ๋ณต๋๋ ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ'์ ๋ชจ๋ ์ ํํด์ฃผ์ธ์.") | |
| # ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ ์ ํ UI | |
| st.session_state.temp_selected_cols = st.multiselect( | |
| "๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ ์ ํ", | |
| options=first_df.columns.tolist(), | |
| default=st.session_state.personal_info_cols, | |
| help="Ctrl(Cmd)ํค๋ฅผ ๋๋ฅธ ์ฑ๋ก ํด๋ฆญํ์ฌ ์ฌ๋ฌ ์ปฌ๋ผ์ ํ ๋ฒ์ ์ ํํ ์ ์์ต๋๋ค.", | |
| ) | |
| with col2: | |
| st.write("") | |
| st.write("") | |
| if st.button("์ ํ ์๋ฃ", use_container_width=True): | |
| if not st.session_state.temp_selected_cols: | |
| st.error("์ต์ 1๊ฐ ์ด์์ ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ์ ์ ํํด์ฃผ์ธ์.") | |
| else: | |
| st.session_state.personal_info_cols = ( | |
| st.session_state.temp_selected_cols.copy() | |
| ) | |
| st.success( | |
| f"{len(st.session_state.personal_info_cols)}๊ฐ ์ปฌ๋ผ์ด ์ ํ๋์์ต๋๋ค:" | |
| ) | |
| for col in st.session_state.personal_info_cols: | |
| st.write(f"- {col}") | |
| # ํ์ ๋ฒํผ | |
| if st.button("๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ ํ์ "): | |
| if not st.session_state.temp_selected_cols: | |
| st.error("์ ํ๋ ์ปฌ๋ผ์ด ์์ต๋๋ค.") | |
| else: | |
| st.session_state.personal_info_cols = ( | |
| st.session_state.temp_selected_cols.copy() | |
| ) | |
| st.success( | |
| f"์ ํ๋ ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ ({len(st.session_state.personal_info_cols)}๊ฐ): " | |
| + ", ".join(st.session_state.personal_info_cols) | |
| ) | |
| st.rerun() | |
| if st.session_state.personal_info_cols: | |
| with st.spinner("์๋ฒ ์ด ํ์ผ ์ฒ๋ฆฌ ์ค..."): | |
| all_surveys = [] | |
| processed_count = 0 | |
| # 1๋จ๊ณ: ๋ชจ๋ ํ์ผ์ ์ปฌ๋ผ ๋งคํ ์์ฑ | |
| column_mapping = {} # ์๋ณธ ์ปฌ๋ผ๋ช -> ์ ๋ฆฌ๋ ์ปฌ๋ผ๋ช | |
| # ๊ฐ ํ์ผ์ ์ปฌ๋ผ๋ช ์ ์์งํ๊ณ ๋งคํ ์์ฑ | |
| for file in survey_files: | |
| df = read_uploaded_file(file, show_success=False) | |
| if df is not None: | |
| # ์ง๋ฌธ ์ปฌ๋ผ๋ง ์ฒ๋ฆฌ | |
| for col in df.columns: | |
| if col not in st.session_state.personal_info_cols: | |
| clean_col = re.sub( | |
| r"^\d+\.\s*", "", str(col) | |
| ).strip() | |
| if col not in column_mapping: | |
| column_mapping[col] = clean_col | |
| elif column_mapping[col] != clean_col: | |
| st.warning( | |
| f"์ฃผ์: ์ปฌ๋ผ '{col}'์ด(๊ฐ) ๋ค๋ฅธ ํ์์ผ ๋ํ๋ฉ๋๋ค: '{clean_col}' vs '{column_mapping[col]}'" | |
| ) | |
| # 2๋จ๊ณ: ๊ฐ ํ์ผ ์ ๋ฐ์ดํฐ ํตํฉ | |
| for file in survey_files: | |
| df = read_uploaded_file(file, show_success=False) | |
| if df is not None: | |
| # ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ ๋ณต์ฌ (๋ชจ๋ ์ปฌ๋ผ์ด ์กด์ฌํ๋์ง ํ์ธ) | |
| missing_cols = [ | |
| col | |
| for col in st.session_state.personal_info_cols | |
| if col not in df.columns | |
| ] | |
| if missing_cols: | |
| st.warning( | |
| f"ํ์ผ '{file.name}'์์ ๋ค์ ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ์ด ๋๋ฝ๋์์ต๋๋ค: {', '.join(missing_cols)}" | |
| ) | |
| continue | |
| df_clean = pd.DataFrame() | |
| # ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ ๋ณต์ฌ | |
| for col in st.session_state.personal_info_cols: | |
| df_clean[col] = df[col] | |
| # ์ง๋ฌธ ์ปฌ๋ผ ์ฒ๋ฆฌ | |
| for col in df.columns: | |
| if col not in st.session_state.personal_info_cols: | |
| if col in column_mapping: | |
| clean_col = column_mapping[col] | |
| df_clean[clean_col] = df[col] | |
| all_surveys.append(df_clean) | |
| processed_count += 1 | |
| if all_surveys: | |
| # ๋ชจ๋ ์ดํฐํ๋ ์์ ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ์ ๊ธฐ์ค์ผ๋ก ๋ณํฉ | |
| combined_df = all_surveys[0] | |
| for df in all_surveys[1:]: | |
| combined_df = pd.merge( | |
| combined_df, | |
| df, | |
| on=st.session_state.personal_info_cols, | |
| how="outer", | |
| ) | |
| # column_order ์ ๋ฐ์ดํธ (๊ฐ์ธ์ ๋ณด ๋ผ + ์ ๋ ฌ๋ ์ง๋ฌธ ์ปฌ) | |
| question_cols = sorted(set(column_mapping.values())) | |
| st.session_state.column_order = ( | |
| st.session_state.personal_info_cols + question_cols | |
| ) | |
| st.session_state.combined_survey = combined_df[ | |
| st.session_state.column_order | |
| ] | |
| st.success(f"์ด {processed_count}๊ฐ์ ์๋ฒ ์ด ํ์ผ์ด ์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ต๋๋ค.") | |
| # ํตํฉ๋ ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ | |
| st.write("### ํตํฉ๋ ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| total_responses = len(st.session_state.combined_survey) | |
| st.write(f"์ด {total_responses}๊ฐ์ ์๋ต์ด ํตํฉ๋์์ต๋๋ค.") | |
| st.dataframe(st.session_state.combined_survey.head()) | |
| # ์ด๊ธฐ column_order ์ค์ (์ฒ์ ํตํฉํ ๋๋ง) | |
| if "column_order" not in st.session_state: | |
| st.session_state.column_order = list( | |
| st.session_state.combined_survey.columns | |
| ) | |
| # ํ์ผ๋ณ ์๋ต ์ ์ ๋ณด๋ฅผ ํ ์ด๋ธ๋ก ํ์ | |
| st.write("### ํ์ผ๋ณ ์๋ต ์") | |
| # CSS๋ก ํ ์ด๋ธ ๋ด ํ ์คํธ ๊ฐ์ด๋ฐ ์ ๋ ฌ | |
| st.markdown( | |
| """ | |
| <style> | |
| [data-testid="stDataFrame"] div[data-testid="stDataFrameCell"] { | |
| justify-content: center; | |
| } | |
| </style> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| # ์๋ต ์ ๋ฐ์ดํฐ ์์ฑ | |
| response_data = pd.DataFrame( | |
| [[len(df) for df in all_surveys]], index=["์๋ต ์"] | |
| ) | |
| # ์ปฌ๋ผ๋ช ์ 'ํ์ผ 1', 'ํ์ผ 2' ๋ฑ์ผ๋ก ์ค์ | |
| response_data.columns = [ | |
| f"ํ์ผ {i+1}" for i in range(len(all_surveys)) | |
| ] | |
| # ๋ฐ์ดํฐ๋ ์ ํ์ | |
| st.dataframe( | |
| response_data, | |
| column_config={ | |
| col: st.column_config.Column(width=80) | |
| for col in response_data.columns | |
| }, | |
| hide_index=False, | |
| ) | |
| # ๋ณ์ ์ ๋ ฌ ๊ธฐ๋ฅ | |
| st.subheader("2. ๋ณ์ ์์ ์กฐ์ ") | |
| # ๋ณ์ ๊ทธ๋ฃน ์ค์ | |
| personal_cols = st.session_state.personal_info_cols | |
| survey_cols = [ | |
| col | |
| for col in st.session_state.column_order | |
| if col not in personal_cols | |
| ] | |
| st.write("### ๋ณ์ ์์ ๊ด๋ฆฌ") | |
| tab1, tab2 = st.tabs(["๐ ์ผ๊ด ๋ณ๊ฒฝ", "๐ ์ธ๋ถ ์กฐ์ "]) | |
| with tab1: | |
| st.write("##### ๋ณ์ ๊ทธ๋ฃน๋ณ ์์ ์ค์ ") | |
| if "current_group_order" not in st.session_state: | |
| st.session_state.current_group_order = [ | |
| "๊ฐ์ธ์ ๋ณด ๋ณ์", | |
| "์๋ฒ ์ด ์๋ต ๋ณ์", | |
| ] | |
| if st.session_state.combined_survey is not None: | |
| personal_cols = st.session_state.personal_info_cols | |
| survey_cols = [ | |
| col | |
| for col in st.session_state.column_order | |
| if col | |
| not in st.session_state.personal_info_cols | |
| ] | |
| group_order = st.multiselect( | |
| "๋ณ์ ๊ทธ๋ฃน ์์๋ฅผ ์ ํํ์ธ์", | |
| options=["๊ฐ์ธ์ ๋ณด ๋ณ์", "์๋ฒ ์ด ์๋ต ๋ณ์"], | |
| default=st.session_state.current_group_order, | |
| help="๋ณ์ ๊ทธ๋ฃน์ ์์๋ฅผ ๋๋๊ทธํ์ฌ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค", | |
| ) | |
| # ํ์ฌ ๊ทธ๋ฃน๋ณ ๋ณ์ ์ ํ์ | |
| st.info( | |
| f""" | |
| ํ์ฌ ๋ณ์ ์ฑ: | |
| - ๊ฐ์ธ์ ๋ณด ๋ณ์: {len(personal_cols)}๊ฐ | |
| - ์๋ฒ ์ด ์๋ต ๋ณ์: {len(survey_cols)}๊ฐ | |
| """ | |
| ) | |
| if st.button("๊ทธ๋ฃน ์์ ์ ์ฉ"): | |
| if not group_order: # group_order๊ฐ ๋น์ด์๋ ๊ฒฝ์ฐ ์ฒดํฌ | |
| st.error("๋ณ์ ๊ทธ๋ฃน์ ์ ํํด์ฃผ์ธ์.") | |
| elif ( | |
| len(group_order) != 2 | |
| ): # ๋ ๊ทธ๋ฃน์ด ๋ชจ๋ ์ ํ๋์ง ์์ ๊ฒฝ์ฐ | |
| st.error("๋ ๊ทธ๋ฃน์ ๋ชจ๋ ์ ํํด์ฃผ์ธ์.") | |
| else: | |
| try: | |
| new_order = [] | |
| for group in group_order: | |
| if group == "๊ฐ์ธ์ ๋ณด ๋ณ์": | |
| new_order.extend(personal_cols) | |
| elif group == "์๋ฒ ์ด ์๋ต ๋ณ์": | |
| new_order.extend( | |
| sorted(survey_cols) | |
| ) | |
| if new_order: | |
| # session_state ์ ๋ฐ์ดํธ | |
| st.session_state.column_order = ( | |
| new_order.copy() | |
| ) | |
| st.session_state.temp_column_order = ( | |
| new_order.copy() | |
| ) | |
| st.session_state.current_group_order = ( | |
| group_order | |
| ) | |
| # ๋ฐ์ดํฐํ๋ ์ ์ปฌ๋ผ ์์ ๋ณ๊ฒฝ | |
| if all( | |
| col | |
| in st.session_state.combined_survey.columns | |
| for col in new_order | |
| ): | |
| st.session_state.combined_survey = st.session_state.combined_survey[ | |
| new_order | |
| ] | |
| st.success("๋ณ์ ๊ทธ๋ฃน ์์๊ฐ ๋ณ๊ฒฝ๋์์ต๋๋ค!") | |
| # ๋ณ๊ฒฝ๋ ์์ ํ์ | |
| display_current_order() | |
| display_data_preview() | |
| else: | |
| missing_cols = [ | |
| col | |
| for col in new_order | |
| if col | |
| not in st.session_state.combined_survey.columns | |
| ] | |
| st.error( | |
| f"๋ค์ ์ปฌ๋ผ์ด ๋ฐ์ดํฐํ๋ ์์ ์์ต๋๋ค: {missing_cols}" | |
| ) | |
| else: | |
| st.error("์๋ก์ด ์์๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.") | |
| except Exception as e: | |
| st.error( | |
| f"์์ ๋ณ๊ฒฝ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}" | |
| ) | |
| # ๋ณ์ ์์ ํ์ ๋ฒํผ ์์ | |
| if st.button( | |
| "ํ์ฌ ๋ณ์ ์์ ํ์ ", | |
| type="primary", | |
| use_container_width=True, | |
| key="confirm_order_bulk", | |
| ): | |
| st.session_state.final_column_order = ( | |
| st.session_state.column_order.copy() | |
| ) | |
| st.session_state.temp_column_order = ( | |
| st.session_state.column_order.copy() | |
| ) | |
| st.session_state.combined_survey = ( | |
| st.session_state.combined_survey[ | |
| st.session_state.column_order | |
| ] | |
| ) | |
| st.success("โ ๋ณ์ ์์๊ฐ ํ์ ๋์์ต๋๋ค!") | |
| st.session_state.show_target_section = True | |
| st.rerun() | |
| # ์ผ๊ด ๋ณ๊ฒฝ ํญ ๋ด๋ถ์ ๋์์ ๋ช ๋จ ํตํฉ ์น์ ์ถ๊ฐ | |
| st.markdown("---") | |
| st.subheader("3. ๋์์ ๋ช ๋จ๊ณผ ํตํฉ") | |
| st.info("๐ฅ ํตํฉ๋ ์๋ฒ ์ด ๋ฐ์ดํฐ์ ๋์์ ๋ช ๋จ์ ๋งค์นญํ ์ ์์ต๋๋ค.") | |
| if "final_column_order" in st.session_state: | |
| target_file = st.file_uploader( | |
| "๋์์ ๋ช ๋จ ํ์ผ์ ์ ๋ก๋ํ์ธ์", | |
| type=["xlsx", "xls", "csv"], | |
| key="target_file_upload_detail", | |
| ) | |
| if target_file: | |
| target_df = read_uploaded_file(target_file) | |
| if target_df is not None: | |
| st.write("### ๋์์ ๋ช ๋จ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| st.dataframe(target_df.head()) | |
| # ํค ๋งค์นญ ์ค์ UI | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.write("### ์๋ฒ ์ด ๋ฐ์ดํฐ ํค ์ ํ") | |
| # ํ์ ๋ ์์๊ฐ ์์ผ๋ฉด ๊ทธ ์์๋ฅผ ์ฌ์ฉ | |
| survey_columns = ( | |
| st.session_state.final_column_order | |
| if "final_column_order" | |
| in st.session_state | |
| else st.session_state.column_order | |
| ) | |
| survey_keys = st.multiselect( | |
| "๋งค์นญํ ์๋ฒ ์ด ๋ฐ์ดํฐ์ ์ปฌ๋ผ์ ์ ํํ์ธ์", | |
| survey_columns, | |
| max_selections=2, | |
| help="ํ์ฌ, ์ฌ๋ฒ๊ณผ ๊ฐ์ด ๊ณ ์ ํ ์๋ณ์ด ๊ฐ๋ฅํ ์ปฌ๋ผ 2๊ฐ๋ฅผ ์ ํํ์ธ์", | |
| ) | |
| with col2: | |
| st.write("### ๋์์ ๋ช ๋จ ํค ์ ํ") | |
| target_keys = st.multiselect( | |
| "๋งค์นญํ ๋์์ ๋ช ๋จ์ ์ปฌ๋ผ์ ์ ํํ์ธ์", | |
| target_df.columns.tolist(), | |
| max_selections=2, | |
| help="์๋ฒ ์ด ๋ฐ์ดํฐ์ ๋งค์นญ๋ ์ปฌ๋ผ 2๊ฐ๋ฅผ ์ ํํ์ธ์", | |
| ) | |
| if ( | |
| len(survey_keys) == 2 | |
| and len(target_keys) == 2 | |
| ): | |
| try: | |
| # ์๋ณธ ๋ฐ์ดํฐ ๋ณต์ฌ | |
| survey_df = ( | |
| st.session_state.combined_survey.copy() | |
| ) | |
| target_df_copy = target_df.copy() | |
| # ๋ฐ์ดํฐ ์ ๋ฆฌ ํจ์ | |
| def clean_key_value(value): | |
| # ๊ฐ์ ๋ฌธ์์ด๋ก ๋ณํ | |
| value = str(value) | |
| # ์์์ ์ ๊ฑฐ (.0 ์ ๊ฑฐ) | |
| if value.endswith(".0"): | |
| value = value[:-2] | |
| # ๊ณต๋ฐฑ ์ ๊ฑฐ | |
| value = value.strip() | |
| # ๋ชจ๋ ๊ณต๋ฐฑ ์ ๊ฑฐ | |
| value = value.replace(" ", "") | |
| # ๋๋ฌธ์ ๋ณํ | |
| value = value.upper() | |
| return value | |
| # ๋ฐ์ดํฐ ์ ๋ฆฌ | |
| for col in survey_keys: | |
| survey_df[col] = survey_df[ | |
| col | |
| ].fillna("") | |
| survey_df[col] = survey_df[ | |
| col | |
| ].apply(clean_key_value) | |
| for col in target_keys: | |
| target_df_copy[ | |
| col | |
| ] = target_df_copy[col].fillna( | |
| "" | |
| ) | |
| target_df_copy[ | |
| col | |
| ] = target_df_copy[col].apply( | |
| clean_key_value | |
| ) | |
| # ๋งค์นญ ํค ์์ฑ | |
| survey_df["match_key"] = survey_df[ | |
| survey_keys | |
| ].agg("_".join, axis=1) | |
| target_df_copy[ | |
| "match_key" | |
| ] = target_df_copy[target_keys].agg( | |
| "_".join, axis=1 | |
| ) | |
| # ๋ฐ์ดํฐ ํตํฉ | |
| final_df = pd.merge( | |
| target_df_copy, | |
| survey_df, | |
| on="match_key", | |
| how="left", | |
| indicator=True, | |
| ) | |
| # ๋งค์นญ ๊ฒฐ๊ณผ ๋ถ์ | |
| total_targets = len(target_df_copy) | |
| matched = len( | |
| final_df[ | |
| final_df["_merge"] == "both" | |
| ] | |
| ) | |
| unmatched = len( | |
| final_df[ | |
| final_df["_merge"] | |
| == "left_only" | |
| ] | |
| ) | |
| col1, col2, col3 = st.columns(3) | |
| col1.metric( | |
| "์ ์ฒด ๋์์ ์", total_targets | |
| ) | |
| col2.metric("๋งค์นญ๋ ๋์์ ์", matched) | |
| col3.metric("๋ฏธ๋งค์นญ ๋์์ ์", unmatched) | |
| # ๋ฏธ๋งค์นญ ๋ฐ์ดํฐ ํ์ธ | |
| if unmatched > 0: | |
| st.warning( | |
| "โ ๏ธ ๋งค์นญ๋์ง ์์ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค." | |
| ) | |
| if st.button("๋ฏธ๋งค์นญ ๋ฐ์ดํฐ ํ์ธ"): | |
| unmatched_df = final_df[ | |
| final_df["_merge"] | |
| == "left_only" | |
| ] | |
| st.dataframe( | |
| unmatched_df[ | |
| target_keys | |
| ] | |
| ) | |
| # ์ต์ข ๋ฐ์ดํฐ ์ ๋ฆฌ | |
| final_df = final_df.drop( | |
| ["match_key", "_merge"], axis=1 | |
| ) | |
| # ํ์ ๋ ๋ณ์ ์ ์ ์ฉ | |
| if ( | |
| "final_column_order" | |
| in st.session_state | |
| ): | |
| try: | |
| # ๋์์ ๋ช ๋จ์ ์ปฌ๋ผ๋ค์ ์์ชฝ์ ๋ฐฐ์น | |
| target_columns = [ | |
| col | |
| for col in target_df.columns | |
| if col | |
| not in target_keys | |
| ] | |
| # ์ค์ ๋ก ์กด์ฌํ๋ ์๋ฒ ์ด ์ปฌ๋ผ๋ง ํํฐ๋ง | |
| survey_columns = [ | |
| col | |
| for col in st.session_state.final_column_order | |
| if col | |
| in final_df.columns | |
| ] | |
| # ์ต์ข ์ปฌ๋ผ ์์ ์ค์ (์ค์ ์กด์ฌํ๋ ์ปฌ๋ผ๋ง ํฌํจ) | |
| final_columns = [] | |
| # ๋์์ ๋ช ๋จ ์ปฌ๋ผ ์ถ๊ฐ | |
| for col in target_columns: | |
| if ( | |
| col | |
| in final_df.columns | |
| ): | |
| final_columns.append( | |
| col | |
| ) | |
| # ์๋ฒ ์ด ์ปฌ๋ผ ์ถ๊ฐ | |
| final_columns.extend( | |
| survey_columns | |
| ) | |
| # ๋๋ฝ๋ ์ปฌ๋ผ์ด ์๋ค๋ฉด ๋ง์ง๋ง์ ์ถ๊ฐ | |
| remaining_cols = [ | |
| col | |
| for col in final_df.columns | |
| if col | |
| not in final_columns | |
| ] | |
| final_columns.extend( | |
| remaining_cols | |
| ) | |
| # ์ปฌ๋ผ ์์ ์ฌ๋ฐฐ์น | |
| final_df = final_df[ | |
| final_columns | |
| ] | |
| except Exception as e: | |
| st.warning( | |
| f"์ปฌ๋ผ ์์ ์ฌ๋ฐฐ์น ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}" | |
| ) | |
| st.info("๊ธฐ๋ณธ ์์๋ก ๋ฐ์ดํฐ๋ฅผ ํ์ํฉ๋๋ค.") | |
| st.write("### ์ต์ข ๋ฐ์ดํฐ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| st.dataframe(final_df.head()) | |
| # ๋ค์ด๋ก๋ ๋ฒํผ | |
| final_processed_data = ( | |
| create_excel_download(final_df) | |
| ) | |
| st.download_button( | |
| label="์ต์ข ํตํฉ ๋ฐ์ดํฐ์ ๋ค์ด๋ก๋", | |
| data=final_processed_data, | |
| file_name="์ต์ข _ํตํฉ_๋ฐ์ดํฐ์ .xlsx", | |
| mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| ) | |
| except Exception as e: | |
| st.error( | |
| f"๋ฐ์ดํฐ ํตํฉ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}" | |
| ) | |
| with tab2: | |
| st.write("##### ๐ ๊ฐ๋ณ ๋ณ์ ์ด๋") | |
| if ( | |
| "combined_survey" in st.session_state | |
| and st.session_state.combined_survey is not None | |
| ): | |
| # ์ปฌ๋ผ ์์ ์ด๊ธฐํ ํ์ธ | |
| if "column_order" not in st.session_state: | |
| st.session_state.column_order = list( | |
| st.session_state.combined_survey.columns | |
| ) | |
| # ์์ ์ปฌ๋ผ ์์ ์ด๊ธฐํ | |
| if "temp_column_order" not in st.session_state: | |
| st.session_state.temp_column_order = ( | |
| st.session_state.column_order.copy() | |
| ) | |
| col1, col2 = st.columns([2, 1]) | |
| with col1: | |
| # ๋ณ์ ์ ํ UI | |
| available_columns = ( | |
| st.session_state.temp_column_order | |
| ) | |
| if not available_columns: | |
| available_columns = list( | |
| st.session_state.combined_survey.columns | |
| ) | |
| selected_cols = st.multiselect( | |
| "์ด๋ํ ๋ณ์๋ค์ ์ ํํ์ธ์", | |
| options=available_columns, | |
| format_func=lambda x: f"๐ {x}" | |
| if x in st.session_state.personal_info_cols | |
| else f"๐ {x}", | |
| key="cols_to_move_select", | |
| ) | |
| with col2: | |
| st.write("") | |
| st.write("") | |
| move_type = st.radio( | |
| "์ด๋ ๋ฐฉ์ ์ ํ", | |
| [ | |
| "๋งจ ์์ผ๋ก ์ด๋ โฌ๏ธ", | |
| "๋งจ ๋ค๋ก ์ด๋ โฌ๏ธ", | |
| "ํน์ ๋ณ์ ๊ธฐ์ค ์ด๋ โ๏ธ", | |
| ], | |
| key="move_type_radio", | |
| ) | |
| # ํน์ ๋ณ์ ๊ธฐ์ค ์ด๋์ผ ๋์ ์ถ๊ฐ ์ต์ | |
| if ( | |
| move_type == "ํน์ ๋ณ์ ๊ธฐ์ค ์ด๋ โ๏ธ" | |
| and selected_cols | |
| ): | |
| remaining_cols = [ | |
| col | |
| for col in available_columns | |
| if col not in selected_cols | |
| ] | |
| if remaining_cols: | |
| reference_col = st.selectbox( | |
| "๊ธฐ์ค ๋ณ์ ์ ํ", | |
| options=remaining_cols, | |
| format_func=lambda x: f"๐ {x}" | |
| if x | |
| in st.session_state.personal_info_cols | |
| else f"๐ {x}", | |
| key="reference_col_select", | |
| ) | |
| move_position = st.radio( | |
| "์ด๋ ์์น", | |
| options=[ | |
| "์ ํํ ๋ณ์ ์์ผ๋กโฌ๏ธ", | |
| "์ ํํ ๋ณ์ ๋ค๋กโฌ๏ธ", | |
| ], | |
| key="position_radio", | |
| ) | |
| else: | |
| st.warning( | |
| "์ ํํ ๋ณ์๋ฅผ ์ ์ธํ ์ด๋ ๊ฐ๋ฅํ ๊ธฐ์ค ๋ณ์๊ฐ ์์ต๋๋ค." | |
| ) | |
| elif move_type == "ํน์ ๋ณ์ ๊ธฐ์ค ์ด๋ โ๏ธ": | |
| st.info("์ด๋ํ ๋ณ์๋ฅผ ๋จผ์ ์ ํํด์ฃผ์ธ์.") | |
| # ๋ณ์ ์ด๋ ์คํ ๋ฒํผ | |
| move_button_disabled = not selected_cols or ( | |
| move_type == "ํน์ ๋ณ์ ๊ธฐ์ค ์ด๋ โ๏ธ" | |
| and ( | |
| not remaining_cols | |
| if "remaining_cols" in locals() | |
| else True | |
| ) | |
| ) | |
| if st.button( | |
| "์ ํํ ๋ณ์ ์ด๋", | |
| key="move_selected_vars", | |
| use_container_width=True, | |
| disabled=move_button_disabled, | |
| ): | |
| try: | |
| new_order = [ | |
| col | |
| for col in st.session_state.temp_column_order | |
| if col not in selected_cols | |
| ] | |
| if move_type == "๋งจ ์์ผ๋ก ์ด๋ โฌ๏ธ": | |
| new_order = selected_cols + new_order | |
| elif move_type == "๋งจ ๋ค๋ก ์ด๋ โฌ๏ธ": | |
| new_order = new_order + selected_cols | |
| elif ( | |
| move_type == "ํน์ ๋ณ์ ๊ธฐ์ค ์ด๋ โ๏ธ" | |
| and "reference_col_select" | |
| in st.session_state | |
| ): | |
| ref_idx = new_order.index( | |
| st.session_state.reference_col_select | |
| ) | |
| if ( | |
| st.session_state.position_radio | |
| == "์ ํํ ๋ณ์ ์์ผ๋กโฌ๏ธ" | |
| ): | |
| new_order = ( | |
| new_order[:ref_idx] | |
| + selected_cols | |
| + new_order[ref_idx:] | |
| ) | |
| else: | |
| new_order = ( | |
| new_order[: ref_idx + 1] | |
| + selected_cols | |
| + new_order[ref_idx + 1 :] | |
| ) | |
| # ์์ ์ ๋ฐ์ดํธ | |
| st.session_state.temp_column_order = ( | |
| new_order | |
| ) | |
| st.session_state.combined_survey = ( | |
| st.session_state.combined_survey[ | |
| new_order | |
| ] | |
| ) | |
| st.success("โ ๋ณ์ ์์๊ฐ ๋ณ๊ฒฝ๋์์ต๋๋ค!") | |
| # ํ์ฌ ์์ ๋ฐ ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ์ | |
| display_current_order() | |
| st.write("### ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| st.dataframe( | |
| st.session_state.combined_survey[ | |
| new_order | |
| ].head() | |
| ) | |
| st.rerun() | |
| except Exception as e: | |
| st.error(f"๋ณ์ ์ด๋ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}") | |
| else: | |
| # ํ์ฌ ์์ ๋ฐ ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ํ์ | |
| st.write("### ํ์ฌ ๋ณ์ ์์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| display_current_order() | |
| st.write("### ๋ฐ์ดํฐ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| st.dataframe( | |
| st.session_state.combined_survey[ | |
| st.session_state.temp_column_order | |
| ].head() | |
| ) | |
| else: | |
| st.warning("๋จผ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ก๋ํ๊ณ ๊ฐ์ธ์ ๋ณด ์ปฌ๋ผ์ ์ ํํด์ฃผ์ธ์.") | |
| # ๋ณ์ ์์ ํ์ ๋ฒํผ | |
| confirm_button = st.button( | |
| "ํ์ฌ ๋ณ์ ์์ ํ์ ", | |
| type="primary", | |
| use_container_width=True, | |
| key="confirm_order_detail", | |
| ) | |
| if confirm_button: | |
| st.session_state.final_column_order = ( | |
| st.session_state.temp_column_order.copy() | |
| ) | |
| st.session_state.column_order = ( | |
| st.session_state.temp_column_order.copy() | |
| ) | |
| st.session_state.combined_survey = ( | |
| st.session_state.combined_survey[ | |
| st.session_state.temp_column_order | |
| ] | |
| ) | |
| st.session_state.show_target_section = True | |
| st.success("โ ๋ณ์ ์์๊ฐ ํ์ ๋์์ต๋๋ค!") | |
| st.rerun() # ํ์ด์ง ๋ฆฌ๋ก๋ | |
| # ๋์์ ํตํฉ ์น์ ์ถ๊ฐ | |
| if "final_column_order" in st.session_state: | |
| st.markdown("---") | |
| st.subheader("3. ๋์์ ๋ช ๋จ๊ณผ ํตํฉ") | |
| st.info("๐ฅ ํตํฉ๋ ์๋ฒ ์ด ๋ฐ์ดํฐ์ ๋์์ ๋ช ๋จ์ ๋งค์นญํ ์ ์์ต๋๋ค.") | |
| target_file = st.file_uploader( | |
| "๋์์ ๋ช ๋จ ํ์ผ์ ์ ๋ก๋ํ์ธ์", | |
| type=["xlsx", "xls", "csv"], | |
| key="target_file_upload_detail_tab2", # ํค ๊ฐ์ ๋ค๋ฅด๊ฒ ์ค์ | |
| ) | |
| if target_file: | |
| target_df = read_uploaded_file(target_file) | |
| if target_df is not None: | |
| st.write("### ๋์์ ๋ช ๋จ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| st.dataframe(target_df.head()) | |
| # ํค ๋งค์นญ ์ค์ UI | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.write("### ์๋ฒ ์ด ๋ฐ์ดํฐ ํค ์ ํ") | |
| # ํ์ ๋ ์์๊ฐ ์์ผ๋ฉด ๊ทธ ์์๋ฅผ ์ฌ์ฉ | |
| survey_columns = ( | |
| st.session_state.final_column_order | |
| if "final_column_order" | |
| in st.session_state | |
| else st.session_state.column_order | |
| ) | |
| survey_keys = st.multiselect( | |
| "๋งค์นญํ ์๋ฒ ์ด ๋ฐ์ดํฐ์ ์ปฌ๋ผ์ ์ ํํ์ธ์", | |
| survey_columns, | |
| max_selections=2, | |
| help="ํ์ฌ, ์ฌ๋ฒ๊ณผ ๊ฐ์ด ๊ณ ์ ํ ์๋ณ์ด ๊ฐ๋ฅํ ์ปฌ๋ผ 2๊ฐ๋ฅผ ์ ํํ์ธ์", | |
| ) | |
| with col2: | |
| st.write("### ๋์์ ๋ช ๋จ ํค ์ ํ") | |
| target_keys = st.multiselect( | |
| "๋งค์นญํ ๋์์ ๋ช ๋จ์ ์ปฌ๋ผ์ ์ ํํ์ธ์", | |
| target_df.columns.tolist(), | |
| max_selections=2, | |
| help="์๋ฒ ์ด ๋ฐ์ดํฐ์ ๋งค์นญ๋ ์ปฌ๋ผ 2๊ฐ๋ฅผ ์ ํํ์ธ์", | |
| ) | |
| if ( | |
| len(survey_keys) == 2 | |
| and len(target_keys) == 2 | |
| ): | |
| try: | |
| # ์๋ณธ ๋ฐ์ดํฐ ๋ณต์ฌ | |
| survey_df = ( | |
| st.session_state.combined_survey.copy() | |
| ) | |
| target_df_copy = target_df.copy() | |
| # ๋ฐ์ดํฐ ์ ๋ฆฌ ํจ์ | |
| def clean_key_value(value): | |
| # ๊ฐ์ ๋ฌธ์์ด๋ก ๋ณํ | |
| value = str(value) | |
| # ์์์ ์ ๊ฑฐ (.0 ์ ๊ฑฐ) | |
| if value.endswith(".0"): | |
| value = value[:-2] | |
| # ๊ณต๋ฐฑ ์ ๊ฑฐ | |
| value = value.strip() | |
| # ๋ชจ๋ ๊ณต๋ฐฑ ์ ๊ฑฐ | |
| value = value.replace(" ", "") | |
| # ๋๋ฌธ์ ๋ณํ | |
| value = value.upper() | |
| return value | |
| # ๋ฐ์ดํฐ ์ ๋ฆฌ | |
| for col in survey_keys: | |
| survey_df[col] = survey_df[ | |
| col | |
| ].fillna("") | |
| survey_df[col] = survey_df[ | |
| col | |
| ].apply(clean_key_value) | |
| for col in target_keys: | |
| target_df_copy[ | |
| col | |
| ] = target_df_copy[col].fillna( | |
| "" | |
| ) | |
| target_df_copy[ | |
| col | |
| ] = target_df_copy[col].apply( | |
| clean_key_value | |
| ) | |
| # ๋งค์นญ ํค ์์ฑ | |
| survey_df["match_key"] = survey_df[ | |
| survey_keys | |
| ].agg("_".join, axis=1) | |
| target_df_copy[ | |
| "match_key" | |
| ] = target_df_copy[target_keys].agg( | |
| "_".join, axis=1 | |
| ) | |
| # ๋ฐ์ดํฐ ํตํฉ | |
| final_df = pd.merge( | |
| target_df_copy, | |
| survey_df, | |
| on="match_key", | |
| how="left", | |
| indicator=True, | |
| ) | |
| # ๋งค์นญ ๊ฒฐ๊ณผ ๋ถ์ | |
| total_targets = len(target_df_copy) | |
| matched = len( | |
| final_df[ | |
| final_df["_merge"] == "both" | |
| ] | |
| ) | |
| unmatched = len( | |
| final_df[ | |
| final_df["_merge"] | |
| == "left_only" | |
| ] | |
| ) | |
| col1, col2, col3 = st.columns(3) | |
| col1.metric( | |
| "์ ์ฒด ๋์์ ์", total_targets | |
| ) | |
| col2.metric("๋งค์นญ๋ ๋์์ ์", matched) | |
| col3.metric("๋ฏธ๋งค์นญ ๋์์ ์", unmatched) | |
| # ๋ฏธ๋งค์นญ ๋ฐ์ดํฐ ํ์ธ | |
| if unmatched > 0: | |
| st.warning( | |
| "โ ๏ธ ๋งค์นญ๋์ง ์์ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค." | |
| ) | |
| if st.button("๋ฏธ๋งค์นญ ๋ฐ์ดํฐ ํ์ธ"): | |
| unmatched_df = final_df[ | |
| final_df["_merge"] | |
| == "left_only" | |
| ] | |
| st.dataframe( | |
| unmatched_df[ | |
| target_keys | |
| ] | |
| ) | |
| # ์ต์ข ๋ฐ์ดํฐ ์ ๋ฆฌ | |
| final_df = final_df.drop( | |
| ["match_key", "_merge"], axis=1 | |
| ) | |
| # ํ์ ๋ ๋ณ์ ์ ์ ์ฉ | |
| if ( | |
| "final_column_order" | |
| in st.session_state | |
| ): | |
| try: | |
| # ๋์์ ๋ช ๋จ์ ์ปฌ๋ผ๋ค์ ์์ชฝ์ ๋ฐฐ์น | |
| target_columns = [ | |
| col | |
| for col in target_df.columns | |
| if col | |
| not in target_keys | |
| ] | |
| # ์ค์ ๋ก ์กด์ฌํ๋ ์๋ฒ ์ด ์ปฌ๋ผ๋ง ํํฐ๋ง | |
| survey_columns = [ | |
| col | |
| for col in st.session_state.final_column_order | |
| if col | |
| in final_df.columns | |
| ] | |
| # ์ต์ข ์ปฌ๋ผ ์์ ์ค์ (์ค์ ์กด์ฌํ๋ ์ปฌ๋ผ๋ง ํฌํจ) | |
| final_columns = [] | |
| # ๋์์ ๋ช ๋จ ์ปฌ๋ผ ์ถ๊ฐ | |
| for col in target_columns: | |
| if ( | |
| col | |
| in final_df.columns | |
| ): | |
| final_columns.append( | |
| col | |
| ) | |
| # ์๋ฒ ์ด ์ปฌ๋ผ ์ถ๊ฐ | |
| final_columns.extend( | |
| survey_columns | |
| ) | |
| # ๋๋ฝ๋ ์ปฌ๋ผ์ด ์๋ค๋ฉด ๋ง์ง๋ง์ ์ถ๊ฐ | |
| remaining_cols = [ | |
| col | |
| for col in final_df.columns | |
| if col | |
| not in final_columns | |
| ] | |
| final_columns.extend( | |
| remaining_cols | |
| ) | |
| # ์ปฌ๋ผ ์์ ์ฌ๋ฐฐ์น | |
| final_df = final_df[ | |
| final_columns | |
| ] | |
| except Exception as e: | |
| st.warning( | |
| f"์ปฌ๋ผ ์์ ์ฌ๋ฐฐ์น ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}" | |
| ) | |
| st.info("๊ธฐ๋ณธ ์์๋ก ๋ฐ์ดํฐ๋ฅผ ํ์ํฉ๋๋ค.") | |
| st.write("### ์ต์ข ๋ฐ์ดํฐ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ") | |
| st.dataframe(final_df.head()) | |
| # ๋ค์ด๋ก๋ ๋ฒํผ | |
| final_processed_data = ( | |
| create_excel_download(final_df) | |
| ) | |
| st.download_button( | |
| label="์ต์ข ํตํฉ ๋ฐ์ดํฐ์ ๋ค์ด๋ก๋", | |
| data=final_processed_data, | |
| file_name="์ต์ข _ํตํฉ_๋ฐ์ดํฐ์ .xlsx", | |
| mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| ) | |
| except Exception as e: | |
| st.error( | |
| f"๋ฐ์ดํฐ ํตํฉ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}" | |
| ) | |
| except Exception as e: | |
| st.error(f"์ฒ๋ฆฌ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค: {str(e)}") | |
| def extract_specific_text(file): | |
| """PPT์์ ํ ์คํธ ์ถ์ถ""" | |
| try: | |
| prs = Presentation(file) | |
| extracted_texts = [] | |
| for slide in prs.slides: | |
| slide_texts = [] | |
| for shape in slide.shapes: | |
| if hasattr(shape, "text") and shape.text.strip(): | |
| if shape.shape_type == MSO_SHAPE_TYPE.GROUP: | |
| for subshape in shape.shapes: | |
| if hasattr(subshape, "text") and subshape.text.strip(): | |
| slide_texts.append(subshape.text.strip()) | |
| else: | |
| slide_texts.append(shape.text.strip()) | |
| if slide_texts: | |
| extracted_texts.extend(slide_texts) | |
| return extracted_texts | |
| except Exception as e: | |
| st.error(f"PPT ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| return [] | |
| def clean_text_for_excel(text): | |
| """์์ ์์ ์ฌ์ฉํ ์ ์๋ ๋ฌธ์ ์ ๊ฑฐ""" | |
| if not isinstance(text, str): | |
| return text | |
| # ์ค๋ฐ๊ฟ, ํญ, carriage return ์ ๊ฑฐ | |
| text = text.replace("\n", " ").replace("\r", " ").replace("\t", " ") | |
| # ๋ถํ์ํ ๊ณต๋ฐฑ ์ ๊ฑฐ | |
| text = re.sub(r"\s+", " ", text).strip() | |
| return text | |
| def extract_responses(): | |
| """PPT ์ฃผ์์๋ต ์ถ์ถ ๋ฐ ๋ถ๋ฅ ๊ธฐ๋ฅ""" | |
| st.header("PPT ์ฃผ์์๋ต ์ถ์ถ ๋ฐ ๋ถ๋ฅ") | |
| # PPT ํ์ผ ์ ๋ก๋ | |
| uploaded_files = st.file_uploader( | |
| "PPT ํ์ผ์ ํํ์ธ์", type=["pptx"], accept_multiple_files=True, key="ppt_files" | |
| ) | |
| if uploaded_files: | |
| try: | |
| all_texts = [] | |
| progress_bar = st.progress(0) | |
| # PPT์์ ํ ์คํธ ์ถ์ถ | |
| for i, file in enumerate(uploaded_files): | |
| texts = extract_specific_text(file) | |
| if texts: | |
| with st.expander(f"ํ์ผ: {file.name} ์ถ์ถ ๊ฒฐ๊ณผ"): | |
| for text in texts: | |
| cleaned_text = clean_text_for_excel( | |
| re.sub(r"\s+", " ", text).strip() | |
| ) | |
| st.write(cleaned_text) | |
| all_texts.extend(texts) | |
| progress_bar.progress((i + 1) / len(uploaded_files)) | |
| if all_texts: | |
| st.success(f"์ด {len(all_texts)}๊ฐ์ ํ ์คํธ๊ฐ ์ถ์ถ๋์์ต๋๋ค!") | |
| # ์ ์ธํ ํ ์คํธ ์ค์ | |
| st.subheader("์ ์ธํ ํ ์คํธ ์ค์ ") | |
| excluded_texts_input = st.text_area( | |
| "์ ์ธํ ํ ์คํธ๋ฅผ ์ ๋ ฅํ์ธ์ (์ค ๋ฐ๊ฟ์ผ๋ก ๊ตฌ๋ถ)", help="์ ์ธํ ํ ์คํธ๋ฅผ ํ ์ค์ฉ ์ ๋ ฅํ์ธ์" | |
| ) | |
| excluded_texts = excluded_texts_input.splitlines() | |
| # ์ ์ธํ ํ ์คํธ ํํฐ๋ง | |
| filtered_texts = [ | |
| clean_text_for_excel(text) | |
| for text in all_texts | |
| if text not in excluded_texts | |
| ] | |
| st.write("### ํํฐ๋ง๋ ํ ์คํธ:") | |
| for text in filtered_texts: | |
| st.write(text) | |
| # ์ง๋ฌธ๋ณ ๋ถ๋ฅ ์ถ๊ฐ | |
| st.subheader("์ง๋ฌธ๋ณ ๋ถ๋ฅ") | |
| user_questions_input = st.text_area( | |
| "์ง๋ฌธ์ ์ ๋ ฅํ์ธ์ (์ค๋ฐ๊ฟ์ผ๋ก ๊ตฌ๋ถ)", help="๊ฐ ์ง๋ฌธ์ด ํ๋์ ์ด(ํค๋)์ด ๋ฉ๋๋ค" | |
| ) | |
| user_questions = user_questions_input.splitlines() | |
| if user_questions: | |
| # ์ง๋ฌธ๋ณ ๋ฐ์ดํฐํ๋ ์ ์์ฑ | |
| question_responses = {question: [] for question in user_questions} | |
| question_responses["๊ธฐํ"] = [] | |
| current_question = None | |
| # ํ ์คํธ ๋ถ๋ฅ | |
| for text in filtered_texts: | |
| matched = False | |
| for question in user_questions: | |
| if question in text: | |
| current_question = question | |
| matched = True | |
| break | |
| if current_question and not matched: | |
| question_responses[current_question].append(text) | |
| elif not matched and not current_question: | |
| question_responses["๊ธฐํ"].append(text) | |
| # ๋ฐ์ดํฐํ๋ ์์ผ๋ก ๋ณํ | |
| max_rows = max( | |
| len(responses) for responses in question_responses.values() | |
| ) | |
| for question, responses in question_responses.items(): | |
| if len(responses) < max_rows: | |
| question_responses[question].extend( | |
| [""] * (max_rows - len(responses)) | |
| ) | |
| df = pd.DataFrame(question_responses) | |
| st.write("### ์ง๋ฌธ๋ณ ๋ถ๋ฅ ๊ฒฐ๊ณผ:") | |
| st.dataframe(df) | |
| # ๊ฒฐ๊ณผ ๋ค์ด๋ก๋ | |
| output = io.BytesIO() | |
| with pd.ExcelWriter(output, engine="openpyxl") as writer: | |
| df.applymap(clean_text_for_excel).to_excel(writer, index=False) | |
| st.download_button( | |
| label="๋ถ๋ฅ ๊ฒฐ๊ณผ ๋ค์ด๋ก๋", | |
| data=output.getvalue(), | |
| file_name="์ง๋ฌธ๋ณ_๋ถ๋ฅ_๊ฒฐ๊ณผ.xlsx", | |
| mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", | |
| ) | |
| except Exception as e: | |
| st.error(f"์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์: {str(e)}") | |
| def analyze_survey_data(): | |
| """๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํต๊ณ ๋ถ์ ๊ธฐ๋ฅ""" | |
| # ํ์คํ ํค ์์ ํ๋ ํธ ํจ์ ์์ | |
| def get_gradient_colors(n, color_type="blue"): | |
| """๊ทธ๋ผ๋ฐ์ด์ ์์ ์์ฑ""" | |
| if color_type == "blue": | |
| # ํ๋์ ๊ณ์ด ๊ทธ๋ผ๋ฐ์ด์ | |
| base_rgb = (186, 225, 255) # ๊ธฐ๋ณธ ํ๋์ | |
| else: | |
| # ํ์ ๊ณ์ด ๊ทธ๋ผ๋ฐ์ด์ | |
| base_rgb = (200, 200, 200) # ๊ธฐ๋ณธ ํ์ | |
| colors = [] | |
| for i in range(n): | |
| opacity = 0.3 + (0.7 * i / (n - 1)) if n > 1 else 1 | |
| colors.append( | |
| f"rgba({base_rgb[0]}, {base_rgb[1]}, {base_rgb[2]}, {opacity})" | |
| ) | |
| return colors | |
| st.header("๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํต๊ณ ๋ถ์") | |
| uploaded_file = st.file_uploader( | |
| "ํต๊ณ ๋ถ์ํ ํตํฉ ๋ฐ์ดํฐ ํ์ผ์ ์ ๋ก๋ํ์ธ์", type=["xlsx", "xls", "csv"], key="stats_file_upload" | |
| ) | |
| if uploaded_file: | |
| df = read_uploaded_file(uploaded_file) | |
| if df is not None: | |
| st.success("๋ฐ์ดํฐ ๋ก๋ ์๋ฃ!") | |
| # ๊ณผ๋ชฉ ๋ณ์ ์๋ ์๋ณ ๋ฐ ๊ทธ๋ฃนํ | |
| subject_vars = [col for col in df.columns if "[" in col and "]" in col] | |
| if not subject_vars: | |
| st.warning("๊ณผ๋ชฉ ๊ด๋ จ ๋ณ์([๊ณผ๋ชฉ๋ช ])๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.") | |
| return | |
| # ๊ณผ๋ชฉ๋ช ์ถ์ถ ๋ฐ ๊ทธ๋ฃนํ | |
| subject_groups = {} | |
| for col in subject_vars: | |
| subject_name = re.search(r"\[(.*?)\]", col).group(1) | |
| if subject_name not in subject_groups: | |
| subject_groups[subject_name] = [] | |
| subject_groups[subject_name].append(col) | |
| # ๊ณผ๋ชฉ๋ณ ํ๊ท ๊ณ์ฐ | |
| subject_means = {} | |
| for subject, columns in subject_groups.items(): | |
| subject_means[subject] = df[columns].mean(axis=1).mean() | |
| # ๊ณผ๋ชฉ ์์ ์ ํ UI | |
| st.subheader("1. ์ ์ฒด ๊ณผ๋ชฉ๋ณ ํ๊ท ์ ์") | |
| subject_order = st.multiselect( | |
| "๊ณผ๋ชฉ ์์๋ฅผ ์ ํํ์ธ์", | |
| options=list(subject_means.keys()), | |
| default=list(subject_means.keys()), | |
| ) | |
| if subject_order: | |
| # ์ ํ๋ ์์๋๋ก ๋ฐ์ดํฐ ์ฌ๊ตฌ์ฑ | |
| ordered_means = { | |
| subject: float(subject_means[subject]) for subject in subject_order | |
| } # float๋ก ์ ์ง | |
| # ์ ์ฒด ํ๊ท ๊ณ์ฐ | |
| total_mean = sum(ordered_means.values()) / len(ordered_means) | |
| ordered_means["์ ์ฒด"] = total_mean | |
| # ๊ณผ๋ชฉ๋ณ ํ๊ท ์ ์ ํ ์ด๋ธ ๋ฐ์ดํฐ (์์์ 2์๋ฆฌ ๋ฌธ์์ด) | |
| display_means = {k: f"{v:.2f}" for k, v in ordered_means.items()} | |
| mean_df = pd.DataFrame([display_means], index=["ํ๊ท ์ ์"]) | |
| st.dataframe(mean_df) | |
| # ๊ฐ์ ๋ฐ๋ฅธ ์์ ์ค์ | |
| values = list(ordered_means.values()) # float ๊ฐ ์ฌ์ฉ | |
| max_val = max(values) | |
| min_val = min(values) | |
| colors = ["#BAE1FF" for _ in values] | |
| # ์ต๋๊ฐ๊ณผ ์ต์๊ฐ ์์ ๋ณ๊ฒฝ | |
| for i, v in enumerate(values): | |
| if v == max_val: | |
| colors[i] = "#BAFFC9" | |
| elif v == min_val: | |
| colors[i] = "#FF9B9B" | |
| # ๊ณผ๋ชฉ๋ณ ํ๊ท ์ ์ ์ฐจํธ | |
| fig = go.Figure( | |
| data=[ | |
| go.Bar( | |
| x=list(ordered_means.keys()), | |
| y=values, # float ๊ฐ ์ฌ์ฉ | |
| text=[f"{v:.2f}" for v in values], # ์ฌ๊ธฐ์ ํฌ๋งทํ | |
| textposition="outside", | |
| marker_color=colors, | |
| textfont=dict(size=12), | |
| ) | |
| ] | |
| ) | |
| fig.update_layout( | |
| title="์ ์ฒด ๊ณผ๋ชฉ๋ณ ํ๊ท ์ ์", | |
| xaxis_title="๊ณผ๋ชฉ", | |
| yaxis_title="ํ๊ท ์ ์", | |
| showlegend=False, | |
| bargap=0.3, | |
| uniformtext_minsize=8, | |
| uniformtext_mode="hide", | |
| ) | |
| st.plotly_chart(fig) | |
| # ๊ฐ์ธ์ ๋ณด ๋ณ์ ์ ํ UI | |
| personal_info_vars = [col for col in df.columns if col not in subject_vars] | |
| selected_personal_vars = st.multiselect( | |
| "๋ถ์์ ์ฌ์ฉํ ๊ฐ์ธ์ ๋ณด ๋ณ์๋ฅผ ์ ํํ์ธ์", | |
| options=personal_info_vars, | |
| help="๊ณผ๋ชฉ๋ณ ํ๊ท ์ ์ ๋น๊ต์ ์ฌ์ฉํ ๊ฐ์ธ์ ๋ณด ๋ณ์๋ค์ ์ ํํ์ธ์", | |
| ) | |
| if selected_personal_vars: | |
| st.subheader("2. ๊ฐ์ธ์ ๋ณด ๋ณ์๋ณ ๊ณผ๋ชฉ ํ๊ท ๋ถ์") | |
| # ๊ณผ๋ชฉ๋ณ ํ๊ท ์ ๊ณ์ฐํ ์๋ก์ด ๋ฐ์ดํฐํ๋ ์ ์์ฑ | |
| subject_scores = pd.DataFrame() | |
| for subject in subject_order: | |
| subject_scores[subject] = df[subject_groups[subject]].mean(axis=1) | |
| # ์ ์ฒด ํ๊ท ๊ณ์ฐ | |
| subject_scores["์ ์ฒด"] = subject_scores[subject_order].mean(axis=1) | |
| # ๊ฐ์ธ์ ๋ณด ๋ณ์ ์ถ๊ฐ | |
| for var in selected_personal_vars: | |
| subject_scores[var] = df[var] | |
| # ๊ฐ ์ ํ๋ ๊ฐ์ธ์ ๋ณด ๋ณ์๋ณ ๋ถ์ | |
| for var in selected_personal_vars: | |
| st.write(f"#### {var}์ ๋ฐ๋ฅธ ๊ณผ๋ชฉ๋ณ ํ๊ท ์ ์") | |
| # ๊ทธ๋ฃน๋ณ ํ๊ท ๊ณ์ฐ | |
| ordered_cols = list(ordered_means.keys()) | |
| grouped_means = ( | |
| subject_scores.groupby(var)[ordered_cols].mean().round(2) | |
| ) | |
| # ํ ์ด๋ธ ํ์ | |
| st.write("๊ทธ๋ฃน๋ณ ํ๊ท ์ ์:") | |
| st.dataframe(grouped_means) | |
| # ๋ง๋ ๊ทธ๋ํ 1: ๊ณผ๋ชฉ๋ณ ๋ฒ๋ก๊ฐ ๊ฐ์ธ์ ๋ณด ๋ณ์ | |
| fig1 = go.Figure() | |
| # ๋ฒ์ฃผ๋ณ๋ก ๋ค๋ฅธ ์์ ์ฌ์ฉ | |
| colors = get_gradient_colors(len(grouped_means.index)) | |
| # ๊ฐ ๋ฒ์ฃผ๋ณ๋ก ๋ง๋ ๊ทธ๋ํ ์์ฑ | |
| for i, category in enumerate(grouped_means.index): | |
| fig1.add_trace( | |
| go.Bar( | |
| name=category, # ๋ฒ์ฃผ ์ด๋ฆ | |
| x=ordered_cols, # x์ถ์ ๊ณผ๋ชฉ์ผ๋ก ๋ณ๊ฒฝ | |
| y=grouped_means.loc[category], | |
| text=[f"{v:.2f}" for v in grouped_means.loc[category]], | |
| textposition="outside", | |
| marker_color=colors[i], # ๋ฒ์ฃผ๋ณ ์์ ์ง์ | |
| textfont=dict(size=12), | |
| ) | |
| ) | |
| fig1.update_layout( | |
| title=f"{var}๋ณ ๊ณผ๋ชฉ ํ๊ท ์ ์", | |
| xaxis_title="๊ณผ๋ชฉ", | |
| yaxis_title="ํ๊ท ์ ์", | |
| barmode="group", | |
| bargap=0.15, | |
| bargroupgap=0.1, | |
| uniformtext_minsize=8, | |
| uniformtext_mode="hide", | |
| # x์ถ ๋ ์ด๋ธ ํ์ ๋ฐ ์์น ์กฐ์ | |
| xaxis=dict( | |
| tickangle=45, | |
| tickmode="array", | |
| ticktext=ordered_cols, | |
| tickvals=list(range(len(ordered_cols))), | |
| tickfont=dict(size=12), | |
| ), | |
| # y์ถ ๋ฒ์ ์ค์ | |
| yaxis=dict( | |
| range=[0, 5], # ์ต๋๊ฐ์ 5๋ก ์ค์ | |
| ), | |
| # ์ฌ๋ฐฑ ์กฐ์ | |
| margin=dict( | |
| b=150, # ํ๋จ ์ฌ๋ฐฑ | |
| t=100, # ์๋จ ์ฌ๋ฐฑ ์ถ๊ฐ | |
| ), | |
| # ๋ฒ๋ก ์์น ์กฐ์ | |
| legend=dict( | |
| orientation="h", | |
| yanchor="bottom", | |
| y=1.02, | |
| xanchor="right", | |
| x=1, | |
| ), | |
| ) | |
| st.plotly_chart(fig1) | |
| # ๋ง๋ ๊ทธ๋ํ 2: ๊ฐ์ธ์ ๋ณด ๋ณ์๋ณ ๋ฒ๋ก๊ฐ ๊ณผ๋ชฉ | |
| fig2 = go.Figure() | |
| # ํ์ ๊ณ์ด ๊ทธ๋ผ๋ฐ์ด์ ์์ ์์ฑ | |
| colors = get_gradient_colors(len(ordered_cols), color_type="gray") | |
| # ๊ณผ๋ชฉ๋ณ๋ก ๋ง๋ ๊ทธ๋ํ ์์ฑ | |
| for i, subject in enumerate(ordered_cols): | |
| # ๊ฐ ๊ณผ๋ชฉ๋ณ ๋ฐ์ดํฐ ์ค๋น | |
| subject_values = grouped_means[subject] | |
| # ํธ๋ฒ ํ ์คํธ ์์ฑ | |
| hover_texts = [] | |
| for idx, value in enumerate(subject_values): | |
| category_name = grouped_means.index[idx] | |
| hover_text = ( | |
| f"{var}: {category_name}<br>{subject}: {value:.2f}" | |
| ) | |
| hover_texts.append(hover_text) | |
| fig2.add_trace( | |
| go.Bar( | |
| name=subject, | |
| x=grouped_means.index, | |
| y=subject_values, | |
| text=[f"{v:.2f}" for v in subject_values], | |
| textposition="outside", | |
| marker_color=colors[i], | |
| textfont=dict(size=12, color="black"), | |
| hovertext=hover_texts, | |
| hoverinfo="text", | |
| ) | |
| ) | |
| # ๋ ์ด์์ ๋ฐ์ดํธ (๋๋จธ์ง ์ฝ๋๋ ๋์ผ) | |
| fig2.update_layout( | |
| title=f"{var}๋ณ ๊ณผ๋ชฉ ํ๊ท ์ ์ (๊ณผ๋ชฉ๋ณ ๋ฒ๋ก)", | |
| xaxis_title=var, | |
| yaxis_title="ํ๊ท ์ ์", | |
| barmode="group", | |
| bargap=0.15, | |
| bargroupgap=0.1, | |
| uniformtext_minsize=8, | |
| uniformtext_mode="hide", | |
| xaxis=dict( | |
| tickangle=45, | |
| tickmode="array", | |
| ticktext=grouped_means.index, | |
| tickvals=list(range(len(grouped_means.index))), | |
| tickfont=dict(size=12), | |
| ), | |
| yaxis=dict( | |
| range=[0, 5], | |
| ), | |
| margin=dict( | |
| b=150, | |
| t=100, | |
| ), | |
| legend=dict( | |
| orientation="h", | |
| yanchor="bottom", | |
| y=1.02, | |
| xanchor="right", | |
| x=1, | |
| ), | |
| ) | |
| st.plotly_chart(fig2) | |
| def main(): | |
| """๋ฉ์ธ ์ ํ์ผ์ด์ ํจ์""" | |
| st.set_page_config( | |
| page_title="์์นด๋ฐ๋ฏธ ๋ฐ์ดํฐ ํตํฉ ํ๋ก๊ทธ๋จ", | |
| page_icon="๐", | |
| layout="wide", | |
| initial_sidebar_state="expanded", | |
| menu_items={ | |
| "Get Help": None, | |
| "Report a bug": None, | |
| "About": "ยฉ 2024 SK mySUNI", | |
| }, | |
| ) | |
| # CSS ์คํ์ผ ์ ์ฉ | |
| local_css() | |
| # ํค์ ํ์ดํ | |
| st.markdown( | |
| """ | |
| <div style='text-align: center; margin-bottom: 2rem;'> | |
| <h1>์์นด๋ฐ๋ฏธ ๋ฐ์ดํฐ ํตํฉ ํ๋ก๊ทธ๋จ</h1> | |
| <p style='color: #666; font-size: 1.2em;'>๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํตํฉ | ๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํต๊ณ | PPT ์ฃผ์์๋ต ์ถ์ถ</p> | |
| </div> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| # ์ฌ์ด๋ ๋ฉ๋ด | |
| selected_menu = sidebar_menu() | |
| # ํ์ด์ง ๋ผ์ฐํ | |
| if st.session_state.current_menu == "1) ๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํตํฉ": | |
| survey_integration() | |
| elif st.session_state.current_menu == "2) ๋ฐ์ผ๋ฆฌ ์๋ฒ ์ด ํต๊ณ": | |
| analyze_survey_data() | |
| elif st.session_state.current_menu == "3) PPT ์ฃผ์์๋ต ์ถ์ถ": | |
| extract_responses() | |
| # app.py์ ๋งจ ๋ง์ง๋ง์ ์ถ๊ฐ | |
| if __name__ == "__main__": | |
| main() | |
| """ | |
| Copyright ยฉ 2024 SK mySUNI | |
| Created by ๋ฐฐ์์ RF (soojeong.bae@sk.com) | |
| All Rights Reserved. | |
| """ | |