import streamlit as st import pandas as pd import os from src.FisrtModule.module1 import MisconceptionModel from src.SecondModule.module2 import SimilarQuestionGenerator from src.ThirdModule.module3 import AnswerVerifier import logging from typing import Optional, Tuple from latex_formatter import LatexFormatter logging.basicConfig(level=logging.DEBUG) # Initialize Misconception Model @st.cache_resource def load_misconception_model(): return MisconceptionModel( model_name="minsuas/Misconceptions__1", misconception_mapping_path=os.path.join(data_path, 'misconception_mapping.parquet'), misconception_embs_paths=[os.path.join(data_path, f'embs_misconception-9-9.npy')] ) # Streamlit 페이지 기본 설정 st.set_page_config( page_title="MisconcepTutor", layout="wide", initial_sidebar_state="expanded" ) @st.cache_resource def load_answer_verifier(): """답안 검증 모델 로드""" from src.ThirdModule.module3 import AnswerVerifier return AnswerVerifier() # 경로 설정 base_path = os.path.dirname(os.path.abspath(__file__)) data_path = os.path.join(base_path, 'Data') misconception_csv_path = os.path.join(data_path, 'misconception_mapping.csv') # 로깅 설정 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # 세션 상태 초기화 if 'initialized' not in st.session_state: st.session_state.initialized = True st.session_state.wrong_questions = [] st.session_state.misconceptions = [] st.session_state.current_question_index = 0 st.session_state.generated_questions = [] st.session_state.current_step = 'initial' st.session_state.selected_wrong_answer = None st.session_state.questions = [] logger.info("Session state initialized") # 문제 생성기 초기화 @st.cache_resource def load_question_generator(): """문제 생성 모델 로드""" if not os.path.exists(misconception_csv_path): st.error(f"CSV 파일이 존재하지 않습니다: {misconception_csv_path}") raise FileNotFoundError(f"CSV 파일이 존재하지 않습니다: {misconception_csv_path}") return SimilarQuestionGenerator(misconception_csv_path=misconception_csv_path) # CSV 데이터 로드 함수 @st.cache_data def load_data(data_file='/train.csv'): try: file_path = os.path.join(data_path, data_file.lstrip('/')) df = pd.read_csv(file_path) logger.info(f"Data loaded successfully from {file_path}") return df except FileNotFoundError: st.error(f"파일을 찾을 수 없습니다: {data_file}") logger.error(f"File not found: {data_file}") return None def start_quiz(): """퀴즈 시작 및 초기화""" df = load_data() if df is None or df.empty: st.error("데이터를 불러올 수 없습니다. 데이터셋을 확인해주세요.") return st.session_state.questions = df.sample(n=10, random_state=42) st.session_state.current_step = 'quiz' st.session_state.current_question_index = 0 st.session_state.wrong_questions = [] st.session_state.misconceptions = [] st.session_state.generated_questions = [] logger.info("Quiz started") def handle_answer(answer, current_q): """답변 처리""" if answer != current_q['CorrectAnswer']: wrong_q_dict = current_q.to_dict() st.session_state.wrong_questions.append(wrong_q_dict) st.session_state.selected_wrong_answer = answer misconception_key = f'Misconception{answer}Id' misconception_id = current_q.get(misconception_key) st.session_state.misconceptions.append(misconception_id) st.session_state.current_question_index += 1 if st.session_state.current_question_index >= 10: st.session_state.current_step = 'review' # 전역 LaTeX 포맷터 인스턴스 생성 latex_formatter = LatexFormatter() def display_math_content(content: str): """수학 내용을 화면에 표시""" formatted_content = latex_formatter.format_expression(content) st.markdown(formatted_content, unsafe_allow_html=True) def main(): """메인 애플리케이션 로직""" st.title("MisconcepTutor") # Misconception Model 로드 misconception_model = load_misconception_model() # 초기 화면 if st.session_state.current_step == 'initial': st.write("#### 학습을 시작하겠습니다. 10개의 문제를 풀어볼까요?") if st.button("학습 시작", key="start_quiz"): start_quiz() st.rerun() # 퀴즈 화면 elif st.session_state.current_step == 'quiz': current_q = st.session_state.questions.iloc[st.session_state.current_question_index] # 진행 상황 표시 progress = st.session_state.current_question_index / 10 st.progress(progress) st.write(f"### 문제 {st.session_state.current_question_index + 1}/10") # 문제 표시 st.markdown("---") display_math_content(current_q['QuestionText']) # 보기 표시 col1, col2 = st.columns(2) with col1: if st.button(latex_formatter.format_expression(f"A) {current_q['AnswerAText']}"), key="A"): handle_answer('A', current_q) st.rerun() if st.button(latex_formatter.format_expression(f"C) {current_q['AnswerCText']}"), key="C"): handle_answer('C', current_q) st.rerun() with col2: if st.button(latex_formatter.format_expression(f"B) {current_q['AnswerBText']}"), key="B"): handle_answer('B', current_q) st.rerun() if st.button(latex_formatter.format_expression(f"D) {current_q['AnswerDText']}"), key="D"): handle_answer('D', current_q) st.rerun() # 복습 화면 elif st.session_state.current_step == 'review': st.write("### 학습 결과") # 결과 통계 col1, col2, col3 = st.columns(3) col1.metric("총 문제 수", 10) col2.metric("맞은 문제", 10 - len(st.session_state.wrong_questions)) col3.metric("틀린 문제", len(st.session_state.wrong_questions)) # 틀린 문제 분석 if st.session_state.wrong_questions: st.write("### ✍️ 틀린 문제 분석") tabs = st.tabs([f"📝 틀린 문제 #{i + 1}" for i in range(len(st.session_state.wrong_questions))]) for i, (tab, (wrong_q, misconception_id)) in enumerate(zip( tabs, zip(st.session_state.wrong_questions, st.session_state.misconceptions) )): with tab: st.write("**📋 문제:**") st.write(wrong_q['QuestionText']) st.write("**✅ 정답:**", wrong_q['CorrectAnswer']) st.write("---") st.write("**🔍 관련된 Misconception:**") if misconception_id and not pd.isna(misconception_id): misconception_text = misconception_model.misconception_names.get(misconception_id, "정보 없음") st.info(f"Misconception ID: {int(misconception_id)}\n\n{misconception_text}") else: st.info("Misconception 정보가 없습니다.") if __name__ == "__main__": main()