Toya0421 commited on
Commit
c92e8e9
·
verified ·
1 Parent(s): be1a0be

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -134
app.py CHANGED
@@ -1,187 +1,206 @@
1
- import os
2
- import time, random, json, datetime
3
- import pandas as pd
4
  import gradio as gr
 
 
 
 
 
 
5
  from openai import OpenAI
6
- from datasets import load_dataset, Dataset, DatasetDict
7
 
8
  API_KEY = os.getenv("API_KEY")
9
- client = OpenAI(
10
- base_url="https://openrouter.ai/api/v1",
11
- api_key=API_KEY
12
- )
13
 
14
- DATASET_REPO = "Toya0421/reading_exercise_logging"
15
-
16
- passages_df = pd.read_csv("passage.csv")
17
 
 
 
 
 
 
18
 
 
19
  def split_into_pages(text, words_per_page=120):
20
  words = text.split()
21
- return [" ".join(words[i:i+words_per_page]) for i in range(0, len(words), words_per_page)]
22
-
23
 
24
- def rewrite_to_lexile(text, lexile_score):
 
25
  prompt = f"""
26
- Rewrite this text to match about {lexile_score}L. Keep meaning. Do not shorten content.
 
 
 
 
 
 
 
 
27
  {text}
28
  """
29
- for _ in range(3):
30
- try:
31
- res = client.chat.completions.create(
32
- model="google/gemma-3-27b-it:free",
33
- messages=[{"role": "user", "content": prompt}]
34
- )
35
- return res.choices[0].message.content.strip()
36
- except:
37
- time.sleep(3)
38
- return text
39
-
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  session_state = {
42
  "user_id": None,
43
- "lexile": None,
44
  "genre": None,
45
- "pages": [],
46
- "page": 0,
47
  "passage_id": None,
 
 
 
48
  "actions": [],
49
- "first_page_time": None
50
  }
51
 
52
- used_passages = set()
53
-
54
-
55
- def save_log(action_type):
56
- finished = datetime.datetime.now().isoformat()
57
- row = {
58
- "user_id": session_state["user_id"],
59
- "lexile_score": session_state["lexile"],
60
- "genre": session_state["genre"],
61
- "passage_id": session_state["passage_id"],
62
- "first_page_time": session_state["first_page_time"],
63
- "finished_time": finished,
64
- "finish_type": action_type,
65
- "actions": json.dumps(session_state["actions"], ensure_ascii=False)
66
- }
67
-
68
- try:
69
- ds = load_dataset(DATASET_REPO)
70
- df = ds["train"].to_pandas()
71
- df = pd.concat([df, pd.DataFrame([row])], ignore_index=True)
72
- new_ds = Dataset.from_pandas(df)
73
- DatasetDict({"train": new_ds}).push_to_hub(DATASET_REPO)
74
- except:
75
- new_ds = Dataset.from_pandas(pd.DataFrame([row]))
76
- DatasetDict({"train": new_ds}).push_to_hub(DATASET_REPO)
77
-
78
-
79
- def load_new_passage():
80
- df = passages_df[passages_df["genre"] == session_state["genre"]]
81
- df = df[~df["passage_id"].isin(used_passages)]
82
 
83
- if df.empty:
84
- return "✅ このジャンルの教材は全て終了しました", "", gr.update(interactive=False)
 
85
 
86
- row = df.sample(1).iloc[0]
87
- used_passages.add(row["passage_id"])
 
 
88
 
89
- rewritten = rewrite_to_lexile(row["text"], session_state["lexile"])
90
  pages = split_into_pages(rewritten)
91
 
92
  session_state.update({
93
- "passage_id": row["passage_id"],
 
94
  "pages": pages,
95
- "page": 0,
96
- "first_page_time": datetime.datetime.now().isoformat(),
97
- "actions": ["open"]
98
  })
99
 
