Jintonic92 commited on
Commit
1883518
ยท
verified ยท
1 Parent(s): 4be3af4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +37 -289
app.py CHANGED
@@ -1,80 +1,11 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import os
4
- from src.SecondModule.module2 import SimilarQuestionGenerator
5
- import logging
6
- from typing import Optional, Tuple
7
- logging.basicConfig(level=logging.DEBUG)
8
-
9
-
10
- # Streamlit ํŽ˜์ด์ง€ ๊ธฐ๋ณธ ์„ค์ •
11
- st.set_page_config(
12
- page_title="MisconcepTutor",
13
- layout="wide",
14
- initial_sidebar_state="expanded"
15
- )
16
-
17
- # ๊ฒฝ๋กœ ์„ค์ •
18
- base_path = os.path.dirname(os.path.abspath(__file__))
19
- data_path = os.path.join(base_path, 'Data')
20
- misconception_csv_path = os.path.join(data_path, 'misconception_mapping.csv')
21
-
22
- # ๋กœ๊น… ์„ค์ •
23
- logging.basicConfig(level=logging.INFO)
24
- logger = logging.getLogger(__name__)
25
-
26
- # ์„ธ์…˜ ์ƒํƒœ ์ดˆ๊ธฐํ™” - ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰๋˜๋„๋ก ์ตœ์ƒ๋‹จ์— ๋ฐฐ์น˜
27
- if 'initialized' not in st.session_state:
28
- st.session_state.initialized = True
29
- st.session_state.wrong_questions = []
30
- st.session_state.misconceptions = []
31
- st.session_state.current_question_index = 0
32
- st.session_state.generated_questions = []
33
- st.session_state.current_step = 'initial'
34
- st.session_state.selected_wrong_answer = None
35
- st.session_state.questions = []
36
- logger.info("Session state initialized")
37
-
38
- # ๋ฌธ์ œ ์ƒ์„ฑ๊ธฐ ์ดˆ๊ธฐํ™”
39
  @st.cache_resource
40
- def load_question_generator():
41
- """๋ฌธ์ œ ์ƒ์„ฑ ๋ชจ๋ธ ๋กœ๋“œ"""
42
- if not os.path.exists(misconception_csv_path):
43
- st.error(f"CSV ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: {misconception_csv_path}")
44
- raise FileNotFoundError(f"CSV ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: {misconception_csv_path}")
45
- return SimilarQuestionGenerator(misconception_csv_path=misconception_csv_path)
46
-
47
- # CSV ๋ฐ์ดํ„ฐ ๋กœ๋“œ ํ•จ์ˆ˜
48
- @st.cache_data
49
- def load_data(data_file = '/train.csv'):
50
- try:
51
- file_path = os.path.join(data_path, data_file.lstrip('/'))
52
- df = pd.read_csv(file_path)
53
- logger.info(f"Data loaded successfully from {file_path}")
54
- return df
55
- except FileNotFoundError:
56
- st.error(f"ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {data_file}")
57
- logger.error(f"File not found: {data_file}")
58
- return None
59
-
60
- def start_quiz():
61
- """ํ€ด์ฆˆ ์‹œ์ž‘ ๋ฐ ์ดˆ๊ธฐํ™”"""
62
- df = load_data()
63
- if df is None or df.empty:
64
- st.error("๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ์…‹์„ ํ™•์ธํ•ด์ฃผ์„ธ์š”.")
65
- return
66
-
67
- st.session_state.questions = df.sample(n=10, random_state=42)
68
- st.session_state.current_step = 'quiz'
69
- st.session_state.current_question_index = 0
70
- st.session_state.wrong_questions = []
71
- st.session_state.misconceptions = []
72
- st.session_state.generated_questions = []
73
- logger.info("Quiz started")
74
-
75
 
76
  def generate_similar_question(wrong_q, misconception_id, generator):
77
- """์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ"""
78
  logger.info(f"Generating similar question for misconception_id: {misconception_id}")
79
 
80
  # ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
@@ -82,14 +13,14 @@ def generate_similar_question(wrong_q, misconception_id, generator):
82
  logger.error(f"Invalid wrong_q type: {type(wrong_q)}")
