Jintonic92 commited on
Commit
a3b3cbc
ยท
verified ยท
1 Parent(s): c3e24fe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +340 -38
app.py CHANGED
@@ -6,7 +6,8 @@ from src.SecondModule.module2 import SimilarQuestionGenerator
6
  from src.ThirdModule.module3 import AnswerVerifier
7
  import logging
8
  from typing import Optional, Tuple
9
- from latex_formatter import LatexFormatter
 
10
 
11
  logging.basicConfig(level=logging.DEBUG)
12
 
@@ -41,7 +42,7 @@ misconception_csv_path = os.path.join(data_path, 'misconception_mapping.csv')
41
  logging.basicConfig(level=logging.INFO)
42
  logger = logging.getLogger(__name__)
43
 
44
- # ์„ธ์…˜ ์ƒํƒœ ์ดˆ๊ธฐํ™”
45
  if 'initialized' not in st.session_state:
46
  st.session_state.initialized = True
47
  st.session_state.wrong_questions = []
@@ -53,6 +54,7 @@ if 'initialized' not in st.session_state:
53
  st.session_state.questions = []
54
  logger.info("Session state initialized")
55
 
 
56
  # ๋ฌธ์ œ ์ƒ์„ฑ๊ธฐ ์ดˆ๊ธฐํ™”
57
  @st.cache_resource
58
  def load_question_generator():
@@ -90,28 +92,214 @@ def start_quiz():
90
  st.session_state.generated_questions = []
91
  logger.info("Quiz started")
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  def handle_answer(answer, current_q):
94
  """๋‹ต๋ณ€ ์ฒ˜๋ฆฌ"""
95
  if answer != current_q['CorrectAnswer']:
96
  wrong_q_dict = current_q.to_dict()
97
  st.session_state.wrong_questions.append(wrong_q_dict)
98
  st.session_state.selected_wrong_answer = answer
99
-
100
  misconception_key = f'Misconception{answer}Id'
101
  misconception_id = current_q.get(misconception_key)
102
  st.session_state.misconceptions.append(misconception_id)
103
-
104
  st.session_state.current_question_index += 1
105
- if st.session_state.current_question_index >= 10:
106
  st.session_state.current_step = 'review'
 
 
107
 
108
- # ์ „์—ญ LaTeX ํฌ๋งทํ„ฐ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
109
- latex_formatter = LatexFormatter()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
- def display_math_content(content: str):
112
- """์ˆ˜ํ•™ ๋‚ด์šฉ์„ ํ™”๋ฉด์— ํ‘œ์‹œ"""
113
- formatted_content = latex_formatter.format_expression(content)
114
- st.markdown(formatted_content, unsafe_allow_html=True)
115
 
116
  def main():
117
  """๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง"""
@@ -120,6 +308,10 @@ def main():
120
  # Misconception Model ๋กœ๋“œ
121
  misconception_model = load_misconception_model()
122
 
 
 
 
 
123
  # ์ดˆ๊ธฐ ํ™”๋ฉด
124
  if st.session_state.current_step == 'initial':
125
  st.write("#### ํ•™์Šต์„ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. 10๊ฐœ์˜ ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋ณผ๊นŒ์š”?")
@@ -130,64 +322,174 @@ def main():
130
  # ํ€ด์ฆˆ ํ™”๋ฉด
131
  elif st.session_state.current_step == 'quiz':
132
  current_q = st.session_state.questions.iloc[st.session_state.current_question_index]
133
-
134
  # ์ง„ํ–‰ ์ƒํ™ฉ ํ‘œ์‹œ
135
  progress = st.session_state.current_question_index / 10
136
  st.progress(progress)
137
  st.write(f"### ๋ฌธ์ œ {st.session_state.current_question_index + 1}/10")
138
-
139
  # ๋ฌธ์ œ ํ‘œ์‹œ
140
  st.markdown("---")
