Toya0421 commited on
Commit
96d0ac4
·
verified ·
1 Parent(s): ab82f6e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -42
app.py CHANGED
@@ -1,10 +1,14 @@
1
  import gradio as gr
2
  from openai import OpenAI
3
- import os
4
- import random
 
5
 
6
- API_KEY = os.getenv("API_KEY")
 
7
  BASE_URL = "https://openrouter.ai/api/v1"
 
 
8
 
9
  # --- Lexile難易度別教材(各段階3つずつ) ---
10
  texts = {
@@ -43,7 +47,7 @@ client = OpenAI(
43
  api_key=API_KEY
44
  )
45
 
46
- # --- AIに問題生成を依頼 ---
47
  def generate_question(text):
48
  prompt = f"""
49
  Read the following passage and create ONE multiple-choice question with 4 options (A–D).
@@ -54,11 +58,9 @@ def generate_question(text):
54
  B. <option>
55
  C. <option>
56
  D. <option>
57
-
58
  Passage:
59
  {text}
60
  """
61
-
62
  response = client.chat.completions.create(
63
  model="google/gemma-3-27b-it:free",
64
  messages=[{"role": "user", "content": prompt}],
@@ -67,22 +69,17 @@ def generate_question(text):
67
  )
68
  return response.choices[0].message.content.strip()
69
 
70
- # --- AIで正解を判定(別プロンプト) ---
71
  def check_answer_with_ai(text, question, user_answer):
72
  prompt = f"""
73
  Read the passage and question below. Decide if the user's answer is correct.
74
-
75
  Passage:
76
  {text}
77
-
78
  Question:
79
  {question}
80
-
81
  User Answer: {user_answer}
82
-
83
  Respond with only one word: "Correct" or "Incorrect".
84
  """
85
-
86
  response = client.chat.completions.create(
87
  model="google/gemma-3-27b-it:free",
88
  messages=[{"role": "user", "content": prompt}],
@@ -91,16 +88,38 @@ def check_answer_with_ai(text, question, user_answer):
91
  )
92
  return response.choices[0].message.content.strip().lower() == "correct"
93
 
94
- # --- 適応型テストの進行 ---
95
  def adaptive_test(prev_level, prev_correct):
96
  idx = levels.index(prev_level)
97
  if prev_correct and idx < len(levels) - 1:
98
- new_level = levels[idx + 1]
99
  elif not prev_correct and idx > 0:
100
- new_level = levels[idx - 1]
101
- else:
102
- new_level = prev_level
103
- return new_level
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
  # --- 状態管理 ---
106
  used_texts = set()
@@ -112,39 +131,51 @@ def start_test():
112
  global used_texts, question_count
113
  used_texts = set()
114
  question_count = 0
115
-
116
- level = 850 # 中間レベルから開始
117
  text = random.choice(texts[level])
118
  used_texts.add(text)
119
  question = generate_question(text)
120
- return text, question, level, None, "", "", True # True = show test
 
121
 
122
- # --- 回答処理して次へ ---
123
- def next_step(prev_level, user_answer, question_text, passage_text):
124
  global question_count, used_texts
125
  question_count += 1
126
 
 
 
 
127
  correct = check_answer_with_ai(passage_text, question_text, user_answer)
128
  new_level = adaptive_test(prev_level, correct)
 
 
 
 
 
 
 
 
 
 
 
 
129
  feedback = "✅ Correct!" if correct else "❌ Incorrect."
130
 
131
- # --- 終了条件 ---
132
  if question_count >= MAX_QUESTIONS:
133
- feedback = f"🎯 **Test finished!**\n\nYour estimated reading level is **{new_level}L**."
134
- feedback += "\n\nThank you for participating!"
135
- # hide text, question, and answer area
136
- return feedback, "", "", new_level, None, "", False
137
 
138
- # --- 次の文章を選択(同じものを避ける) ---
139
  available = [t for t in texts[new_level] if t not in used_texts]
140
  if not available:
141
  available = texts[new_level]
142
  next_text = random.choice(available)
143
  used_texts.add(next_text)
144
  next_question = generate_question(next_text)
145
-
146
- feedback += f"\n➡️ Next question loading..."
147
- return feedback, next_text, next_question, new_level, None, "", True
148
 
149
  # --- Gradio UI ---
150
  with gr.Blocks() as demo:
@@ -152,19 +183,17 @@ with gr.Blocks() as demo:
152
 
153
  start_btn = gr.Button("▶️ Start Test")
154
 
155
- # 可視領域
156
  text_display = gr.Textbox(label="Reading Passage", lines=6, interactive=False, visible=True)
157
  question_display = gr.Textbox(label="Generated Question", lines=8, interactive=False, visible=True)
158
  user_answer = gr.Radio(choices=["A", "B", "C", "D"], label="Your Answer", visible=True)
159
  submit_btn = gr.Button("Submit Answer", visible=True)
160
 
161
- # フィードバック領域
162
  feedback_display = gr.Markdown()
163
  hidden_level = gr.Number(visible=False)
164
  hidden_passage = gr.Textbox(visible=False)
165
  test_visible = gr.State(True)
 
166
 
167
- # --- Start Test ---
168
  start_btn.click(
169
  fn=start_test,
170
  inputs=[],
@@ -175,14 +204,14 @@ with gr.Blocks() as demo:
175
  user_answer,
176
  feedback_display,
177
  hidden_passage,
178
- test_visible
 
179
  ]
180
  )
