Jintonic92 commited on
Commit
14abbf8
·
verified ·
1 Parent(s): 6903b8d

Delete app_문제까지는latex표시.py

Browse files
Files changed (1) hide show
  1. app_문제까지는latex표시.py +0 -484
app_문제까지는latex표시.py DELETED
@@ -1,484 +0,0 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import os
4
- from src.SecondModule.module2 import SimilarQuestionGenerator
5
- from src.ThirdModule.module3 import AnswerVerifier
6
- import logging
7
- from typing import Optional, Tuple
8
- #from latex_formatter import LatexFormatter # LaTeX 포맷터 import
9
- from pylatexenc.latex2text import LatexNodes2Text
10
- import re
11
-
12
-
13
- logging.basicConfig(level=logging.DEBUG)
14
-
15
-
16
- # Streamlit 페이지 기본 설정
17
- st.set_page_config(
18
- page_title="MisconcepTutor",
19
- layout="wide",
20
- initial_sidebar_state="expanded"
21
- )
22
-
23
- @st.cache_resource
24
- def load_answer_verifier():
25
- """답안 검증 모델 로드"""
26
- from src.ThirdModule.module3 import AnswerVerifier
27
- return AnswerVerifier()
28
-
29
- # 경로 설정
30
- base_path = os.path.dirname(os.path.abspath(__file__))
31
- data_path = os.path.join(base_path, 'Data')
32
- misconception_csv_path = os.path.join(data_path, 'misconception_mapping.csv')
33
-
34
- # 로깅 설정
35
- logging.basicConfig(level=logging.INFO)
36
- logger = logging.getLogger(__name__)
37
-
38
- # 세션 상태 초기화 - 가장 먼저 실행되도록 최상단에 배치
39
- if 'initialized' not in st.session_state:
40
- st.session_state.initialized = True
41
- st.session_state.wrong_questions = []
42
- st.session_state.misconceptions = []
43
- st.session_state.current_question_index = 0
44
- st.session_state.generated_questions = []
45
- st.session_state.current_step = 'initial'
46
- st.session_state.selected_wrong_answer = None
47
- st.session_state.questions = []
48
- logger.info("Session state initialized")
49
-
50
- # 문제 생성기 초기화
51
- @st.cache_resource
52
- def load_question_generator():
53
- """문제 생성 모델 로드"""
54
- if not os.path.exists(misconception_csv_path):
55
- st.error(f"CSV 파일이 존재하지 않습니다: {misconception_csv_path}")
56
- raise FileNotFoundError(f"CSV 파일이 존재하지 않습니다: {misconception_csv_path}")
57
- return SimilarQuestionGenerator(misconception_csv_path=misconception_csv_path)
58
-
59
- # CSV 데이터 로드 함수
60
- @st.cache_data
61
- def load_data(data_file = '/train.csv'):
62
- try:
63
- file_path = os.path.join(data_path, data_file.lstrip('/'))
64
- df = pd.read_csv(file_path)
65
- logger.info(f"Data loaded successfully from {file_path}")
66
- return df
67
- except FileNotFoundError:
68
- st.error(f"파일을 찾을 수 없습니다: {data_file}")
69
- logger.error(f"File not found: {data_file}")
70
- return None
71
-
72
- def start_quiz():
73
- """퀴즈 시작 및 초기화"""
74
- df = load_data()
75
- if df is None or df.empty:
76
- st.error("데이터를 불러올 수 없습니다. 데이터셋을 확인해주세요.")
77
- return
78
-
79
- st.session_state.questions = df.sample(n=10, random_state=42)
80
- st.session_state.current_step = 'quiz'
81
- st.session_state.current_question_index = 0
82
- st.session_state.wrong_questions = []
83
- st.session_state.misconceptions = []
84
- st.session_state.generated_questions = []
85
- logger.info("Quiz started")
86
-
87
-
88
- def generate_similar_question(wrong_q, misconception_id, generator):
89
- """유사 문제 생성"""
90
- logger.info(f"Generating similar question for misconception_id: {misconception_id}")
91
-
92
- # 입력 데이터 유효성 검사
93
- if not isinstance(wrong_q, dict):
94
- logger.error(f"Invalid wrong_q type: {type(wrong_q)}")
95
- st.error("유사 문제 생성에 필요한 데이터 형식이 잘못되었습니다.")
96
- return None
97
-
98
- try:
99
- # misconception_id가 없거나 NaN인 경우 다른 misconception 사용
100
- if pd.isna(misconception_id):
101
- logger.info("Original misconception_id is NaN, trying to find alternative")
102
- # 현재까지 나온 misconception들 중에서 선택
103
- available_misconceptions = [m for m in st.session_state.misconceptions if not pd.isna(m)]
104
-
105
- if available_misconceptions:
106
- # 가장 최근에 나온 misconception 선택
107
- misconception_id = available_misconceptions[-1]
108
- logger.info(f"Using alternative misconception_id: {misconception_id}")
109
- else:
110
- # 기본 misconception ID 사용 (예: 가장 기본적인 misconception)
111
- misconception_id = 2001 # 적절한 기본값으로 수정 필요
112
- logger.info(f"Using default misconception_id: {misconception_id}")
113
-
114
- # 데이터 준비 (튜플 변환 방지)
115
- input_data = {
116
- 'construct_name': str(wrong_q.get('ConstructName', '')),
117
- 'subject_name': str(wrong_q.get('SubjectName', '')),
118
- 'question_text': str(wrong_q.get('QuestionText', '')),
119
- 'correct_answer_text': str(wrong_q.get(f'Answer{wrong_q["CorrectAnswer"]}Text', '')),
120
- 'wrong_answer_text': str(wrong_q.get(f'Answer{st.session_state.selected_wrong_answer}Text', '')),
121
- 'misconception_id': int(misconception_id)
122
- }
123
-
124
- logger.info(f"Prepared input data: {input_data}")
125
-
126
- with st.spinner("📝 유사 문제를 생성하고 있습니다..."):
127
- # 유사 문제 생성 호출
128
- generated_q, _ = generator.generate_similar_question_with_text(
129
- construct_name=input_data['construct_name'],
130
- subject_name=input_data['subject_name'],
131
- question_text=input_data['question_text'],
132
- correct_answer_text=input_data['correct_answer_text'],
133
- wrong_answer_text=input_data['wrong_answer_text'],
134
- misconception_id=input_data['misconception_id']
135
- )
136
-
137
- if generated_q:
138
- verifier = load_answer_verifier()
139
- with st.status("🤔 AI가 문제를 검토하고 있습니다..."):
140
- st.write("답안의 정확성을 검증하고 있습니다...")
141
- verified_answer = verifier.verify_answer(
142
- question=generated_q.question,
143
- choices=generated_q.choices
144
- )
145
-
146
- if verified_answer:
147
- logger.info(f"Answer verified: {verified_answer}")
148
- st.write("✅ 검증 완료!")
149
- result = {
150
- 'question': generated_q.question,
151
- 'choices': generated_q.choices,
152
- 'correct': verified_answer,
153
- 'explanation': generated_q.explanation
154
- }
155
- st.session_state['current_similar_question_answer'] = verified_answer
156
- return result
157
- else:
158
- logger.warning("Answer verification failed, using original answer")
159
- st.write("⚠️ 검증에 실패했습니다. 원본 답안을 사용합니다.")
160
- result = {
161
- 'question': generated_q.question,
162
- 'choices': generated_q.choices,
163
- 'correct': generated_q.correct_answer,
164
- 'explanation': generated_q.explanation
165
- }
166
- st.session_state['current_similar_question_answer'] = generated_q.correct_answer
167
- return result
168
-
169
- except Exception as e:
170
- logger.error(f"Error in generate_similar_question: {str(e)}")
171
- st.error(f"문제 생성 중 오류가 발생했습니다: {str(e)}")
172
- return None
173
-
174
- return None
175
-
176
- # 수정
177
- def handle_answer(answer, current_q):
178
- """답변 처리"""
179
- if answer != current_q['CorrectAnswer']:
180
- wrong_q_dict = current_q.to_dict()
181
- st.session_state.wrong_questions.append(wrong_q_dict)
182
- st.session_state.selected_wrong_answer = answer
183
-
184
- misconception_key = f'Misconception{answer}Id'
185
- misconception_id = current_q.get(misconception_key)
186
- st.session_state.misconceptions.append(misconception_id)
187
-
188
- st.session_state.current_question_index += 1
189
- if st.session_state.current_question_index >= len(st.session_state.questions):
190
- st.session_state.current_step = 'review'
191
- else:
192
- st.session_state.current_step = 'quiz'
193
-
194
- # 수정
195
- def display_math_content(content):
196
- """
197
- Display mathematical content with proper formatting.
198
-
199
- Args:
200
- content (str): The math content to display
201
- """
202
- # Convert LaTeX to plain text for display
203
- from pylatexenc.latex2text import LatexNodes2Text
204
-
205
- # Clean and format the content
206
- formatted_content = LatexNodes2Text().latex_to_text(content)
207
-
208
- # Display in streamlit
209
- st.markdown(f'<div class="math-container">{formatted_content}</div>', unsafe_allow_html=True)
210
-
211
- # 추가
212
- def add_custom_css():
213
- st.markdown(
214
- """
215
- <style>
216
- .problem-header {
217
- color: #FF6B6B;
218
- font-size: 24px;
219
- font-weight: bold;
220
- margin-bottom: 20px;
221
- }
222
-
223
- .math-container {
224
- background-color: #f9f9f9;
225
- padding: 20px;
226
- border-radius: 10px;
227
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
228
- margin-bottom: 20px;
229
- }
230
-
231
- .options-container {
232
- display: grid;
233
- grid-template-columns: 1fr 1fr;
234
- gap: 20px;
235
- }
236
-
237
- .option {
238
- background-color: white;
239
- border: 1px solid #ddd;
240
- border-radius: 8px;
241
- padding: 15px;
242
- text-align: center;
243
- font-size: 18px;
244
- cursor: pointer;
245
- transition: background-color 0.3s;
246
- }
247
-
248
- .option:hover {
249
- background-color: #f8f9fa;
250
- }
251
- </style>
252
- """,
253
- unsafe_allow_html=True
254
- )
255
-
256
-
257
- # 2210
258
- # def display_question(question, answers):
259
- # """Display a math problem and its options"""
260
- # st.markdown('<div class="problem-header">Problem:</div>', unsafe_allow_html=True)
261
- # display_math_content(question) # 문제 렌더링
262
- # col1, col2 = st.columns(2)
263
- # for i, (col, opt) in enumerate([(col1, 'A'), (col1, 'C'), (col2, 'B'), (col2, 'D')]):
264
- # with col:
265
- # if st.button(opt, key=f"btn_{opt}"): # 괄호 제거
266
- # handle_answer(opt, st.session_state.questions.iloc[st.session_state.current_question_index])
267
- # st.rerun()
268
- # display_math_content(answers[opt]) # 보기 LaTeX 렌더링
269
-
270
- def display_question(question, answers):
271
- """Display a math problem and its options with LaTeX formatting"""
272
- st.markdown('<div class="problem-header">Problem:</div>', unsafe_allow_html=True)
273
- display_math_content(question)
274
-
275
- col1, col2 = st.columns(2)
276
- for i, (col, opt) in enumerate([(col1, 'A'), (col1, 'C'), (col2, 'B'), (col2, 'D')]):
277
- with col:
278
- answer_text = answers[opt]
279
- if st.button(answer_text, key=f"btn_{opt}"):
280
- handle_answer(opt, st.session_state.questions.iloc[st.session_state.current_question_index])
281
- st.rerun()
282
-
283
-
284
- def main():
285
- """메인 애플리케이션 로직"""
286
- st.title("MisconcepTutor")
287
-
288
- # Generator 초기화
289
- generator = load_question_generator()
290
-
291
- # 초기 화면
292
- if st.session_state.current_step == 'initial':
293
- st.write("#### 학습을 시작하겠습니다. 10개의 문제를 풀어볼까요?")
294
- if st.button("학습 시작", key="start_quiz"):
295
- start_quiz()
296
- st.rerun()
297
-
298
- # 퀴즈 화면
299
- elif st.session_state.current_step == 'quiz':
300
- current_q = st.session_state.questions.iloc[st.session_state.current_question_index]
301
-
302
- # 진행 상황 표시
303
- progress = st.session_state.current_question_index / 10
304
- st.progress(progress)
305
- st.write(f"### 문제 {st.session_state.current_question_index + 1}/10")
306
-
307
- # 문제 표시
308
- st.markdown("---")
309
- question_row = current_q['QuestionText']
310
- question_text = LatexNodes2Text().latex_to_text(current_q['QuestionText'])
311
- #st.write(current_q['QuestionText'])
312
- #st.write(question_text)
313
-
314
- answers ={
315
- 'A': current_q['AnswerAText'],
316
- 'B': current_q['AnswerBText'],
317
- 'C': current_q['AnswerCText'],
318
- 'D': current_q['AnswerDText']
319
- }
320
-
321
- display_question(question_text, answers)
322
-
323
-
324
- # 복습 화면
325
- elif st.session_state.current_step == 'review':
326
- st.write("### 학습 결과")
327
-
328
- # 결과 통계
329
- col1, col2, col3 = st.columns(3)
330
- col1.metric("총 문제 수", 10)
331
- col2.metric("맞은 문제", 10 - len(st.session_state.wrong_questions))
332
- col3.metric("틀린 문제", len(st.session_state.wrong_questions))
333
-
334
- # 결과에 따른 메시지 표시
335
- if len(st.session_state.wrong_questions) == 0:
336
- st.balloons() # 축하 효과
337
- st.success("🎉 축하합니다! 모든 문제를 맞추셨어요!")
338
- st.markdown("""
339
- ### 🏆 수학왕이십니다!
340
- 완벽한 점수를 받으셨네요! 수학적 개념을 정확하게 이해하고 계신 것 같습니다.
341
- """)
342
- elif len(st.session_state.wrong_questions) <= 3:
343
- st.success("잘 하셨어요! 조금만 더 연습하면 완벽할 거예요!")
344
- else:
345
- st.info("천천히 개념을 복습해보아요. 연습하다 보면 늘어날 거예요!")
346
-
347
- # 네비게이션 버튼
348
- col1, col2 = st.columns(2)
349
- with col1:
350
- if st.button("🔄 새로운 문제 세트 시작하기", use_container_width=True):
351
- start_quiz()
352
- st.rerun()
353
- with col2:
354
- if st.button("🏠 처음으로 돌아가기", use_container_width=True):
355
- st.session_state.clear()
356
- st.rerun()
357
-
358
- # 틀린 문제 분석 부분
359
- if st.session_state.wrong_questions:
360
- st.write("### ✍️ 틀린 문제 분석")
361
- tabs = st.tabs([f"📝 틀린 문제 #{i + 1}" for i in range(len(st.session_state.wrong_questions))])
362
-
363
- for i, (tab, (wrong_q, misconception_id)) in enumerate(zip(
364
- tabs,
365
- zip(st.session_state.wrong_questions, st.session_state.misconceptions)
366
- )):
367
- with tab:
368
- st.write("**📋 문제:**")
369
- #st.write(wrong_q['QuestionText'])
370
- display_math_content(wrong_q['QuestionText']) # 문제 렌더링
371
- #st.write("**✅ 정답:**", wrong_q['CorrectAnswer'])
372
- st.write("**✅ 정답:**")
373
- display_math_content(wrong_q[f'Answer{wrong_q["CorrectAnswer"]}Text']) # 정답 렌더링
374
- st.write("---")
375
- st.write("**🔍 관련된 Misconception:**")
376
- if misconception_id and not pd.isna(misconception_id):
377
- misconception_text = generator.get_misconception_text(misconception_id)
378
- st.info(f"Misconception ID: {int(misconception_id)}\n\n{misconception_text}")
379
- else:
380
- st.info("Misconception 정보가 없습니다.")
381
-
382
- if st.button(f"📚 유사 문제 풀기", key=f"retry_{i}"):
383
- st.session_state[f"show_similar_question_{i}"] = True
384
- st.session_state[f"similar_question_answered_{i}"] = False
385
- st.rerun()
386
-
387
- if st.session_state.get(f"show_similar_question_{i}", False):
388
- st.divider()
389
- new_question = generate_similar_question(wrong_q, misconception_id, generator)
390
- if new_question:
391
- st.write("### 🎯 유사 문제")
392
- display_math_content(new_question['question']) # 함수 교체
393
-
394
- # 답변 상태 확인
395
- answered = st.session_state.get(f"similar_question_answered_{i}", False)
396
-
397
- # 보기 표시
398
- st.write("**보기:**")
399
- col1, col2 = st.columns(2)
400
-
401
-
402
- # 답변하지 않은 경우에만 버튼 활성화
403
- if not answered:
404
- with col1:
405
- for option in ['A', 'C']:
406
- if st.button(
407
- f"{option}) {LatexNodes2Text().latex_to_text(new_question['choices'][option])}",
408
- key=f"similar_{option}_{i}"
409
- ):
410
- st.session_state[f"similar_question_answered_{i}"] = True
411
- st.session_state[f"selected_answer_{i}"] = option
412
- correct_answer = st.session_state.get('current_similar_question_answer')
413
- if option == correct_answer:
414
- st.session_state[f"is_correct_{i}"] = True
415
- else:
416
- st.session_state[f"is_correct_{i}"] = False
417
- st.rerun()
418
-
419
- with col2:
420
- for option in ['B', 'D']:
421
- if st.button(
422
- f"{option}) {LatexNodes2Text().latex_to_text(new_question['choices'][option])}",
423
- key=f"similar_{option}_{i}"
424
- ):
425
- st.session_state[f"similar_question_answered_{i}"] = True
426
- st.session_state[f"selected_answer_{i}"] = option
427
- correct_answer = st.session_state.get('current_similar_question_answer')
428
- if option == correct_answer:
429
- st.session_state[f"is_correct_{i}"] = True
430
- else:
431
- st.session_state[f"is_correct_{i}"] = False
432
- st.rerun()
433
-
434
- # 답변한 경우 결과 표시
435
- if answered:
436
- is_correct = st.session_state.get(f"is_correct_{i}", False)
437
- correct_answer = st.session_state.get('current_similar_question_answer')
438
- if is_correct:
439
- st.success("✅ 정답입니다!")
440
- else:
441
- st.error(f"❌ 틀렸습니다. 정답은 {correct_answer}입니다.")
442
-
443
- # 해설 표시
444
- st.write("---")
445
- st.write("**📝 해설:**", new_question['explanation'])
446
-
447
- # 다시 풀기 버튼
448
- if st.button("🔄 다시 풀기", key=f"reset_{i}"):
449
- st.session_state[f"similar_question_answered_{i}"] = False
450
- st.session_state[f"selected_answer_{i}"] = None
451
- st.session_state[f"is_correct_{i}"] = None
452
- st.rerun()
453
-
454
- # 문제 닫기 버튼
455
- if st.button("❌ 문제 닫기", key=f"close_{i}"):
456
- st.session_state[f"show_similar_question_{i}"] = False
457
- st.session_state[f"similar_question_answered_{i}"] = False
458
- st.session_state[f"selected_answer_{i}"] = None
459
- st.session_state[f"is_correct_{i}"] = None
460
- st.rerun()
461
-
462
- # 화면 아래 여백 추가
463
- st.markdown("<br>" * 5, unsafe_allow_html=True) # 5줄의 빈 줄 추가
464
- st.markdown("""
465
- <div style="height: 100px;">
466
- </div>
467
- """, unsafe_allow_html=True) # 추가 여백
468
- else:
469
- st.error("유사 문제를 생성할 수 없습니다.")
470
- if st.button("❌ 닫기", key=f"close_error_{i}"):
471
- st.session_state[f"show_similar_question_{i}"] = False
472
- st.rerun()
473
- # 화면 아래 여백 추가
474
- st.markdown("<br>" * 5, unsafe_allow_html=True) # 5줄의 빈 줄 추가
475
- st.markdown("""
476
- <div style="height: 100px;">
477
- </div>
478
- """, unsafe_allow_html=True) # 추가 여백
479
- if __name__ == "__main__":
480
- main()
481
-
482
- # random_state 42에서 정답
483
- # D C A A C
484
- # A B B B B