100
- finish_enabled = (len(pages) == 1)
101
- return pages[0], f"1 / {len(pages)}ページ", gr.update(interactive=finish_enabled)
102
-
103
-
104
- def start(user_id, lexile, genre):
105
- if not user_id or not genre:
106
- return "⚠️学生番号とジャンルを入力してください", "", gr.update(interactive=False)
107
-
108
- session_state["user_id"] = user_id
109
- session_state["lexile"] = lexile
110
- session_state["genre"] = genre
111
-
112
- return load_new_passage()
113
-
114
-
115
- def next_page():
116
- session_state["actions"].append("next")
117
-
118
- if session_state["page"] < len(session_state["pages"]) - 1:
119
- session_state["page"] += 1
120
-
121
- p = session_state["page"]
122
- is_last = (p == len(session_state["pages"]) - 1)
123
-
124
  return (
125
- session_state["pages"][p],
126
- f"{p+1} / {len(session_state['pages'])}ページ",
127
- gr.update(interactive=is_last)
 
128
  )
129
 
 
 
 
 
 
 
130
 
131
  def prev_page():
132
- session_state["actions"].append("prev")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
- if session_state["page"] > 0:
135
- session_state["page"] -= 1
136
 
137
- p = session_state["page"]
138
- is_last = (p == len(session_state["pages"]) - 1)
 
 
 
139
 
140
- return (
141
- session_state["pages"][p],
142
- f"{p+1} / {len(session_state['pages'])}ページ",
143
- gr.update(interactive=is_last)
144
- )
145
 
 
 
 
 
146
 
147
- def finish_reading():
148
- session_state["actions"].append("finish")
149
- save_log("finish")
150
- return load_new_passage()
151
 
 
 
 
 
 
 
 
 
152
 
153
- def retire():
154
- session_state["actions"].append("retire")
155
- save_log("retire")
156
- return load_new_passage()
157
 
 
 
 
 
 
 
158
 
159
- # ==== UI ====
160
  with gr.Blocks() as demo:
161
- gr.Markdown("### 📚 Reading Exercise")
162
 
163
- user_id = gr.Textbox(label="学生番号(必須)")
164
- lexile = gr.Number(label="Lexile")
165
- genre_select = gr.Dropdown(
166
- label="ジャンルを選択(1つ)",
167
- choices=sorted(passages_df["genre"].unique())
168
- )
169
- start_btn = gr.Button("スタート")
170
 
171
- text_display = gr.Textbox(label="教材", lines=25)
172
- status = gr.Textbox(label="ページ")
173
 
174
- with gr.Row():
175
- prev_btn = gr.Button("前へ")
176
- next_btn = gr.Button("次へ")
177
 
178
- finish_btn = gr.Button("読み終えた", interactive=False)
 
 
179
  retire_btn = gr.Button("リタイア")
180
 
181
- start_btn.click(start, [user_id, lexile, genre_select], [text_display, status, finish_btn])
182
- next_btn.click(next_page, outputs=[text_display, status, finish_btn])
183
- prev_btn.click(prev_page, outputs=[text_display, status, finish_btn])
184
- finish_btn.click(finish_reading, outputs=[text_display, status, finish_btn])
185
- retire_btn.click(retire, outputs=[text_display, status, finish_btn])
 
186
 
187
  demo.launch()
 
 
 
 
1
  import gradio as gr
2
+ import pandas as pd
3
+ import datetime
4
+ import json
5
+ import uuid
6
+ import os
7
+ from datasets import load_dataset, Dataset
8
  from openai import OpenAI
 
9
 
10
  API_KEY = os.getenv("API_KEY")
11
+ HF_TOKEN = os.getenv("HF_TOKEN")
12
+ HF_DATASET = "Toya0421/reading_exercise_logging"
 
 
13
 
14
+ client = OpenAI(api_key=API_KEY)
 
 
15
 