141
- display_math_content(current_q['QuestionText'])
142
-
143
- # ๋ณด๊ธฐ ํ‘œ์‹œ
144
- col1, col2 = st.columns(2)
145
- with col1:
146
- if st.button(latex_formatter.format_expression(f"A) {current_q['AnswerAText']}"), key="A"):
147
- handle_answer('A', current_q)
148
- st.rerun()
149
- if st.button(latex_formatter.format_expression(f"C) {current_q['AnswerCText']}"), key="C"):
150
- handle_answer('C', current_q)
151
- st.rerun()
152
- with col2:
153
- if st.button(latex_formatter.format_expression(f"B) {current_q['AnswerBText']}"), key="B"):
154
- handle_answer('B', current_q)
155
- st.rerun()
156
- if st.button(latex_formatter.format_expression(f"D) {current_q['AnswerDText']}"), key="D"):
157
- handle_answer('D', current_q)
158
- st.rerun()
159
 
160
  # ๋ณต์Šต ํ™”๋ฉด
161
  elif st.session_state.current_step == 'review':
162
  st.write("### ํ•™์Šต ๊ฒฐ๊ณผ")
163
-
164
  # ๊ฒฐ๊ณผ ํ†ต๊ณ„
165
  col1, col2, col3 = st.columns(3)
166
  col1.metric("์ด ๋ฌธ์ œ ์ˆ˜", 10)
167
  col2.metric("๋งž์€ ๋ฌธ์ œ", 10 - len(st.session_state.wrong_questions))
168
  col3.metric("ํ‹€๋ฆฐ ๋ฌธ์ œ", len(st.session_state.wrong_questions))
169
-
170
- # ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  if st.session_state.wrong_questions:
172
  st.write("### โœ๏ธ ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„")
173
  tabs = st.tabs([f"๐Ÿ“ ํ‹€๋ฆฐ ๋ฌธ์ œ #{i + 1}" for i in range(len(st.session_state.wrong_questions))])
174
-
175
  for i, (tab, (wrong_q, misconception_id)) in enumerate(zip(
176
  tabs,
177
  zip(st.session_state.wrong_questions, st.session_state.misconceptions)
178
  )):
179
  with tab:
180
  st.write("**๐Ÿ“‹ ๋ฌธ์ œ:**")
181
- st.write(wrong_q['QuestionText'])
182
- st.write("**โœ… ์ •๋‹ต:**", wrong_q['CorrectAnswer'])
 
183
 
184
  st.write("---")
185
  st.write("**๐Ÿ” ๊ด€๋ จ๋œ Misconception:**")
186
  if misconception_id and not pd.isna(misconception_id):
187
- misconception_text = misconception_model.misconception_names.get(misconception_id, "์ •๋ณด ์—†์Œ")
188
  st.info(f"Misconception ID: {int(misconception_id)}\n\n{misconception_text}")
189
  else:
