MisConceptTutor / app.py
minsuas's picture
Update app.py
c3e24fe verified
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()