83
  st.error("์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ํ˜•์‹์ด ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
84
  return None
85
-
86
  # misconception_id๊ฐ€ ์œ ํšจํ•œ์ง€ ํ™•์ธ
87
  if pd.isna(misconception_id):
88
  logger.warning("misconception_id is NaN")
89
  return None
90
-
91
  try:
92
- # ๋ฐ์ดํ„ฐ ์ค€๋น„ (ํŠœํ”Œ ๋ณ€ํ™˜ ๋ฐฉ์ง€)
93
  input_data = {
94
  'construct_name': str(wrong_q.get('ConstructName', '')),
95
  'subject_name': str(wrong_q.get('SubjectName', '')),
@@ -99,222 +30,39 @@ def generate_similar_question(wrong_q, misconception_id, generator):
99
  'misconception_id': int(misconception_id)
100
  }
101
 
102
- logger.info(f"Prepared input data: {input_data}")
103
-
104
- # ์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ ํ˜ธ์ถœ
105
- generated_q, _ = generator.generate_similar_question_with_text(
106
- construct_name=input_data['construct_name'],
107
- subject_name=input_data['subject_name'],
108
- question_text=input_data['question_text'],
109
- correct_answer_text=input_data['correct_answer_text'],
110
- wrong_answer_text=input_data['wrong_answer_text'],
111
- misconception_id=input_data['misconception_id']
112
- )
113
 
114
  if generated_q:
115
- return {
116
- 'question': generated_q.question,
117
- 'choices': generated_q.choices,
118
- 'correct': generated_q.correct_answer,
119
- 'explanation': generated_q.explanation
120
- }
121
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  except Exception as e:
123
  logger.error(f"Error in generate_similar_question: {str(e)}")
124
  st.error(f"๋ฌธ์ œ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}")
125
  return None
126
 
127
- return None
128
-
129
- def handle_answer(answer, current_q):
130
- """๋‹ต๋ณ€ ์ฒ˜๋ฆฌ"""
131
- if answer != current_q['CorrectAnswer']:
132
- wrong_q_dict = current_q.to_dict()
133
- st.session_state.wrong_questions.append(wrong_q_dict)
134
- st.session_state.selected_wrong_answer = answer
135
-
136
- misconception_key = f'Misconception{answer}Id'
137
- misconception_id = current_q.get(misconception_key)
138
- st.session_state.misconceptions.append(misconception_id)
139
-
140
- st.session_state.current_question_index += 1
141
- if st.session_state.current_question_index >= 10:
142
- st.session_state.current_step = 'review'
143
-
144
- def main():
145
- """๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง"""
146
- st.title("MisconcepTutor")
147
-
148
- # Generator ์ดˆ๊ธฐํ™”
149
- generator = load_question_generator()
150
-
151
- # ์ดˆ๊ธฐ ํ™”๋ฉด
152
- if st.session_state.current_step == 'initial':
153
- st.write("#### ํ•™์Šต์„ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. 10๊ฐœ์˜ ๋ฌธ์ œ๋ฅผ ํ’€์–ด๋ณผ๊นŒ์š”?")
154
- if st.button("ํ•™์Šต ์‹œ์ž‘", key="start_quiz"):
155
- start_quiz()
156
- st.rerun()
157
-
158
- # ํ€ด์ฆˆ ํ™”๋ฉด
159
- elif st.session_state.current_step == 'quiz':
160
- current_q = st.session_state.questions.iloc[st.session_state.current_question_index]
161
-
162
- # ์ง„ํ–‰ ์ƒํ™ฉ ํ‘œ์‹œ
163
- progress = st.session_state.current_question_index / 10
164
- st.progress(progress)
165
- st.write(f"### ๋ฌธ์ œ {st.session_state.current_question_index + 1}/10")
166
-
167
- # ๋ฌธ์ œ ํ‘œ์‹œ
168
- st.markdown("---")
169
- st.write(current_q['QuestionText'])
170
-
171
- # ๋ณด๊ธฐ ํ‘œ์‹œ
172
- col1, col2 = st.columns(2)
173
- with col1:
174
- if st.button(f"A) {current_q['AnswerAText']}", key="A"):
175
- handle_answer('A', current_q)
176
- st.rerun()
177
- if st.button(f"C) {current_q['AnswerCText']}", key="C"):
178
- handle_answer('C', current_q)
179
- st.rerun()
180
- with col2:
181
- if st.button(f"B) {current_q['AnswerBText']}", key="B"):
182
- handle_answer('B', current_q)
183
- st.rerun()
184
- if st.button(f"D) {current_q['AnswerDText']}", key="D"):
185
- handle_answer('D', current_q)
186
- st.rerun()
187
-
188
- # ๋ณต์Šต ํ™”๋ฉด
189
- elif st.session_state.current_step == 'review':
190
- st.write("### ํ•™์Šต ๊ฒฐ๊ณผ")
191
-
192
- # ๊ฒฐ๊ณผ ํ†ต๊ณ„
193
- col1, col2, col3 = st.columns(3)
194
- col1.metric("์ด ๋ฌธ์ œ ์ˆ˜", 10)
195
- col2.metric("๋งž์€ ๋ฌธ์ œ", 10 - len(st.session_state.wrong_questions))
196
- col3.metric("ํ‹€๋ฆฐ ๋ฌธ์ œ", len(st.session_state.wrong_questions))
197
-
198
- # ๊ฒฐ๊ณผ์— ๋”ฐ๋ฅธ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ
199
- if len(st.session_state.wrong_questions) == 0:
200
- st.balloons() # ์ถ•ํ•˜ ํšจ๊ณผ
201
- st.success("๐ŸŽ‰ ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค! ๋ชจ๋“  ๋ฌธ์ œ๋ฅผ ๋งž์ถ”์…จ์–ด์š”!")
202
- st.markdown("""
203
- ### ๐Ÿ† ์ˆ˜ํ•™์™•์ด์‹ญ๋‹ˆ๋‹ค!
204
- ์™„๋ฒฝํ•œ ์ ์ˆ˜๋ฅผ ๋ฐ›์œผ์…จ๋„ค์š”! ์ˆ˜ํ•™์  ๊ฐœ๋…์„ ์ •ํ™•ํ•˜๊ฒŒ ์ดํ•ดํ•˜๊ณ  ๊ณ„์‹  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
205
- """)
206
- elif len(st.session_state.wrong_questions) <= 3:
207
- st.success("์ž˜ ํ•˜์…จ์–ด์š”! ์กฐ๊ธˆ๋งŒ ๋” ์—ฐ์Šตํ•˜๋ฉด ์™„๋ฒฝํ•  ๊ฑฐ์˜ˆ์š”!")
208
- else:
209
- st.info("์ฒœ์ฒœํžˆ ๊ฐœ๋…์„ ๋ณต์Šตํ•ด๋ณด์•„์š”. ์—ฐ์Šตํ•˜๋‹ค ๋ณด๋ฉด ๋Š˜์–ด๋‚  ๊ฑฐ์˜ˆ์š”!")
210
-
211
- # ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฒ„ํŠผ
212
- col1, col2 = st.columns(2)
213
- with col1:
214
- if st.button("๐Ÿ”„ ์ƒˆ๋กœ์šด ๋ฌธ์ œ ์„ธํŠธ ์‹œ์ž‘ํ•˜๊ธฐ", use_container_width=True):
215
- start_quiz()
216
- st.rerun()
217
- with col2:
218
- if st.button("๐Ÿ  ์ฒ˜์Œ์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ", use_container_width=True):
219
- st.session_state.clear()
220
- st.rerun()
221
-
222
- # ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„
223
- if st.session_state.wrong_questions:
224
- st.write("### โœ๏ธ ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„")
225
- for i, (wrong_q, misconception_id) in enumerate(zip(
226
- st.session_state.wrong_questions,
227
- st.session_state.misconceptions
228
- )):
229
- with st.expander(f"๐Ÿ“ ํ‹€๋ฆฐ ๋ฌธ์ œ #{i + 1}"):
230
- st.write("**๐Ÿ“‹ ๋ฌธ์ œ:**")
231
- st.write(wrong_q['QuestionText'])
232
- st.write("**โœ… ์ •๋‹ต:**", wrong_q['CorrectAnswer'])
233
-
234
- st.write("---")
235
- st.write("**๐Ÿ” ๊ด€๋ จ๋œ Misconception:**")
236
- if misconception_id and not pd.isna(misconception_id):
237
- misconception_text = generator.get_misconception_text(misconception_id)
238
- st.info(f"Misconception ID: {int(misconception_id)}\n\n{misconception_text}")
239
- else:
240
- st.info("Misconception ์ •๋ณด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
241
-
242
- # ํ‹€๋ฆฐ ๋ฌธ์ œ ๋ถ„์„ ๋ถ€๋ถ„์—์„œ
243
- if st.button(f"๐Ÿ“š ์œ ์‚ฌ ๋ฌธ์ œ ํ’€๊ธฐ #{i + 1}", key=f"retry_{i}"):
244
- # ์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ ์ƒํƒœ๋ฅผ ์„ธ์…˜์— ์ €์žฅ
245
- st.session_state[f"show_similar_question_{i}"] = True
246
- st.session_state[f"similar_question_answered_{i}"] = False
247
- # ๊ธฐ์กด ๋‹ต๋ณ€ ๊ธฐ๋ก ์ดˆ๊ธฐํ™”
248
- st.rerun()
249
-
250
- # ์œ ์‚ฌ ๋ฌธ์ œ๊ฐ€ ์ƒ์„ฑ๋œ ์ƒํƒœ์ธ ๊ฒฝ์šฐ
251
- if st.session_state.get(f"show_similar_question_{i}", False):
252
- with st.spinner("์œ ์‚ฌ ๋ฌธ์ œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค..."):
253
- new_question = generate_similar_question(wrong_q, misconception_id, generator)
254
- if new_question:
255
- st.write("### ๐ŸŽฏ ์œ ์‚ฌ ๋ฌธ์ œ")
256
- st.write(new_question['question'])
257
-
258
- # ๋‹ต๋ณ€ ์ƒํƒœ ํ™•์ธ
259
- answered = st.session_state.get(f"similar_question_answered_{i}", False)
260
- selected_answer = st.session_state.get(f"selected_answer_{i}", None)
261
-
262
- # ๋ณด๊ธฐ ํ‘œ์‹œ
263
- st.write("**๋ณด๊ธฐ:**")
264
- col1, col2 = st.columns(2)
265
-
266
- # ๋‹ต๋ณ€ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋งŒ ๋ฒ„ํŠผ ํ™œ์„ฑํ™”
267
- if not answered:
268
- with col1:
269
- for option in ['A', 'C']:
270
- if st.button(
271
- f"{option}) {new_question['choices'][option]}",
272
- key=f"similar_{option}_{i}"
273
- ):
274
- st.session_state[f"similar_question_answered_{i}"] = True
275
- st.session_state[f"selected_answer_{i}"] = option
276
- st.rerun()
277
-
278
- with col2:
279
- for option in ['B', 'D']:
280
- if st.button(
281
- f"{option}) {new_question['choices'][option]}",
282
- key=f"similar_{option}_{i}"
283
- ):
284
- st.session_state[f"similar_question_answered_{i}"] = True
285
- st.session_state[f"selected_answer_{i}"] = option
286
- st.rerun()
287
-
288
- # ๋‹ต๋ณ€ํ•œ ๊ฒฝ์šฐ ๊ฒฐ๊ณผ ํ‘œ์‹œ
289
- if answered:
290
- selected = st.session_state[f"selected_answer_{i}"]
291
- if selected == new_question['correct']:
292
- st.success("โœ… ์ •๋‹ต์ž…๋‹ˆ๋‹ค!")
293
- else:
294
- st.error(f"โŒ ํ‹€๋ ธ์Šต๋‹ˆ๋‹ค. ์ •๋‹ต์€ {new_question['correct']}์ž…๋‹ˆ๋‹ค.")
295
-
296
- # ํ•ด์„ค ํ‘œ์‹œ
297
- st.write("---")
298
- st.write("**๐Ÿ“ ํ•ด์„ค:**", new_question['explanation'])
299
-
300
- # ๋‹ค์‹œ ํ’€๊ธฐ ๋ฒ„ํŠผ
301
- if st.button("๐Ÿ”„ ๋‹ค์‹œ ํ’€๊ธฐ", key=f"reset_{i}"):
302
- st.session_state[f"similar_question_answered_{i}"] = False
303
- st.session_state[f"selected_answer_{i}"] = None
304
- st.rerun()
305
-
306
- # ๋ฌธ์ œ ๋‹ซ๊ธฐ ๋ฒ„ํŠผ
307
- if st.button("โŒ ๋ฌธ์ œ ๋‹ซ๊ธฐ", key=f"close_{i}"):
308
- st.session_state[f"show_similar_question_{i}"] = False
309
- st.rerun()
310
- else:
311
- st.error("์œ ์‚ฌ ๋ฌธ์ œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
312
- if st.button("โŒ ๋‹ซ๊ธฐ", key=f"close_error_{i}"):
313
- st.session_state[f"show_similar_question_{i}"] = False
314
- st.rerun()
315
- if __name__ == "__main__":
316
- main()
317
-
318
- # random_state 42์—์„œ ์ •๋‹ต
319
- # D C A A C
320
- # A B B B B
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  @st.cache_resource
2
+ def load_answer_verifier():
3
+ """๋‹ต์•ˆ ๊ฒ€์ฆ ๋ชจ๋ธ ๋กœ๋“œ"""
4
+ from src.ThirdModule.module3 import AnswerVerifier
5
+ return AnswerVerifier()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  def generate_similar_question(wrong_q, misconception_id, generator):
8
+ """์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ ๋ฐ ๊ฒ€์ฆ"""
9
  logger.info(f"Generating similar question for misconception_id: {misconception_id}")
10
 
11
  # ์ž…๋ ฅ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
 
13
  logger.error(f"Invalid wrong_q type: {type(wrong_q)}")
14
  st.error("์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ํ˜•์‹์ด ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
15
  return None
16
+
17
  # misconception_id๊ฐ€ ์œ ํšจํ•œ์ง€ ํ™•์ธ
18
  if pd.isna(misconception_id):
19
  logger.warning("misconception_id is NaN")
20
  return None
21
+
22
  try:
23
+ # ๋ฐ์ดํ„ฐ ์ค€๋น„
24
  input_data = {
25
  'construct_name': str(wrong_q.get('ConstructName', '')),
26
  'subject_name': str(wrong_q.get('SubjectName', '')),
 
30
  'misconception_id': int(misconception_id)
31
  }
32
 
33
+ # ์œ ์‚ฌ ๋ฌธ์ œ ์ƒ์„ฑ
34
+ generated_q, _ = generator.generate_similar_question_with_text(**input_data)
 
 
 
 
 
 
 
 
 
35
 
36
  if generated_q:
37
+ verifier = load_answer_verifier()
38
+ with st.spinner("๋‹ต์•ˆ์„ ๊ฒ€์ฆํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค..."):
39
+ # ๋‹ต์•ˆ ๊ฒ€์ฆ ๏ฟฝ๏ฟฝ๏ฟฝ๋„
40
+ verified_answer = verifier.verify_answer(
41
+ question=generated_q.question,
42
+ choices=generated_q.choices
43
+ )
44
+
45
+ if verified_answer:
46
+ logger.info(f"Answer verified: {verified_answer}")
47
+ # ๊ฒ€์ฆ๋œ ๋‹ต์•ˆ์œผ๋กœ ์—…๋ฐ์ดํŠธ
48
+ return {
49
+ 'question': generated_q.question,
50
+ 'choices': generated_q.choices,
51
+ 'correct': verified_answer, # ๊ฒ€์ฆ๋œ ๋‹ต์•ˆ ์‚ฌ์šฉ
52
+ 'explanation': generated_q.explanation
53
+ }
54
+ else:
55
+ # ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์›๋ณธ ๋‹ต์•ˆ ์‚ฌ์šฉ
56
+ logger.warning("Answer verification failed, using original answer")
57
+ return {
58
+ 'question': generated_q.question,
59
+ 'choices': generated_q.choices,
60
+ 'correct': generated_q.correct_answer,
61
+ 'explanation': generated_q.explanation
62
+ }
63
  except Exception as e:
64
  logger.error(f"Error in generate_similar_question: {str(e)}")
65
  st.error(f"๋ฌธ์ œ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}")
66
  return None
67
 
68
+ return None