Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,199 +1,109 @@
|
|
| 1 |
-
import
|
| 2 |
-
from openai import OpenAI
|
| 3 |
import random
|
|
|
|
| 4 |
|
| 5 |
-
#
|
| 6 |
API_KEY = "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"
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
"
|
| 13 |
-
"
|
| 14 |
-
"The girl eats an apple under the tree while the wind blows softly."
|
| 15 |
],
|
| 16 |
600: [
|
| 17 |
-
"
|
| 18 |
-
"
|
| 19 |
-
"
|
| 20 |
],
|
| 21 |
-
|
| 22 |
-
"
|
| 23 |
-
"
|
| 24 |
-
"
|
| 25 |
],
|
| 26 |
-
|
| 27 |
-
"
|
| 28 |
-
"
|
| 29 |
-
"
|
| 30 |
],
|
| 31 |
-
|
| 32 |
-
"
|
| 33 |
-
"
|
| 34 |
-
"Researchers
|
| 35 |
]
|
| 36 |
}
|
| 37 |
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
# --- AIに問題生成を依頼 ---
|
| 47 |
-
def generate_question(text):
|
| 48 |
-
prompt = f"""
|
| 49 |
-
Read the following passage and generate ONE multiple-choice question with 4 options (A–D).
|
| 50 |
-
Clearly mark the correct answer with an asterisk (*).
|
| 51 |
-
The format must be:
|
| 52 |
-
|
| 53 |
-
Q: <question text>
|
| 54 |
-
A. <option>
|
| 55 |
-
B. <option>
|
| 56 |
-
C. <option>
|
| 57 |
-
D. <option>
|
| 58 |
-
|
| 59 |
-
Passage:
|
| 60 |
-
{text}
|
| 61 |
-
"""
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
max_tokens=400,
|
| 67 |
-
temperature=0.7,
|
| 68 |
-
)
|
| 69 |
-
raw_output = response.choices[0].message.content.strip()
|
| 70 |
-
|
| 71 |
-
# --- 正解の抽出 ---
|
| 72 |
-
correct_option = None
|
| 73 |
-
for line in raw_output.splitlines():
|
| 74 |
-
if line.lower().startswith("correct:"):
|
| 75 |
-
correct_option = line.split(":")[1].strip().upper()
|
| 76 |
-
break
|
| 77 |
-
|
| 78 |
-
# --- アスタリスク除去(*印があっても削除) ---
|
| 79 |
-
cleaned_lines = []
|
| 80 |
-
for line in raw_output.splitlines():
|
| 81 |
-
if "*" in line:
|
| 82 |
-
line = line.replace("*", "").strip()
|
| 83 |
-
# Correct行は表示しない
|
| 84 |
-
if not line.lower().startswith("correct:"):
|
| 85 |
-
cleaned_lines.append(line)
|
| 86 |
-
|
| 87 |
-
cleaned_output = "\n".join(cleaned_lines)
|
| 88 |
-
return cleaned_output, correct_option
|
| 89 |
-
|
| 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 |
-
new_level = levels[idx + 1]
|
| 96 |
-
elif not prev_correct and idx > 0:
|
| 97 |
-
new_level = levels[idx - 1]
|
| 98 |
-
else:
|
| 99 |
-
new_level = prev_level
|
| 100 |
-
return new_level
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
# --- 未使用の文章を取得 ---
|
| 104 |
-
def get_unused_text(level):
|
| 105 |
-
available = [t for t in texts[level] if (level, t) not in used_texts]
|
| 106 |
if not available:
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
def
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
new_question,
|
| 147 |
-
new_level,
|
| 148 |
-
new_correct,
|
| 149 |
-
""
|
| 150 |
-
)
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
| 153 |
-
|
| 154 |
-
with gr.Blocks() as demo:
|
| 155 |
-
gr.Markdown("# 📘 Adaptive Reading Level Test (Lexile-based)")
|
| 156 |
-
|
| 157 |
-
start_btn = gr.Button("▶️ Start Test")
|
| 158 |
-
|
| 159 |
-
level_display = gr.Textbox(label="Current Level", interactive=False)
|
| 160 |
-
text_display = gr.Textbox(label="Reading Passage", lines=6, interactive=False)
|
| 161 |
-
question_display = gr.Textbox(label="Generated Question", lines=8, interactive=False)
|
| 162 |
-
|
| 163 |
-
user_answer = gr.Radio(choices=["A", "B", "C", "D"], label="Your Answer")
|
| 164 |
-
submit_btn = gr.Button("Submit Answer")
|
| 165 |
-
|
| 166 |
-
feedback_display = gr.Markdown()
|
| 167 |
-
hidden_level = gr.Number(visible=False)
|
| 168 |
-
hidden_correct = gr.Textbox(visible=False)
|
| 169 |
-
|
| 170 |
-
# --- Start Test ---
|
| 171 |
-
start_btn.click(
|
| 172 |
-
fn=start_test,
|
| 173 |
-
inputs=[],
|
| 174 |
-
outputs=[
|
| 175 |
-
level_display,
|
| 176 |
-
text_display,
|
| 177 |
-
question_display,
|
| 178 |
-
hidden_level,
|
| 179 |
-
hidden_correct,
|
| 180 |
-
feedback_display,
|
| 181 |
-
],
|
| 182 |
-
)
|
| 183 |
|
| 184 |
-
|
| 185 |
-
|
|
|
|
|
|
|
| 186 |
fn=next_step,
|
| 187 |
-
inputs=[
|
| 188 |
-
outputs=[
|
| 189 |
-
feedback_display,
|
| 190 |
-
level_display,
|
| 191 |
-
text_display,
|
| 192 |
-
question_display,
|
| 193 |
-
hidden_level,
|
| 194 |
-
hidden_correct,
|
| 195 |
-
feedback_display,
|
| 196 |
-
],
|
| 197 |
)
|
| 198 |
|
|
|
|
|
|
|
|
|
|
| 199 |
demo.launch()
|
|
|
|
| 1 |
+
import os
|
|
|
|
| 2 |
import random
|
| 3 |
+
import gradio as gr
|
| 4 |
|
| 5 |
+
# OpenRouter APIキーを指定
|
| 6 |
API_KEY = "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"
|
| 7 |
+
|
| 8 |
+
# Lexileレベルごとの文章セット(各3問)
|
| 9 |
+
texts_by_level = {
|
| 10 |
+
400: [
|
| 11 |
+
{"text": "Tom has a little dog. The dog likes to run in the park.", "question": "What does Tom's dog like to do?", "options": ["Run in the park", "Sleep all day", "Eat candy", "Climb trees"], "answer": "Run in the park"},
|
| 12 |
+
{"text": "The sun is bright today. Many children are playing outside.", "question": "Where are the children playing?", "options": ["Outside", "In school", "At night", "In the house"], "answer": "Outside"},
|
| 13 |
+
{"text": "Anna has a red ball. She throws it to her friend in the garden.", "question": "Where does Anna throw the ball?", "options": ["In the garden", "In the kitchen", "In the school", "In the store"], "answer": "In the garden"},
|
|
|
|
| 14 |
],
|
| 15 |
600: [
|
| 16 |
+
{"text": "Sara goes to the library every Saturday. She likes to read mystery books.", "question": "What kind of books does Sara like?", "options": ["Mystery books", "Science books", "History books", "Cooking books"], "answer": "Mystery books"},
|
| 17 |
+
{"text": "A frog can jump far. It uses its strong legs to move fast.", "question": "What helps the frog jump far?", "options": ["Its strong legs", "Its arms", "Its eyes", "Its color"], "answer": "Its strong legs"},
|
| 18 |
+
{"text": "Ben plants some seeds in the garden. After a few days, small plants grow.", "question": "What happens after a few days?", "options": ["Small plants grow", "The seeds disappear", "It starts to snow", "The garden becomes empty"], "answer": "Small plants grow"},
|
| 19 |
],
|
| 20 |
+
800: [
|
| 21 |
+
{"text": "David wanted to learn how machines work. He took apart an old radio to see the parts inside.", "question": "Why did David take apart the radio?", "options": ["To see the parts inside", "To break it", "To sell it", "To clean it"], "answer": "To see the parts inside"},
|
| 22 |
+
{"text": "In winter, many animals rest or sleep for a long time. This is called hibernation.", "question": "What do animals do during hibernation?", "options": ["Sleep for a long time", "Eat more food", "Play outside", "Travel to other countries"], "answer": "Sleep for a long time"},
|
| 23 |
+
{"text": "Lisa looked through the telescope and saw bright stars in the night sky.", "question": "What did Lisa use to see the stars?", "options": ["A telescope", "A microscope", "A flashlight", "A magnifier"], "answer": "A telescope"},
|
| 24 |
],
|
| 25 |
+
1000: [
|
| 26 |
+
{"text": "Plants use sunlight to make food. This process is called photosynthesis.", "question": "What do plants use to make food?", "options": ["Sunlight", "Wind", "Soil", "Rain"], "answer": "Sunlight"},
|
| 27 |
+
{"text": "Electric cars are better for the environment because they produce less pollution.", "question": "Why are electric cars better for the environment?", "options": ["They produce less pollution", "They are faster", "They use gasoline", "They make noise"], "answer": "They produce less pollution"},
|
| 28 |
+
{"text": "The computer stores data in its memory. This allows users to save their work.", "question": "What does the computer store in its memory?", "options": ["Data", "Food", "Games", "Pictures only"], "answer": "Data"},
|
| 29 |
],
|
| 30 |
+
1200: [
|
| 31 |
+
{"text": "The scientist observed how the new medicine affected the cells under a microscope.", "question": "What did the scientist use to see the cells?", "options": ["A microscope", "A telescope", "A camera", "A ruler"], "answer": "A microscope"},
|
| 32 |
+
{"text": "During the experiment, the temperature was carefully controlled to ensure accurate results.", "question": "Why was the temperature controlled?", "options": ["To ensure accurate results", "To make it hotter", "To test faster", "To make it colder"], "answer": "To ensure accurate results"},
|
| 33 |
+
{"text": "Researchers developed a new algorithm that can predict weather patterns more accurately.", "question": "What can the new algorithm do?", "options": ["Predict weather patterns", "Make maps", "Control the wind", "Measure temperature"], "answer": "Predict weather patterns"},
|
| 34 |
]
|
| 35 |
}
|
| 36 |
|
| 37 |
+
# 状態管理
|
| 38 |
+
state = {
|
| 39 |
+
"current_level": 600,
|
| 40 |
+
"question_count": 0,
|
| 41 |
+
"used_texts": set(),
|
| 42 |
+
"correct_count": 0,
|
| 43 |
+
"finished": False
|
| 44 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
+
def get_next_question():
|
| 47 |
+
level_texts = texts_by_level[state["current_level"]]
|
| 48 |
+
available = [t for t in level_texts if t["text"] not in state["used_texts"]]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
if not available:
|
| 50 |
+
return None
|
| 51 |
+
question = random.choice(available)
|
| 52 |
+
state["used_texts"].add(question["text"])
|
| 53 |
+
return question
|
| 54 |
+
|
| 55 |
+
def check_answer(user_answer):
|
| 56 |
+
correct = state["current_question"]["answer"]
|
| 57 |
+
return user_answer == correct
|
| 58 |
+
|
| 59 |
+
def quiz_flow(user_answer=None):
|
| 60 |
+
if state["finished"]:
|
| 61 |
+
return "テストはすでに終了しています。再開するにはページをリロードしてください。", None, None, None
|
| 62 |
+
|
| 63 |
+
# 回答処理
|
| 64 |
+
if user_answer is not None:
|
| 65 |
+
if check_answer(user_answer):
|
| 66 |
+
state["correct_count"] += 1
|
| 67 |
+
state["current_level"] = min(state["current_level"] + 200, 1200)
|
| 68 |
+
else:
|
| 69 |
+
state["current_level"] = max(state["current_level"] - 200, 400)
|
| 70 |
+
state["question_count"] += 1
|
| 71 |
+
|
| 72 |
+
# 5問で終了
|
| 73 |
+
if state["question_count"] >= 5:
|
| 74 |
+
state["finished"] = True
|
| 75 |
+
return f"✅ テスト終了!あなたの推定読解レベルは {state['current_level']}L です。", None, None, None
|
| 76 |
+
|
| 77 |
+
# 次の問題取得
|
| 78 |
+
question = get_next_question()
|
| 79 |
+
if not question:
|
| 80 |
+
state["finished"] = True
|
| 81 |
+
return f"📘 これ以上の問題がありません。あなたの推定レベルは {state['current_level']}L です。", None, None, None
|
| 82 |
+
|
| 83 |
+
state["current_question"] = question
|
| 84 |
+
return question["text"], question["question"], question["options"], None
|
| 85 |
+
|
| 86 |
+
# Gradio UI
|
| 87 |
+
with gr.Blocks() as demo:
|
| 88 |
+
gr.Markdown("## 📖 英文読解テスト(全5問)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
+
passage = gr.Textbox(label="文章", interactive=False, lines=4)
|
| 91 |
+
question_box = gr.Textbox(label="問題", interactive=False)
|
| 92 |
+
options = gr.Radio(label="選択肢", choices=[])
|
| 93 |
+
result = gr.Textbox(label="結果表示", interactive=False)
|
| 94 |
|
| 95 |
+
next_btn = gr.Button("次へ")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
+
def next_step(user_choice):
|
| 98 |
+
return quiz_flow(user_choice)
|
| 99 |
+
|
| 100 |
+
next_btn.click(
|
| 101 |
fn=next_step,
|
| 102 |
+
inputs=[options],
|
| 103 |
+
outputs=[passage, question_box, options, result],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
)
|
| 105 |
|
| 106 |
+
# 初期化時に最初の問題表示
|
| 107 |
+
demo.load(fn=quiz_flow, inputs=None, outputs=[passage, question_box, options, result])
|
| 108 |
+
|
| 109 |
demo.launch()
|