16
+ # ✅ passage.csv 読み込み
17
+ if os.path.exists("passage.csv") and os.path.getsize("passage.csv") > 0:
18
+ passages_df = pd.read_csv("passage.csv")
19
+ else:
20
+ passages_df = pd.DataFrame(columns=["passage_id","genre","text","original_lexile_score"])
21
 
22
+ # ✅ ページ分割(1ページ=約120ワード)
23
  def split_into_pages(text, words_per_page=120):
24
  words = text.split()
25
+ pages = [" ".join(words[i:i+words_per_page]) for i in range(0, len(words), words_per_page)]
26
+ return pages
27
 
28
+ # 書き換え
29
+ def rewrite_to_lexile(text, target_lexile):
30
  prompt = f"""
31
+ You are an expert at leveling reading materials based on Lexile measures.
32
+
33
+ Rewrite the following English passage so that the estimated Lexile score is close to {target_lexile}.
34
+ - Keep original meaning.
35
+ - Keep length similar.
36
+ - Use vocabulary and structure appropriate for the target Lexile.
37
+
38
+ Return ONLY the rewritten passage. Do not add explanations.
39
+
40
  {text}
41
  """
42
+ resp = client.chat.completions.create(
43
+ model="gpt-5",
44
+ messages=[{"role": "user", "content": prompt}],
45
+ temperature=0.3,
46
+ )
47
+ return resp.choices[0].message.content.strip()
 
 
 
 
 
48
 
49
+ # ✅ Hugging Faceにログ保存
50
+ def save_log_to_hf(row):
51
+ try:
52
+ dataset = load_dataset(HF_DATASET, split="train")
53
+ dataset = dataset.add_item(row)
54
+ dataset.push_to_hub(HF_DATASET)
55
+ except:
56
+ dataset = Dataset.from_list([row])
57
+ dataset.push_to_hub(HF_DATASET)
58
+
59
+ # ✅ ローカルにも追記
60
+ def save_local_csv(row):
61
+ df = pd.DataFrame([row])
62
+ if os.path.exists("full_log.csv"):
63
+ df.to_csv("full_log.csv", mode="a", header=False, index=False)
64
+ else:
65
+ df.to_csv("full_log.csv", index=False)
66
+
67
+ # ✅ 状態管理
68
  session_state = {
69
  "user_id": None,
 
70
  "genre": None,
71
+ "lexile": None,
 
72
  "passage_id": None,
73
+ "original_lexile": None,
74
+ "pages": [],
75
+ "current_page": 0,
76
  "actions": [],
77
+ "start_time": None,
78
  }
79
 