190
  st.info("Misconception ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
  if __name__ == "__main__":
193
  main()
 
 
 
 
 
 
6
  from src.ThirdModule.module3 import AnswerVerifier
7
  import logging
8
  from typing import Optional, Tuple
9
+ from pylatexenc.latex2text import LatexNodes2Text
10
+ import re
11
 
12
  logging.basicConfig(level=logging.DEBUG)
13
 
 
42
  logging.basicConfig(level=logging.INFO)
43
  logger = logging.getLogger(__name__)
44
 
45
+ # ์„ธ์…˜ ์ƒํƒœ ์ดˆ๊ธฐํ™” - ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰๋˜๋„๋ก ์ตœ์ƒ๋‹จ์— ๋ฐฐ์น˜
46
  if 'initialized' not in st.session_state:
47
  st.session_state.initialized = True
48
  st.session_state.wrong_questions = []
 
54
  st.session_state.questions = []
55
  logger.info("Session state initialized")
56
 
57
+
58
  # ๋ฌธ์ œ ์ƒ์„ฑ๊ธฐ ์ดˆ๊ธฐํ™”
59
  @st.cache_resource
60
  def load_question_generator():
 
92
  st.session_state.generated_questions = []
93
  logger.info("Quiz started")
94
 
95
+ def generate_similar_question(wrong_q, misconception_id, generator):
96
+ """์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ"""
97
+ logger.info(f"Generating similar question for misconception_id: {misconception_id}")
98
+
99
+ # ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
100
+ if not isinstance(wrong_q, dict):
101
+ logger.error(f"Invalid wrong_q type: {type(wrong_q)}")
102
+ st.error("์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ํ˜•์‹์ด ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
103
+ return None
104
+
105
+ try:
106
+ # misconception_id๊ฐ€ ์—†๊ฑฐ๋‚˜ NaN์ธ ๊ฒฝ์šฐ ๋‹ค๋ฅธ misconception ์‚ฌ์šฉ
107
+ if pd.isna(misconception_id):
108
+ logger.info("Original misconception_id is NaN, trying to find alternative")
109
+ # ํ˜„์žฌ๊นŒ์ง€ ๋‚˜์˜จ misconception๋“ค ์ค‘์—์„œ ์„ ํƒ
110
+ available_misconceptions = [m for m in st.session_state.misconceptions if not pd.isna(m)]
111
+
112
+ if available_misconceptions:
113
+ # ๊ฐ€์žฅ ์ตœ๊ทผ์— ๋‚˜์˜จ misconception ์„ ํƒ
114
+ misconception_id = available_misconceptions[-1]
115
+ logger.info(f"Using alternative misconception_id: {misconception_id}")
116
+ else:
117
+ # ๊ธฐ๋ณธ misconception ID ์‚ฌ์šฉ (์˜ˆ: ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ misconception)
118
+ misconception_id = 2001 # ์ ์ ˆํ•œ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ˆ˜์ • ํ•„์š”
119
+ logger.info(f"Using default misconception_id: {misconception_id}")
120
+
121
+ # ๋ฐ์ดํ„ฐ ์ค€๋น„ (ํŠœํ”Œ ๋ณ€ํ™˜ ๋ฐฉ์ง€)
122
+ input_data = {
123
+ 'construct_name': str(wrong_q.get('ConstructName', '')),
124
+ 'subject_name': str(wrong_q.get('SubjectName', '')),
125
+ 'question_text': str(wrong_q.get('QuestionText', '')),
126
+ 'correct_answer_text': str(wrong_q.get(f'Answer{wrong_q["CorrectAnswer"]}Text', '')),
127
+ 'wrong_answer_text': str(wrong_q.get(f'Answer{st.session_state.selected_wrong_answer}Text', '')),
128
+ 'misconception_id': int(misconception_id)
129
+ }
130
+
131
+ logger.info(f"Prepared input data: {input_data}")
132
+
133
+ with st.spinner("๐Ÿ“ ์œ ์‚ฌ ๋ฌธ์ œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค..."):
134
+ # ์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ ํ˜ธ์ถœ
135
+ generated_q, _ = generator.generate_similar_question_with_text(
136
+ construct_name=input_data['construct_name'],
137
+ subject_name=input_data['subject_name'],
138
+ question_text=input_data['question_text'],
139
+ correct_answer_text=input_data['correct_answer_text'],
140
+ wrong_answer_text=input_data['wrong_answer_text'],
141
+ misconception_id=input_data['misconception_id']
142
+ )
143
+
144
+ if generated_q:
145
+ verifier = load_answer_verifier()
146
+ with st.status("๐Ÿค” AI๊ฐ€ ๋ฌธ์ œ๋ฅผ ๊ฒ€ํ† ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค..."):
147
+ st.write("๋‹ต์•ˆ์˜ ์ •ํ™•์„ฑ์„ ๊ฒ€์ฆํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค...")
148
+ verified_answer = verifier.verify_answer(
149
+ question=generated_q.question,
150
+ choices=generated_q.choices
151
+ )
152
+
153
+ if verified_answer:
154
+ logger.info(f"Answer verified: {verified_answer}")
155
+ st.write("โœ… ๊ฒ€์ฆ ์™„๋ฃŒ!")
156
+ result = {
157
+ 'question': generated_q.question,
158
+ 'choices': generated_q.choices,
159
+ 'correct': verified_answer,
160
+ 'explanation': generated_q.explanation
161
+ }
162
+ st.session_state['current_similar_question_answer'] = verified_answer
163
+ return result
164
+ else:
165
+ logger.warning("Answer verification failed, using original answer")
166
+ st.write("โš ๏ธ ๊ฒ€์ฆ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ์›๋ณธ ๋‹ต์•ˆ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.")
167
+ result = {
168
+ 'question': generated_q.question,
169
+ 'choices': generated_q.choices,
170
+ 'correct': generated_q.correct_answer,
171
+ 'explanation': generated_q.explanation
172
+ }
173
+ st.session_state['current_similar_question_answer'] = generated_q.correct_answer
174
+ return result
175
+
176
+ except Exception as e:
177
+ logger.error(f"Error in generate_similar_question: {str(e)}")
178
+ st.error(f"๋ฌธ์ œ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}")
179
+ return None
180
+
181
+ return None
182
+
183
  def handle_answer(answer, current_q):
184
  """๋‹ต๋ณ€ ์ฒ˜๋ฆฌ"""
185
  if answer != current_q['CorrectAnswer']:
186
  wrong_q_dict = current_q.to_dict()
187
  st.session_state.wrong_questions.append(wrong_q_dict)
188
  st.session_state.selected_wrong_answer = answer
189
+
190
  misconception_key = f'Misconception{answer}Id'
191
  misconception_id = current_q.get(misconception_key)
192
  st.session_state.misconceptions.append(misconception_id)
193
+
194
  st.session_state.current_question_index += 1
195
+ if st.session_state.current_question_index >= len(st.session_state.questions):
196
  st.session_state.current_step = 'review'
197
+ else:
198
+ st.session_state.current_step = 'quiz'
199
 
200
+ def display_math_content(content):
201
+ """
202
+ Display mathematical content with proper formatting.
203
+
204
+ Args:
205
+ content (str): The math content to display
206
+ """
207
+ # Convert LaTeX to plain text for display
208
+ from pylatexenc.latex2text import LatexNodes2Text
209
+
210
+ # Clean and format the content
211
+ formatted_content = LatexNodes2Text().latex_to_text(content)
212
+ st.markdown(f'<div class="math-container">{formatted_content}</div>', unsafe_allow_html=True)
213
+
214
+ def add_custom_css():
215
+ st.markdown(
216
+ """
217
+ <style>
218
+ .problem-header {
219
+ color: #FF6B6B;
220
+ font-size: 24px;
221
+ font-weight: bold;
222
+ margin-bottom: 20px;
223
+ }
224
+ .math-container {
225
+ background-color: #f0f8ff;
226
+ padding: 15px 20px;
227
+ border-radius: 5px;
228
+ margin: 5px 0;
229
+ }
230
+ button {
231
+ color: #0066ff;
232
+ font-weight: 500;
233
+ }
234
+ </style>
235
+ """,
236
+ unsafe_allow_html=True
237
+ )
238
+
239
+ def display_question(question, answers):
240
+ """Display question and options with LaTeX formatting"""
241
+ st.markdown('<div class="problem-header">Problem:</div>', unsafe_allow_html=True)
242
+ display_math_content(question)
243
+
244
+ # Add custom CSS for options
245
+ st.markdown("""
246
+ <style>
247
+ .option-container {
248
+ background-color: #f0f8ff;
249
+ padding: 10px 20px;
250
+ margin: 5px 0;
251
+ border-radius: 5px;
252
+ cursor: pointer;
253
+ display: flex;
254
+ align-items: center;
255
+ gap: 20px;
256
+ }
257
+ .option-text {
258
+ color: #0066ff;
259
+ font-weight: 500;
260
+ width: 30px;
261
+ }
262
+ </style>
263
+ """, unsafe_allow_html=True)
264
+
265
+ # Display options
266
+ for opt in ['A', 'B', 'C', 'D']:
267
+ with st.container():
268
+ col1, col2 = st.columns([1, 11])
269
+ with col1:
270
+ if st.button(f"{opt}.", key=f"btn_{opt}", help="Click to select"):
271
+ handle_answer(opt, st.session_state.questions.iloc[st.session_state.current_question_index])
272
+ st.rerun()
273
+ with col2:
274
+ display_option_content(answers[opt])
275
+
276
+ def display_option_content(option_text):
277
+ """Process and display option content with LaTeX formatting"""
278
+ from pylatexenc.latex2text import LatexNodes2Text
279
+ formatted_content = LatexNodes2Text().latex_to_text(option_text)
280
+ st.markdown(f'<div class="math-container">{formatted_content}</div>', unsafe_allow_html=True)
281
+
282
+ def update_similar_question_display(new_question, i, answered=False):
283
+ """Display similar question and its options"""
284
+ display_math_content(new_question['question'])
285
+
286
+ # Display options
287
+ for opt in ['A', 'B', 'C', 'D']:
288
+ with st.container():
289
+ col1, col2 = st.columns([1, 11])
290
+ with col1:
291
+ if st.button(f"{opt}.", key=f"sim_btn_{opt}_{i}", help="Click to select"):
292
+ if not answered:
293
+ # ์„ ํƒํ•œ ์˜ต์…˜(opt)์„ st.session_state์— ์ €์žฅ
294
+ st.session_state[f"similar_question_answered_{i}"] = True
295
+ st.session_state[f"selected_answer_{i}"] = opt
296
+ correct_answer = st.session_state.get('current_similar_question_answer')
297
+ # ์ •๋‹ต ์—ฌ๋ถ€๋ฅผ ํ™•์ธ
298
+ st.session_state[f"is_correct_{i}"] = (opt == correct_answer)
299
+ st.rerun()
300
+ with col2:
301
+ display_option_content(new_question['choices'][opt])
302
 
 
 
 
 
303
 
304
  def main():
305
  """๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง"""
 
308
  # Misconception Model ๋กœ๋“œ
309
  misconception_model = load_misconception_model()
310
 
311
+ # Generator ์ดˆ๊ธฐํ™”
312
+ generator = load_question_generator()
313
+ add_custom_css()
314
+
315
  # ์ดˆ๊ธฐ ํ™”๋ฉด
316
  if st.session_state.current_step == 'initial':
317
  st.write("#### ํ•™์Šต์„ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. 10๊ฐœ์˜ ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋ณผ๊นŒ์š”?")
 
322
  # ํ€ด์ฆˆ ํ™”๋ฉด
323
  elif st.session_state.current_step == 'quiz':
324
  current_q = st.session_state.questions.iloc[st.session_state.current_question_index]
325
+
326
  # ์ง„ํ–‰ ์ƒํ™ฉ ํ‘œ์‹œ
327
  progress = st.session_state.current_question_index / 10
328
  st.progress(progress)
329
  st.write(f"### ๋ฌธ์ œ {st.session_state.current_question_index + 1}/10")
330
+
331
  # ๋ฌธ์ œ ํ‘œ์‹œ
332
  st.markdown("---")
333
+ question_row = current_q['QuestionText']
334
+ question_text = LatexNodes2Text().latex_to_text(current_q['QuestionText'])
335
+
336
+ answers ={
337
+ 'A': current_q['AnswerAText'],
338
+ 'B': current_q['AnswerBText'],
339
+ 'C': current_q['AnswerCText'],
340
+ 'D': current_q['AnswerDText']
341
+ }
342
+
343
+ display_question(question_text, answers)
344
+
 
 
 
 
 
 
345
 
346
  # ๋ณต์Šต ํ™”๋ฉด
347
  elif st.session_state.current_step == 'review':
348
  st.write("### ํ•™์Šต ๊ฒฐ๊ณผ")
349
+
350
  # ๊ฒฐ๊ณผ ํ†ต๊ณ„
351
  col1, col2, col3 = st.columns(3)
352
  col1.metric("์ด ๋ฌธ์ œ ์ˆ˜", 10)
353
  col2.metric("๋งž์€ ๋ฌธ์ œ", 10 - len(st.session_state.wrong_questions))
354
  col3.metric("ํ‹€๋ฆฐ ๋ฌธ์ œ", len(st.session_state.wrong_questions))
355
+
356
+ # ๊ฒฐ๊ณผ์— ๋”ฐ๋ฅธ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
357
+ if len(st.session_state.wrong_questions) == 0:
358
+ st.balloons() # ์ถ•ํ•˜ ํšจ๊ณผ
359
+ st.success("๐ŸŽ‰ ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค! ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ๋งž์ถ”์…จ์–ด์š”!")
360
+ st.markdown("""
361
+ ### ๐Ÿ† ์ˆ˜ํ•™์™•์ด์‹ญ๋‹ˆ๋‹ค!
362
+ ์™„๋ฒฝํ•œ ์ ์ˆ˜๋ฅผ ๋ฐ›์œผ์…จ๋„ค์š”! ์ˆ˜ํ•™์  ๊ฐœ๋…์„ ์ •ํ™•ํ•˜๊ฒŒ ์ดํ•ดํ•˜๊ณ  ๊ณ„์‹  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
363
+ """)
364
+ elif len(st.session_state.wrong_questions) <= 3:
365
+ st.success("์ž˜ ํ•˜์…จ์–ด์š”! ์กฐ๊ธˆ๋งŒ ๋” ์—ฐ์Šตํ•˜๋ฉด ์™„๋ฒฝํ•  ๊ฑฐ์˜ˆ์š”!")
366
+ else:
367
+ st.info("์ฒœ์ฒœํžˆ ๊ฐœ๋…์„ ๋ณต์Šตํ•ด๋ณด์•„์š”. ์—ฐ์Šตํ•˜๋‹ค ๋ณด๋ฉด ๋Š˜์–ด๋‚  ๊ฑฐ์˜ˆ์š”!")
368
+
369
+ # ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฒ„ํŠผ
370
+ col1, col2 = st.columns(2)
371
+ with col1:
372
+ if st.button("๐Ÿ”„ ์ƒˆ๋กœ์šด ๋ฌธ์ œ ์„ธํŠธ ์‹œ์ž‘ํ•˜๊ธฐ", use_container_width=True):
373
+ start_quiz()
374
+ st.rerun()
375
+ with col2:
376
+ if st.button("๐Ÿ  ์ฒ˜์Œ์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ", use_container_width=True):
377
+ st.session_state.clear()
378
+ st.rerun()
379
+
380
+ # ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„ ๋ถ€๋ถ„
381
  if st.session_state.wrong_questions:
382
  st.write("### โœ๏ธ ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„")
383
  tabs = st.tabs([f"๐Ÿ“ ํ‹€๋ฆฐ ๋ฌธ์ œ #{i + 1}" for i in range(len(st.session_state.wrong_questions))])
384
+
385
  for i, (tab, (wrong_q, misconception_id)) in enumerate(zip(
386
  tabs,
387
  zip(st.session_state.wrong_questions, st.session_state.misconceptions)
388
  )):
389
  with tab:
390
  st.write("**๐Ÿ“‹ ๋ฌธ์ œ:**")
391
+ display_math_content(wrong_q['QuestionText']) # ๋ฌธ์ œ ๋ Œ๋”๋ง
392
+ st.write("**โœ… ์ •๋‹ต:**")
393
+ display_option_content(wrong_q[f'Answer{wrong_q["CorrectAnswer"]}Text'])
394
 
395
  st.write("---")
396
  st.write("**๐Ÿ” ๊ด€๋ จ๋œ Misconception:**")
397
  if misconception_id and not pd.isna(misconception_id):
398
+ misconception_text = generator.get_misconception_text(misconception_id)
399
  st.info(f"Misconception ID: {int(misconception_id)}\n\n{misconception_text}")
400
  else:
401
  st.info("Misconception ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
402
+
403
+ if st.button(f"๐Ÿ“š ์œ ์‚ฌ ๋ฌธ์ œ ํ’€๊ธฐ", key=f"retry_{i}"):
404
+ st.session_state[f"show_similar_question_{i}"] = True
405
+ st.session_state[f"similar_question_answered_{i}"] = False
406
+ st.rerun()
407
+ if st.session_state.get(f"show_similar_question_{i}", False):
408
+ st.divider()
409
+ new_question = generate_similar_question(wrong_q, misconception_id, generator)
410
+ if new_question:
411
+ st.write("### ๐ŸŽฏ ์œ ์‚ฌ ๋ฌธ์ œ")
412
+ #display_math_content(new_question['question']) # ํ•จ์ˆ˜ ๊ต์ฒด
413
+
414
+ # ๋‹ต๋ณ€ ์ƒํƒœ ํ™•์ธ
415
+ answered = st.session_state.get(f"similar_question_answered_{i}", False)
416
+ #update_similar_question_display(new_question, i, answered)
417
+ # ์„ ํƒํ•œ ์˜ต์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœ
418
+ update_similar_question_display(new_question, i)
419
+
420
+ # ๋‹ต๋ณ€ํ•œ ๊ฒฝ์šฐ ๊ฒฐ๊ณผ ํ‘œ์‹œ
421
+ if answered:
422
+ is_correct = st.session_state.get(f"is_correct_{i}", False)
423
+ correct_answer = st.session_state.get('current_similar_question_answer')
424
+ if is_correct:
425
+ st.success("โœ… ์ •๋‹ต์ž…๋‹ˆ๋‹ค!")
426
+ else:
427
+ st.error(f"โŒ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค. ์ •๋‹ต์€ {correct_answer}์ž…๋‹ˆ๋‹ค.")
428
+
429
+ # ํ•ด์„ค ํ‘œ์‹œ
430
+ st.write("---")
431
+ st.write("**๐Ÿ“ ํ•ด์„ค:**", new_question['explanation'])
432
+
433
+ # ๋‹ค์‹œ ํ’€๊ธฐ ๋ฒ„ํŠผ
434
+ if st.button("๐Ÿ”„ ๋‹ค์‹œ ํ’€๊ธฐ", key=f"reset_{i}"):
435
+ st.session_state[f"similar_question_answered_{i}"] = False
436
+ st.session_state[f"selected_answer_{i}"] = None
437
+ st.session_state[f"is_correct_{i}"] = None
438
+ st.rerun()
439
+
440
+ # ๋ฌธ์ œ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ
441
+ if st.button("โŒ ๋ฌธ์ œ ๋‹ซ๊ธฐ", key=f"close_{i}"):
442
+ st.session_state[f"show_similar_question_{i}"] = False
443
+ st.session_state[f"similar_question_answered_{i}"] = False
444
+ st.session_state[f"selected_answer_{i}"] = None
445
+ st.session_state[f"is_correct_{i}"] = None
446
+ st.rerun()
447
+
448
+ # ํ™”๋ฉด ์•„๋ž˜ ์—ฌ๋ฐฑ ์ถ”๊ฐ€
449
+ st.markdown("<br>" * 5, unsafe_allow_html=True) # 5์ค„์˜ ๋นˆ ์ค„ ์ถ”๊ฐ€
450
+ st.markdown("""
451
+ <div style="height: 100px;">
452
+ </div>
453
+ """, unsafe_allow_html=True) # ์ถ”๊ฐ€ ์—ฌ๋ฐฑ
454
+ else:
455
+ st.error("์œ ์‚ฌ ๋ฌธ์ œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
456
+ if st.button("โŒ ๋‹ซ๊ธฐ", key=f"close_error_{i}"):
457
+ st.session_state[f"show_similar_question_{i}"] = False
458
+ st.rerun()
459
+ # ํ™”๋ฉด ์•„๋ž˜ ์—ฌ๋ฐฑ ์ถ”๊ฐ€
460
+ st.markdown("<br>" * 5, unsafe_allow_html=True) # 5์ค„์˜ ๋นˆ ์ค„ ์ถ”๊ฐ€
461
+ st.markdown("""
462
+ <div style="height: 100px;">
463
+ </div>
464
+ """, unsafe_allow_html=True) # ์ถ”๊ฐ€ ์—ฌ๋ฐฑ
465
+
466
+
467
+ # # ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„
468
+ # if st.session_state.wrong_questions:
469
+ # st.write("### โœ๏ธ ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„")
470
+ # tabs = st.tabs([f"๐Ÿ“ ํ‹€๋ฆฐ ๋ฌธ์ œ #{i + 1}" for i in range(len(st.session_state.wrong_questions))])
471
+
472
+ # for i, (tab, (wrong_q, misconception_id)) in enumerate(zip(
473
+ # tabs,
474
+ # zip(st.session_state.wrong_questions, st.session_state.misconceptions)
475
+ # )):
476
+ # with tab:
477
+ # st.write("**๐Ÿ“‹ ๋ฌธ์ œ:**")
478
+ # st.write(wrong_q['QuestionText'])
479
+ # st.write("**โœ… ์ •๋‹ต:**", wrong_q['CorrectAnswer'])
480
+
481
+ # st.write("---")
482
+ # st.write("**๐Ÿ” ๊ด€๋ จ๋œ Misconception:**")
483
+ # if misconception_id and not pd.isna(misconception_id):
484
+ # misconception_text = misconception_model.misconception_names.get(misconception_id, "์ •๋ณด ์—†์Œ")
485
+ # st.info(f"Misconception ID: {int(misconception_id)}\n\n{misconception_text}")
486
+ # else:
487
+ # st.info("Misconception ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
488
 
489
  if __name__ == "__main__":
490
  main()
491
+
492
+
493
+ # random_state 42์—์„œ ์ •๋‹ต
494
+ # D C A A C
495
+ # A B B B B