181
 
182
- # --- Submit & Move to Next ---
183
  submit_btn.click(
184
  fn=next_step,
185
- inputs=[hidden_level, user_answer, question_display, text_display],
186
  outputs=[
187
  feedback_display,
188
  text_display,
@@ -190,14 +219,15 @@ with gr.Blocks() as demo:
190
  hidden_level,
191
  user_answer,
192
  hidden_passage,
193
- test_visible
 
194
  ]
195
  )
196
 
197
- # --- 表示制御 ---
198
  def toggle_visibility(show):
199
- visible = bool(show)
200
- return gr.update(visible=visible), gr.update(visible=visible), gr.update(visible=visible), gr.update(visible=visible)
 
201
 
202
  test_visible.change(
203
  fn=toggle_visibility,
 
1
  import gradio as gr
2
  from openai import OpenAI
3
+ from datasets import load_dataset, Dataset
4
+ import pandas as pd
5
+ import time, os, random
6
 
7
+ # --- API設定 ---
8
+ API_KEY = os.getenv("API_KEY") # Hugging FaceではSpaces Secretsに設定
9
  BASE_URL = "https://openrouter.ai/api/v1"
10
+ HF_TOKEN = os.getenv("HF_TOKEN") # Hugging Face Token(Secretsに設定)
11
+ DATASET_REPO = "your-username/reading_test_logs" # ← 自分のDataset名に変更
12
 
13
  # --- Lexile難易度別教材(各段階3つずつ) ---
14
  texts = {
 
47
  api_key=API_KEY
48
  )
49
 
50
+ # --- 問題生成 ---
51
  def generate_question(text):
52
  prompt = f"""
53
  Read the following passage and create ONE multiple-choice question with 4 options (A–D).
 
58
  B. <option>
59
  C. <option>
60
  D. <option>
 
61
  Passage:
62
  {text}
63
  """
 
64
  response = client.chat.completions.create(
65
  model="google/gemma-3-27b-it:free",
66
  messages=[{"role": "user", "content": prompt}],
 
69
  )
70
  return response.choices[0].message.content.strip()
71
 
72
+ # --- AIで正判定 ---
73
  def check_answer_with_ai(text, question, user_answer):
74
  prompt = f"""
75
  Read the passage and question below. Decide if the user's answer is correct.
 
76
  Passage:
77
  {text}
 
78
  Question:
79
  {question}
 
80
  User Answer: {user_answer}
 
81
  Respond with only one word: "Correct" or "Incorrect".
82
  """
 
83
  response = client.chat.completions.create(
84
  model="google/gemma-3-27b-it:free",
85
  messages=[{"role": "user", "content": prompt}],
 
88
  )
89
  return response.choices[0].message.content.strip().lower() == "correct"
90
 
91
+ # --- 適応型テスト ---
92
  def adaptive_test(prev_level, prev_correct):
93
  idx = levels.index(prev_level)
94
  if prev_correct and idx < len(levels) - 1:
95
+ return levels[idx + 1]
96
  elif not prev_correct and idx > 0:
97
+ return levels[idx - 1]
98
+ return prev_level
99
+
100
+ # --- ログ書き込み関数 ---
101
+ def log_to_hf_dataset(user_id, level, passage, question, user_answer, correct, response_time):
102
+ log_entry = {
103
+ "user_id": [user_id],
104
+ "lexile_level": [level],
105
+ "passage": [passage],
106
+ "question": [question],
107
+ "user_answer": [user_answer],
108
+ "correct": [correct],
109
+ "response_time": [response_time],
110
+ "timestamp": [pd.Timestamp.now().strftime("%Y-%m-%d %H:%M:%S")],
111
+ }
112
+
113
+ new_df = pd.DataFrame(log_entry)
114
+ try:
115
+ dataset = load_dataset(DATASET_REPO, split="train", use_auth_token=HF_TOKEN)
116
+ existing_df = dataset.to_pandas()
117
+ updated_df = pd.concat([existing_df, new_df], ignore_index=True)
118
+ except Exception:
119
+ updated_df = new_df
120
+
121
+ updated_dataset = Dataset.from_pandas(updated_df)
122
+ updated_dataset.push_to_hub(DATASET_REPO, token=HF_TOKEN)
123
 
124
  # --- 状態管理 ---
125
  used_texts = set()
 
131
  global used_texts, question_count
132
  used_texts = set()
133
  question_count = 0
134
+ level = 850
 
135
  text = random.choice(texts[level])
136
  used_texts.add(text)
137
  question = generate_question(text)
138
+ start_time = time.time()
139
+ return text, question, level, None, "", "", True, start_time
140
 
141
+ # --- 回答処理 ---
142
+ def next_step(prev_level, user_answer, question_text, passage_text, start_time):
143
  global question_count, used_texts
144
  question_count += 1
145
 
146
+ end_time = time.time()
147
+ response_time = round(end_time - start_time, 2)
148
+
149
  correct = check_answer_with_ai(passage_text, question_text, user_answer)
150
  new_level = adaptive_test(prev_level, correct)
151
+
152
+ # ログ保存
153
+ log_to_hf_dataset(
154
+ user_id="test_user",
155
+ level=prev_level,
156
+ passage=passage_text,
157
+ question=question_text,
158
+ user_answer=user_answer,
159
+ correct=correct,
160
+ response_time=response_time
161
+ )
162
+
163
  feedback = "✅ Correct!" if correct else "❌ Incorrect."
164
 
165
+ # 終了条件
166
  if question_count >= MAX_QUESTIONS:
167
+ feedback = f"🎯 **Test finished!**\n\nYour estimated reading level is **{new_level}L**.\n\nThank you for participating!"
168
+ return feedback, "", "", new_level, None, "", False, 0.0
 
 
169
 
170
+ # 次の文章を選
171
  available = [t for t in texts[new_level] if t not in used_texts]
172
  if not available:
173
  available = texts[new_level]
174
  next_text = random.choice(available)
175
  used_texts.add(next_text)
176
  next_question = generate_question(next_text)
177
+ feedback += "\n➡️ Next question loading..."
178
+ return feedback, next_text, next_question, new_level, None, "", True, time.time()
 
179
 
180
  # --- Gradio UI ---
181
  with gr.Blocks() as demo:
 
183
 
184
  start_btn = gr.Button("▶️ Start Test")
185
 
 
186
  text_display = gr.Textbox(label="Reading Passage", lines=6, interactive=False, visible=True)
187
  question_display = gr.Textbox(label="Generated Question", lines=8, interactive=False, visible=True)
188
  user_answer = gr.Radio(choices=["A", "B", "C", "D"], label="Your Answer", visible=True)
189
  submit_btn = gr.Button("Submit Answer", visible=True)
190
 
 
191
  feedback_display = gr.Markdown()
192
  hidden_level = gr.Number(visible=False)
193
  hidden_passage = gr.Textbox(visible=False)
194
  test_visible = gr.State(True)
195
+ hidden_start_time = gr.Number(visible=False)
196
 
 
197
  start_btn.click(
198
  fn=start_test,
199
  inputs=[],
 
204
  user_answer,
205
  feedback_display,
206
  hidden_passage,
207
+ test_visible,
208
+ hidden_start_time
209
  ]
210
  )
211
 
 
212
  submit_btn.click(
213
  fn=next_step,
214
+ inputs=[hidden_level, user_answer, question_display, text_display, hidden_start_time],
215
  outputs=[
216
  feedback_display,
217
  text_display,
 
219
  hidden_level,
220
  user_answer,
221
  hidden_passage,
222
+ test_visible,
223
+ hidden_start_time
224
  ]
225
  )
226
 
 
227
  def toggle_visibility(show):
228
+ v = bool(show)
229
+ return (gr.update(visible=v), gr.update(visible=v),
230
+ gr.update(visible=v), gr.update(visible=v))
231
 
232
  test_visible.change(
233
  fn=toggle_visibility,