80
+ # テスト開始
81
+ def start_test(student_id, genre, lexile):
82
+ if not student_id:
83
+ return "学生番号を入力してください", "", "", gr.update(interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
+ session_state["user_id"] = student_id
86
+ session_state["genre"] = genre
87
+ session_state["lexile"] = int(lexile)
88
 
89
+ row = passages_df[passages_df["genre"] == genre].sample(1).iloc[0]
90
+ pid = row["passage_id"]
91
+ orig_text = row["text"]
92
+ orig_lex = row["original_lexile_score"]
93
 
94
+ rewritten = rewrite_to_lexile(orig_text, session_state["lexile"])
95
  pages = split_into_pages(rewritten)
96
 
97
  session_state.update({
98
+ "passage_id": pid,
99
+ "original_lexile": orig_lex,
100
  "pages": pages,
101
+ "current_page": 0,
102
+ "start_time": datetime.datetime.now().isoformat(),
103
+ "actions": ["first_page_displayed"]
104
  })
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  return (
107
+ pages[0],
108
+ f"1 / {len(pages)} ページ",
109
+ "",
110
+ gr.update(interactive=True)
111
  )
112
 
113
+ def next_page():
114
+ if session_state["current_page"] < len(session_state["pages"]) - 1:
115
+ session_state["current_page"] += 1
116
+ session_state["actions"].append("next_page")
117
+ page = session_state["current_page"]
118
+ return session_state["pages"][page], f"{page+1} / {len(session_state['pages'])} ページ"
119
 
120
  def prev_page():
121
+ if session_state["current_page"] > 0:
122
+ session_state["current_page"] -= 1
123
+ session_state["actions"].append("prev_page")
124
+ page = session_state["current_page"]
125
+ return session_state["pages"][page], f"{page+1} / {len(session_state['pages'])} ページ"
126
+
127
+ # ✅ 読み終えた or リタイア → 次の教材へ
128
+ def finish(action):
129
+ finished = datetime.datetime.now().isoformat()
130
+
131
+ # ✅ ログ1行を保存
132
+ log_row = {
133
+ "user_id": session_state["user_id"],
134
+ "genre": session_state["genre"],
135
+ "lexile": session_state["lexile"],
136
+ "passage_id": session_state["passage_id"],
137
+ "original_lexile": session_state["original_lexile"],
138
+ "start_time": session_state["start_time"],
139
+ "finished_time": finished,
140
+ "user_action": json.dumps(session_state["actions"], ensure_ascii=False),
141
+ }
142
 
143
+ save_log_to_hf(log_row)
144
+ save_local_csv(log_row)
145
 
146
+ # 次の教材選択
147
+ if action == "finished":
148
+ next_df = passages_df[passages_df["genre"] == session_state["genre"]]
149
+ else: # retire
150
+ next_df = passages_df[passages_df["genre"] != session_state["genre"]]
151
 
152
+ if len(next_df) == 0:
153
+ return "教材がありません。終了です。", ""
 
 
 
154
 
155
+ row = next_df.sample(1).iloc[0]
156
+ pid = row["passage_id"]
157
+ orig_text = row["text"]
158
+ orig_lex = row["original_lexile_score"]
159
 
160
+ rewritten = rewrite_to_lexile(orig_text, session_state["lexile"])
161
+ pages = split_into_pages(rewritten)
 
 
162
 
163
+ session_state.update({
164
+ "passage_id": pid,
165
+ "original_lexile": orig_lex,
166
+ "pages": pages,
167
+ "current_page": 0,
168
+ "start_time": datetime.datetime.now().isoformat(),
169
+ "actions": ["first_page_displayed"]
170
+ })
171
 
172
+ return pages[0], f"1 / {len(pages)} ページ"
 
 
 
173
 
174
+ # ✅ Gradio UI
175
+ genres = [
176
+ "Literature","Science&Technology","History","Social Science&Society",
177
+ "Arts&Culture","Religion&Philosophy","Lifestyle&Hobbies",
178
+ "Health&Medicine","Education&Reference"
179
+ ]
180
 
 
181
  with gr.Blocks() as demo:
182
+ gr.Markdown("## Lexile Reading Exercise")
183
 
184
+ student_id = gr.Textbox(label="学生番号(必須)")
185
+ lexile = gr.Number(label="��検者のLexile")
186
+ genre_select = gr.Dropdown(choices=genres, label="ジャンルを1つ選択")
 
 
 
 
187
 
188
+ start_btn = gr.Button("スタート")
 
189
 
190
+ text_display = gr.Textbox(label="教材", lines=15)
191
+ page_display = gr.Textbox(label="ページ")
192
+ debug_out = gr.Textbox(label="デバッグ用(任意)")
193
 
194
+ prev_btn = gr.Button("前へ")
195
+ next_btn = gr.Button("次へ")
196
+ finish_btn = gr.Button("読み終えた")
197
  retire_btn = gr.Button("リタイア")
198
 
199
+ start_btn.click(start_test, [student_id, genre_select, lexile], [text_display, page_display, debug_out, finish_btn])
200
+ next_btn.click(next_page, [], [text_display, page_display])
201
+ prev_btn.click(prev_page, [], [text_display, page_display])
202
+
203
+ finish_btn.click(lambda: finish("finished"), [], [text_display, page_display])
204
+ retire_btn.click(lambda: finish("retire"), [], [text_display, page_display])
205
 
206
  demo.launch()