Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -8,6 +8,9 @@ import re
|
|
| 8 |
import threading
|
| 9 |
import csv # ★追加(csv 1行追記用)
|
| 10 |
|
|
|
|
|
|
|
|
|
|
| 11 |
# --- API / HF 設定 ---
|
| 12 |
API_KEY = os.getenv("API_KEY")
|
| 13 |
BASE_URL = "https://openrouter.ai/api/v1"
|
|
@@ -41,6 +44,7 @@ LOG_COLUMNS = [
|
|
| 41 |
"assigned_level",
|
| 42 |
"passage_id",
|
| 43 |
"original_level",
|
|
|
|
| 44 |
"action_time",
|
| 45 |
"action_type",
|
| 46 |
"page_text",
|
|
@@ -90,7 +94,8 @@ def download_log_csv(password: str) -> str:
|
|
| 90 |
# ======================================================
|
| 91 |
# 新しい教材管理:passages フォルダからランダム選択
|
| 92 |
# ※ used_passages は session_state に保持(グローバル禁止)
|
| 93 |
-
# ★
|
|
|
|
| 94 |
# ======================================================
|
| 95 |
|
| 96 |
def load_passage_file(text_id):
|
|
@@ -117,12 +122,10 @@ def get_title_from_excel(text_id):
|
|
| 117 |
|
| 118 |
def get_new_passage_random(used_passages_set, target_level):
|
| 119 |
"""
|
|
|
|
| 120 |
passages フォルダからランダムに教材を選び(pg◯.txt)、
|
| 121 |
passage_information.xlsx の Text# の flesch_score を original_level として返す。
|
| 122 |
-
|
| 123 |
-
★変更点:
|
| 124 |
- ユーザーの target_level に対応する目標FREよりも低い(=難しい)教材のみから選ぶ
|
| 125 |
-
※ flesch_score は passage_information.xlsx から取得
|
| 126 |
"""
|
| 127 |
level_to_flesch = {1: 90, 2: 80, 3: 70, 4: 60, 5: 50}
|
| 128 |
target_flesch = float(level_to_flesch[int(target_level)])
|
|
@@ -171,7 +174,54 @@ def get_new_passage_random(used_passages_set, target_level):
|
|
| 171 |
orig_level = None
|
| 172 |
title = None
|
| 173 |
else:
|
| 174 |
-
orig_level = row.iloc[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
title = row.iloc[0].get("Title", None)
|
| 176 |
if pd.isna(title):
|
| 177 |
title = None
|
|
@@ -180,6 +230,7 @@ def get_new_passage_random(used_passages_set, target_level):
|
|
| 180 |
|
| 181 |
return text_id, text, orig_level, title, used_passages_set
|
| 182 |
|
|
|
|
| 183 |
# ======================================================
|
| 184 |
# Group1: 本文のみ抽出(書き換えなし)
|
| 185 |
# ======================================================
|
|
@@ -250,22 +301,68 @@ def extract_main_body(text: str) -> str:
|
|
| 250 |
|
| 251 |
# ======================================================
|
| 252 |
# Rewrite(同時実行制限付き) Group2で使用
|
|
|
|
| 253 |
# ======================================================
|
| 254 |
|
| 255 |
-
def rewrite_level(text, target_level):
|
| 256 |
level_to_flesch = {1: 90, 2: 80, 3: 70, 4: 60, 5: 50}
|
| 257 |
target_flesch = level_to_flesch[int(target_level)]
|
| 258 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
prompt = f"""
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
|
|
|
|
|
|
| 267 |
{text}
|
| 268 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
|
| 270 |
with _rewrite_sem:
|
| 271 |
resp = client.chat.completions.create(
|
|
@@ -302,12 +399,19 @@ def split_pages(text, max_words=300):
|
|
| 302 |
|
| 303 |
# ======================================================
|
| 304 |
# Start(session_stateでユーザー状態管理)
|
|
|
|
| 305 |
# ======================================================
|
| 306 |
|
| 307 |
def start_test(student_id, level_input, group_input, session_state):
|
| 308 |
action = "start_pushed"
|
| 309 |
now = (datetime.utcnow() + timedelta(hours=9)).isoformat()
|
| 310 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
if not student_id or str(student_id).strip() == "":
|
| 312 |
entry = {
|
| 313 |
"user_id": None,
|
|
@@ -315,6 +419,7 @@ def start_test(student_id, level_input, group_input, session_state):
|
|
| 315 |
"assigned_level": None,
|
| 316 |
"passage_id": None,
|
| 317 |
"original_level": None,
|
|
|
|
| 318 |
"action_time": now,
|
| 319 |
"action_type": action,
|
| 320 |
"page_text": None
|
|
@@ -334,7 +439,8 @@ def start_test(student_id, level_input, group_input, session_state):
|
|
| 334 |
gr.update(interactive=False, visible=False),
|
| 335 |
gr.update(interactive=False, visible=True),
|
| 336 |
gr.update(interactive=False, visible=False),
|
| 337 |
-
session_state
|
|
|
|
| 338 |
)
|
| 339 |
|
| 340 |
user_id = str(student_id).strip()
|
|
@@ -349,13 +455,19 @@ def start_test(student_id, level_input, group_input, session_state):
|
|
| 349 |
"assigned_level": level,
|
| 350 |
"passage_id": None,
|
| 351 |
"original_level": None,
|
|
|
|
| 352 |
"action_time": now,
|
| 353 |
"action_type": action,
|
| 354 |
"page_text": None
|
| 355 |
}
|
| 356 |
save_log(entry)
|
| 357 |
|
| 358 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 359 |
if text is None:
|
| 360 |
return (
|
| 361 |
"",
|
|
@@ -370,13 +482,20 @@ def start_test(student_id, level_input, group_input, session_state):
|
|
| 370 |
gr.update(interactive=False, visible=False),
|
| 371 |
gr.update(interactive=False, visible=False),
|
| 372 |
gr.update(interactive=False, visible=False),
|
| 373 |
-
session_state
|
|
|
|
| 374 |
)
|
| 375 |
|
| 376 |
if group == 1:
|
| 377 |
processed = extract_main_body(text)
|
|
|
|
| 378 |
else:
|
| 379 |
-
processed = rewrite_level(text, level)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 380 |
|
| 381 |
pages = split_pages(processed)
|
| 382 |
total = len(pages)
|
|
@@ -397,6 +516,7 @@ def start_test(student_id, level_input, group_input, session_state):
|
|
| 397 |
"assigned_level": level,
|
| 398 |
"passage_id": pid,
|
| 399 |
"original_level": orig_lev,
|
|
|
|
| 400 |
"action_time": now2,
|
| 401 |
"action_type": "page_displayed_1",
|
| 402 |
"page_text": pages[0]
|
|
@@ -422,7 +542,8 @@ def start_test(student_id, level_input, group_input, session_state):
|
|
| 422 |
prev_upd,
|
| 423 |
next_upd,
|
| 424 |
finish_upd,
|
| 425 |
-
session_state
|
|
|
|
| 426 |
)
|
| 427 |
|
| 428 |
# ======================================================
|
|
@@ -441,6 +562,7 @@ def next_page(pages_json, current_page, total_pages, pid, orig_lev, session_stat
|
|
| 441 |
"assigned_level": level,
|
| 442 |
"passage_id": pid,
|
| 443 |
"original_level": orig_lev,
|
|
|
|
| 444 |
"action_time": now,
|
| 445 |
"action_type": "next_pushed",
|
| 446 |
"page_text": None
|
|
@@ -463,6 +585,7 @@ def next_page(pages_json, current_page, total_pages, pid, orig_lev, session_stat
|
|
| 463 |
"assigned_level": level,
|
| 464 |
"passage_id": pid,
|
| 465 |
"original_level": orig_lev,
|
|
|
|
| 466 |
"action_time": now2,
|
| 467 |
"action_type": f"page_displayed_{new_page+1}",
|
| 468 |
"page_text": pages[new_page]
|
|
@@ -503,6 +626,7 @@ def prev_page(pages_json, current_page, total_pages, pid, orig_lev, session_stat
|
|
| 503 |
"assigned_level": level,
|
| 504 |
"passage_id": pid,
|
| 505 |
"original_level": orig_lev,
|
|
|
|
| 506 |
"action_time": now,
|
| 507 |
"action_type": "prev_pushed",
|
| 508 |
"page_text": None
|
|
@@ -530,6 +654,7 @@ def prev_page(pages_json, current_page, total_pages, pid, orig_lev, session_stat
|
|
| 530 |
"assigned_level": level,
|
| 531 |
"passage_id": pid,
|
| 532 |
"original_level": orig_lev,
|
|
|
|
| 533 |
"action_time": now2,
|
| 534 |
"action_type": f"page_displayed_{new_page+1}",
|
| 535 |
"page_text": pages[new_page]
|
|
@@ -561,12 +686,18 @@ def finish_or_retire(pages_json, current_page, pid, orig_lev, action, session_st
|
|
| 561 |
"assigned_level": level,
|
| 562 |
"passage_id": pid,
|
| 563 |
"original_level": orig_lev,
|
|
|
|
| 564 |
"action_time": now,
|
| 565 |
"action_type": action,
|
| 566 |
"page_text": None
|
| 567 |
})
|
| 568 |
|
| 569 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 570 |
if new_text is None:
|
| 571 |
return (
|
| 572 |
"", "教材がありません", "", json.dumps([]), 0, "",
|
|
@@ -579,8 +710,13 @@ def finish_or_retire(pages_json, current_page, pid, orig_lev, action, session_st
|
|
| 579 |
|
| 580 |
if group == 1:
|
| 581 |
processed = extract_main_body(new_text)
|
|
|
|
| 582 |
else:
|
| 583 |
-
processed = rewrite_level(new_text, level)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 584 |
|
| 585 |
new_pages = split_pages(processed)
|
| 586 |
total = len(new_pages)
|
|
@@ -601,6 +737,7 @@ def finish_or_retire(pages_json, current_page, pid, orig_lev, action, session_st
|
|
| 601 |
"assigned_level": level,
|
| 602 |
"passage_id": new_pid,
|
| 603 |
"original_level": new_orig_lev,
|
|
|
|
| 604 |
"action_time": now2,
|
| 605 |
"action_type": "page_displayed_1",
|
| 606 |
"page_text": new_pages[0]
|
|
@@ -659,7 +796,6 @@ custom_css = """
|
|
| 659 |
.gradio-container select {
|
| 660 |
color: inherit !important;
|
| 661 |
}
|
| 662 |
-
|
| 663 |
/* ===============================
|
| 664 |
ライトモード
|
| 665 |
=============================== */
|
|
@@ -699,7 +835,6 @@ custom_css = """
|
|
| 699 |
background-color: #eaeaea !important;
|
| 700 |
}
|
| 701 |
}
|
| 702 |
-
|
| 703 |
/* ===============================
|
| 704 |
ダークモード
|
| 705 |
=============================== */
|
|
@@ -739,7 +874,6 @@ custom_css = """
|
|
| 739 |
color: #e6e6e6 !important;
|
| 740 |
}
|
| 741 |
}
|
| 742 |
-
|
| 743 |
/* ===============================
|
| 744 |
★Group選択:CSSのみで見やすく(EdgeでもOK)
|
| 745 |
=============================== */
|
|
@@ -760,7 +894,6 @@ custom_css = """
|
|
| 760 |
align-items: center !important;
|
| 761 |
gap: 10px !important;
|
| 762 |
}
|
| 763 |
-
|
| 764 |
/* :has が効く環境は行全体ハイライト */
|
| 765 |
@media (prefers-color-scheme: light){
|
| 766 |
#group_radio label:has(input[type="radio"]:checked){
|
|
@@ -822,6 +955,7 @@ with gr.Blocks(css=custom_css) as demo:
|
|
| 822 |
|
| 823 |
retire_btn = gr.Button("リタイア")
|
| 824 |
|
|
|
|
| 825 |
start_btn.click(
|
| 826 |
fn=start_test,
|
| 827 |
inputs=[student_id_input, level_input, group_input, session_state],
|
|
@@ -832,7 +966,8 @@ with gr.Blocks(css=custom_css) as demo:
|
|
| 832 |
hidden_total_pages, hidden_passage_id,
|
| 833 |
hidden_orig_lev, hidden_assigned_lev,
|
| 834 |
prev_btn, next_btn, finish_btn,
|
| 835 |
-
session_state
|
|
|
|
| 836 |
]
|
| 837 |
)
|
| 838 |
|
|
|
|
| 8 |
import threading
|
| 9 |
import csv # ★追加(csv 1行追記用)
|
| 10 |
|
| 11 |
+
# ★追加:Flesch計測
|
| 12 |
+
import textstat
|
| 13 |
+
|
| 14 |
# --- API / HF 設定 ---
|
| 15 |
API_KEY = os.getenv("API_KEY")
|
| 16 |
BASE_URL = "https://openrouter.ai/api/v1"
|
|
|
|
| 44 |
"assigned_level",
|
| 45 |
"passage_id",
|
| 46 |
"original_level",
|
| 47 |
+
"flesch_score", # ★追加:Group1=orig_lev, Group2=rewritten fre
|
| 48 |
"action_time",
|
| 49 |
"action_type",
|
| 50 |
"page_text",
|
|
|
|
| 94 |
# ======================================================
|
| 95 |
# 新しい教材管理:passages フォルダからランダム選択
|
| 96 |
# ※ used_passages は session_state に保持(グローバル禁止)
|
| 97 |
+
# ★Group2:target level よりスコアが低い教材から選ぶ(excelのflesch_score)
|
| 98 |
+
# ★Group1:全教材からランダム選択
|
| 99 |
# ======================================================
|
| 100 |
|
| 101 |
def load_passage_file(text_id):
|
|
|
|
| 122 |
|
| 123 |
def get_new_passage_random(used_passages_set, target_level):
|
| 124 |
"""
|
| 125 |
+
★Group2用:
|
| 126 |
passages フォルダからランダムに教材を選び(pg◯.txt)、
|
| 127 |
passage_information.xlsx の Text# の flesch_score を original_level として返す。
|
|
|
|
|
|
|
| 128 |
- ユーザーの target_level に対応する目標FREよりも低い(=難しい)教材のみから選ぶ
|
|
|
|
| 129 |
"""
|
| 130 |
level_to_flesch = {1: 90, 2: 80, 3: 70, 4: 60, 5: 50}
|
| 131 |
target_flesch = float(level_to_flesch[int(target_level)])
|
|
|
|
| 174 |
orig_level = None
|
| 175 |
title = None
|
| 176 |
else:
|
| 177 |
+
orig_level = row.iloc[0].get("flesch_score", None)
|
| 178 |
+
title = row.iloc[0].get("Title", None)
|
| 179 |
+
if pd.isna(title):
|
| 180 |
+
title = None
|
| 181 |
+
else:
|
| 182 |
+
title = str(title)
|
| 183 |
+
|
| 184 |
+
return text_id, text, orig_level, title, used_passages_set
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def get_new_passage_random_any(used_passages_set):
|
| 188 |
+
"""
|
| 189 |
+
★Group1用:target_level による難易度フィルタなし
|
| 190 |
+
passages フォルダ内の全教材からランダムに選ぶ。
|
| 191 |
+
original_level (=flesch_score) は passage_information.xlsx から取得して返す。
|
| 192 |
+
"""
|
| 193 |
+
files = glob.glob("passages/pg*.txt")
|
| 194 |
+
if not files:
|
| 195 |
+
return None, None, None, None, used_passages_set
|
| 196 |
+
|
| 197 |
+
all_ids = []
|
| 198 |
+
for f in files:
|
| 199 |
+
name = os.path.basename(f)
|
| 200 |
+
num = name.replace("pg", "").replace(".txt", "")
|
| 201 |
+
if num.isdigit():
|
| 202 |
+
all_ids.append(int(num))
|
| 203 |
+
|
| 204 |
+
if not all_ids:
|
| 205 |
+
return None, None, None, None, used_passages_set
|
| 206 |
+
|
| 207 |
+
available = [pid for pid in all_ids if pid not in used_passages_set]
|
| 208 |
+
if not available:
|
| 209 |
+
used_passages_set = set()
|
| 210 |
+
available = list(all_ids)
|
| 211 |
+
|
| 212 |
+
text_id = random.choice(available)
|
| 213 |
+
used_passages_set.add(text_id)
|
| 214 |
+
|
| 215 |
+
text = load_passage_file(text_id)
|
| 216 |
+
if text is None:
|
| 217 |
+
return None, None, None, None, used_passages_set
|
| 218 |
+
|
| 219 |
+
row = passage_info_df[passage_info_df["Text#"] == text_id]
|
| 220 |
+
if len(row) == 0:
|
| 221 |
+
orig_level = None
|
| 222 |
+
title = None
|
| 223 |
+
else:
|
| 224 |
+
orig_level = row.iloc[0].get("flesch_score", None)
|
| 225 |
title = row.iloc[0].get("Title", None)
|
| 226 |
if pd.isna(title):
|
| 227 |
title = None
|
|
|
|
| 230 |
|
| 231 |
return text_id, text, orig_level, title, used_passages_set
|
| 232 |
|
| 233 |
+
|
| 234 |
# ======================================================
|
| 235 |
# Group1: 本文のみ抽出(書き換えなし)
|
| 236 |
# ======================================================
|
|
|
|
| 301 |
|
| 302 |
# ======================================================
|
| 303 |
# Rewrite(同時実行制限付き) Group2で使用
|
| 304 |
+
# ★プロンプトを「改善後プロンプト」に置換
|
| 305 |
# ======================================================
|
| 306 |
|
| 307 |
+
def rewrite_level(text, target_level, original_fre):
|
| 308 |
level_to_flesch = {1: 90, 2: 80, 3: 70, 4: 60, 5: 50}
|
| 309 |
target_flesch = level_to_flesch[int(target_level)]
|
| 310 |
|
| 311 |
+
try:
|
| 312 |
+
original_fre_val = float(original_fre)
|
| 313 |
+
except Exception:
|
| 314 |
+
original_fre_val = float("nan")
|
| 315 |
+
|
| 316 |
prompt = f"""
|
| 317 |
+
The Flesch Reading Ease score is a numeric measure of text readability,
|
| 318 |
+
where higher scores indicate easier readability and lower scores indicate more difficult text.
|
| 319 |
+
|
| 320 |
+
In this task, we are trying to rewrite a given text into the target Flesch Reading Ease score
|
| 321 |
+
and preserving the original meaning and information.
|
| 322 |
+
|
| 323 |
+
Given the original draft (Flesch Reading Ease = {original_fre_val}):
|
| 324 |
+
|
| 325 |
+
[TEXT START]
|
| 326 |
{text}
|
| 327 |
+
[TEXT END]
|
| 328 |
+
|
| 329 |
+
Rewrite the above text to the difficulty level of:
|
| 330 |
+
Flesch Reading Ease = {target_flesch}
|
| 331 |
+
|
| 332 |
+
Follow the instructions below carefully.
|
| 333 |
+
|
| 334 |
+
Content preservation:
|
| 335 |
+
- Maintain the original meaning faithfully.
|
| 336 |
+
- Do not add new information.
|
| 337 |
+
- Do not remove important information.
|
| 338 |
+
- Do not introduce interpretations or opinions that are not present in the original text.
|
| 339 |
+
|
| 340 |
+
Scope of rewriting:
|
| 341 |
+
- Rewrite ONLY the main body text.
|
| 342 |
+
- Completely EXCLUDE titles, headings, chapter labels, author names, source information, footnotes, annotations, and introductions.
|
| 343 |
+
- Do NOT include any text other than the rewritten main body under any circumstances.
|
| 344 |
+
|
| 345 |
+
Readability control guidelines:
|
| 346 |
+
- Make sentences shorter.
|
| 347 |
+
- Prefer familiar, high-frequency vocabulary.
|
| 348 |
+
- Use simple and direct sentence structures.
|
| 349 |
+
- Avoid jargon; if technical terms are necessary, explain them clearly in simple language.
|
| 350 |
+
- Minimize figurative language, idioms, and expressions whose meanings are not directly inferable.
|
| 351 |
+
|
| 352 |
+
Language modernization:
|
| 353 |
+
- Rewrite the text in clear, modern English.
|
| 354 |
+
- Remove archaic expressions and unnatural or outdated syntax typical of older texts at all levels.
|
| 355 |
+
|
| 356 |
+
Structure and formatting:
|
| 357 |
+
- Preserve the original paragraph structure of the main text.
|
| 358 |
+
- Insert exactly ONE blank line between paragraphs.
|
| 359 |
+
- Do NOT create new section breaks, chapter divisions, or headings.
|
| 360 |
+
|
| 361 |
+
Output constraints:
|
| 362 |
+
- Output only the rewritten text.
|
| 363 |
+
- Do not include explanations, comments, or metadata.
|
| 364 |
+
- Do not include [TEXT START] and [TEXT END] in the output.
|
| 365 |
+
""".strip()
|
| 366 |
|
| 367 |
with _rewrite_sem:
|
| 368 |
resp = client.chat.completions.create(
|
|
|
|
| 399 |
|
| 400 |
# ======================================================
|
| 401 |
# Start(session_stateでユーザー状態管理)
|
| 402 |
+
# ★Start後:入力はリロードまで固定(1回だけ)
|
| 403 |
# ======================================================
|
| 404 |
|
| 405 |
def start_test(student_id, level_input, group_input, session_state):
|
| 406 |
action = "start_pushed"
|
| 407 |
now = (datetime.utcnow() + timedelta(hours=9)).isoformat()
|
| 408 |
|
| 409 |
+
# ★Start押下時点で入力を固定(成功/失敗問わず、リロードまで不可)
|
| 410 |
+
lock_student = gr.update(interactive=False)
|
| 411 |
+
lock_group = gr.update(interactive=False)
|
| 412 |
+
lock_level = gr.update(interactive=False)
|
| 413 |
+
lock_start = gr.update(interactive=False)
|
| 414 |
+
|
| 415 |
if not student_id or str(student_id).strip() == "":
|
| 416 |
entry = {
|
| 417 |
"user_id": None,
|
|
|
|
| 419 |
"assigned_level": None,
|
| 420 |
"passage_id": None,
|
| 421 |
"original_level": None,
|
| 422 |
+
"flesch_score": None,
|
| 423 |
"action_time": now,
|
| 424 |
"action_type": action,
|
| 425 |
"page_text": None
|
|
|
|
| 439 |
gr.update(interactive=False, visible=False),
|
| 440 |
gr.update(interactive=False, visible=True),
|
| 441 |
gr.update(interactive=False, visible=False),
|
| 442 |
+
session_state,
|
| 443 |
+
lock_student, lock_group, lock_level, lock_start
|
| 444 |
)
|
| 445 |
|
| 446 |
user_id = str(student_id).strip()
|
|
|
|
| 455 |
"assigned_level": level,
|
| 456 |
"passage_id": None,
|
| 457 |
"original_level": None,
|
| 458 |
+
"flesch_score": None,
|
| 459 |
"action_time": now,
|
| 460 |
"action_type": action,
|
| 461 |
"page_text": None
|
| 462 |
}
|
| 463 |
save_log(entry)
|
| 464 |
|
| 465 |
+
# ★変更:Group1は全教材、Group2はレベル制限あり
|
| 466 |
+
if group == 1:
|
| 467 |
+
pid, text, orig_lev, title, used_passages_set = get_new_passage_random_any(used_passages_set)
|
| 468 |
+
else:
|
| 469 |
+
pid, text, orig_lev, title, used_passages_set = get_new_passage_random(used_passages_set, level)
|
| 470 |
+
|
| 471 |
if text is None:
|
| 472 |
return (
|
| 473 |
"",
|
|
|
|
| 482 |
gr.update(interactive=False, visible=False),
|
| 483 |
gr.update(interactive=False, visible=False),
|
| 484 |
gr.update(interactive=False, visible=False),
|
| 485 |
+
session_state,
|
| 486 |
+
lock_student, lock_group, lock_level, lock_start
|
| 487 |
)
|
| 488 |
|
| 489 |
if group == 1:
|
| 490 |
processed = extract_main_body(text)
|
| 491 |
+
measured_fre = orig_lev # ★要件:Group1はpassage_informationのflesch_scoreを記録
|
| 492 |
else:
|
| 493 |
+
processed = rewrite_level(text, level, orig_lev)
|
| 494 |
+
# ★要件:Group2は書き換え後をtextstatで計測して記録
|
| 495 |
+
try:
|
| 496 |
+
measured_fre = float(textstat.flesch_reading_ease(processed))
|
| 497 |
+
except Exception:
|
| 498 |
+
measured_fre = None
|
| 499 |
|
| 500 |
pages = split_pages(processed)
|
| 501 |
total = len(pages)
|
|
|
|
| 516 |
"assigned_level": level,
|
| 517 |
"passage_id": pid,
|
| 518 |
"original_level": orig_lev,
|
| 519 |
+
"flesch_score": measured_fre, # ★追加
|
| 520 |
"action_time": now2,
|
| 521 |
"action_type": "page_displayed_1",
|
| 522 |
"page_text": pages[0]
|
|
|
|
| 542 |
prev_upd,
|
| 543 |
next_upd,
|
| 544 |
finish_upd,
|
| 545 |
+
session_state,
|
| 546 |
+
lock_student, lock_group, lock_level, lock_start
|
| 547 |
)
|
| 548 |
|
| 549 |
# ======================================================
|
|
|
|
| 562 |
"assigned_level": level,
|
| 563 |
"passage_id": pid,
|
| 564 |
"original_level": orig_lev,
|
| 565 |
+
"flesch_score": "", # ★列維持
|
| 566 |
"action_time": now,
|
| 567 |
"action_type": "next_pushed",
|
| 568 |
"page_text": None
|
|
|
|
| 585 |
"assigned_level": level,
|
| 586 |
"passage_id": pid,
|
| 587 |
"original_level": orig_lev,
|
| 588 |
+
"flesch_score": "", # ★列維持
|
| 589 |
"action_time": now2,
|
| 590 |
"action_type": f"page_displayed_{new_page+1}",
|
| 591 |
"page_text": pages[new_page]
|
|
|
|
| 626 |
"assigned_level": level,
|
| 627 |
"passage_id": pid,
|
| 628 |
"original_level": orig_lev,
|
| 629 |
+
"flesch_score": "", # ★列維持
|
| 630 |
"action_time": now,
|
| 631 |
"action_type": "prev_pushed",
|
| 632 |
"page_text": None
|
|
|
|
| 654 |
"assigned_level": level,
|
| 655 |
"passage_id": pid,
|
| 656 |
"original_level": orig_lev,
|
| 657 |
+
"flesch_score": "", # ★列維持
|
| 658 |
"action_time": now2,
|
| 659 |
"action_type": f"page_displayed_{new_page+1}",
|
| 660 |
"page_text": pages[new_page]
|
|
|
|
| 686 |
"assigned_level": level,
|
| 687 |
"passage_id": pid,
|
| 688 |
"original_level": orig_lev,
|
| 689 |
+
"flesch_score": "", # ★列維持
|
| 690 |
"action_time": now,
|
| 691 |
"action_type": action,
|
| 692 |
"page_text": None
|
| 693 |
})
|
| 694 |
|
| 695 |
+
# ★変更:Group1は全教材、Group2はレベル制限あり
|
| 696 |
+
if group == 1:
|
| 697 |
+
new_pid, new_text, new_orig_lev, title, used_passages_set = get_new_passage_random_any(used_passages_set)
|
| 698 |
+
else:
|
| 699 |
+
new_pid, new_text, new_orig_lev, title, used_passages_set = get_new_passage_random(used_passages_set, level)
|
| 700 |
+
|
| 701 |
if new_text is None:
|
| 702 |
return (
|
| 703 |
"", "教材がありません", "", json.dumps([]), 0, "",
|
|
|
|
| 710 |
|
| 711 |
if group == 1:
|
| 712 |
processed = extract_main_body(new_text)
|
| 713 |
+
measured_fre = new_orig_lev
|
| 714 |
else:
|
| 715 |
+
processed = rewrite_level(new_text, level, new_orig_lev)
|
| 716 |
+
try:
|
| 717 |
+
measured_fre = float(textstat.flesch_reading_ease(processed))
|
| 718 |
+
except Exception:
|
| 719 |
+
measured_fre = None
|
| 720 |
|
| 721 |
new_pages = split_pages(processed)
|
| 722 |
total = len(new_pages)
|
|
|
|
| 737 |
"assigned_level": level,
|
| 738 |
"passage_id": new_pid,
|
| 739 |
"original_level": new_orig_lev,
|
| 740 |
+
"flesch_score": measured_fre, # ★追加
|
| 741 |
"action_time": now2,
|
| 742 |
"action_type": "page_displayed_1",
|
| 743 |
"page_text": new_pages[0]
|
|
|
|
| 796 |
.gradio-container select {
|
| 797 |
color: inherit !important;
|
| 798 |
}
|
|
|
|
| 799 |
/* ===============================
|
| 800 |
ライトモード
|
| 801 |
=============================== */
|
|
|
|
| 835 |
background-color: #eaeaea !important;
|
| 836 |
}
|
| 837 |
}
|
|
|
|
| 838 |
/* ===============================
|
| 839 |
ダークモード
|
| 840 |
=============================== */
|
|
|
|
| 874 |
color: #e6e6e6 !important;
|
| 875 |
}
|
| 876 |
}
|
|
|
|
| 877 |
/* ===============================
|
| 878 |
★Group選択:CSSのみで見やすく(EdgeでもOK)
|
| 879 |
=============================== */
|
|
|
|
| 894 |
align-items: center !important;
|
| 895 |
gap: 10px !important;
|
| 896 |
}
|
|
|
|
| 897 |
/* :has が効く環境は行全体ハイライト */
|
| 898 |
@media (prefers-color-scheme: light){
|
| 899 |
#group_radio label:has(input[type="radio"]:checked){
|
|
|
|
| 955 |
|
| 956 |
retire_btn = gr.Button("リタイア")
|
| 957 |
|
| 958 |
+
# ★変更:Start後に入力をロックするため、入力コンポーネントもoutputsに追加
|
| 959 |
start_btn.click(
|
| 960 |
fn=start_test,
|
| 961 |
inputs=[student_id_input, level_input, group_input, session_state],
|
|
|
|
| 966 |
hidden_total_pages, hidden_passage_id,
|
| 967 |
hidden_orig_lev, hidden_assigned_lev,
|
| 968 |
prev_btn, next_btn, finish_btn,
|
| 969 |
+
session_state,
|
| 970 |
+
student_id_input, group_input, level_input, start_btn
|
| 971 |
]
|
| 972 |
)
|
| 973 |
|