KanaQuiz / app.py
Penut's picture
add "n" and select/deselect button
d811247
raw
history blame
5 kB
import random
from copy import deepcopy
import gradio as gr
from romkan import to_hiragana, to_katakana, to_roma
a_base = ["a", "k", "s", "t", "n", "h", "m", "y", "r", "w", "nn"]
a_ext1 = ["g", "z", "d", "b"]
a_ext2 = ["p"]
b_base = ["a", "i", "u", "e", "o"]
b_ext = ["ya", "yo", "yu"]
c_opt = ["濁音", "半濁音", "拗音"]
invalid = ["yi", "ye", "wi", "wu", "we"]
spec_map = {"cya": "cha", "cyu": "chu", "cyo": "cho"}
def next_question(hira, kata, c, quiz_list):
info_flag = False
if not quiz_list:
info_flag = True
quiz_list = init_question(hira, kata, c)
if not quiz_list:
gr.Info("請選擇至少一項測驗!")
return None, quiz_list
if info_flag:
gr.Info("已完成一輪測試!")
return quiz_list.pop(), quiz_list
def init_question(hira, kata, c):
curr_hira = deepcopy(hira)
curr_hira += deepcopy(a_ext1) if "濁音" in c else []
curr_hira += deepcopy(a_ext2) if "半濁音" in c else []
curr_kata = deepcopy(kata)
if curr_kata:
curr_kata += deepcopy(a_ext1) if "濁音" in c else []
curr_kata += deepcopy(a_ext2) if "半濁音" in c else []
curr_b = deepcopy(b_base)
curr_b += deepcopy(b_ext) if "拗音" in c else []
hira_list = [to_hira(a, b) for a in curr_hira for b in curr_b if is_valid(a, b)]
kata_list = [to_kata(a, b) for a in curr_kata for b in curr_b if is_valid(a, b)]
quiz_list = hira_list + kata_list
quiz_list = list(set(quiz_list))
random.shuffle(quiz_list)
return quiz_list
def is_valid(aa: str, bb: str):
if f"{aa}{bb}" in invalid:
return False
if aa == "y":
if bb[0] == "y":
return False
if bb[0] == "w":
return False
if bb == "":
return False
return True
def to_hira(a, b):
return to_hiragana(combine(a, b))
def to_kata(a, b):
return to_katakana(combine(a, b))
def combine(a, b):
if a == "nn":
return "n"
if a == "a":
a = ""
return f"{a}{b}"
def check(kana: str, roma: str, correct, total):
roma = roma.lower()
kana_roma = to_roma(kana)
roma_hira = to_hiragana(roma)
roma_kata = to_katakana(roma)
spec_roma = spec_map.get(roma, roma)
spec_hira = to_hiragana(spec_roma)
spec_kata = to_katakana(spec_roma)
c0 = kana_roma == roma
c1 = roma_hira == kana
c2 = roma_kata == kana
c3 = spec_hira == kana
c4 = spec_kata == kana
ans_correct = any((c0, c1, c2, c3, c4))
correct += ans_correct
total += 1
info = "正確" if ans_correct else f"錯誤 - {kana} ({kana_roma})"
msg = f"{correct}/{total} - " + info
return correct, total, msg, ""
def reset_score():
return 0, 0, "0/0", ""
def select_all():
return a_base, a_base, c_opt
def deselect_all():
return [], [], []
font = gr.themes.GoogleFont("NotoSans CJK")
theme = gr.themes.Soft(font=font)
with gr.Blocks(theme=theme, title="假名小測驗") as app:
correct = gr.State(0)
total = gr.State(0)
quiz_list = gr.State(init_question(a_base[:5], [], []))
with gr.Row():
with gr.Column():
with gr.Tab("測驗"):
with gr.Row():
desc = "完成設定後按下「開始測驗」"
question = gr.Textbox(placeholder=desc, label="題目", interactive=False)
score = gr.Textbox("0/0", label="分數")
answer = gr.Textbox(label="作答")
with gr.Column():
with gr.Tab("設定"):
setting_hira = gr.CheckboxGroup(a_base, value=a_base[:5], label="平假名")
setting_kata = gr.CheckboxGroup(a_base, label="片假名")
setting_c = gr.CheckboxGroup(c_opt, label="延伸")
with gr.Row():
select_all_btn = gr.Button("全選")
deselect_all_btn = gr.Button("全不選")
apply_btn = gr.Button("開始測驗")
chk_inn = [question, answer, correct, total]
chk_out = [correct, total, score, answer]
chk_arg = dict(fn=check, inputs=chk_inn, outputs=chk_out, show_progress="hidden")
nq_inn = [setting_hira, setting_kata, setting_c, quiz_list]
nq_out = [question, quiz_list]
nq_arg = dict(fn=next_question, inputs=nq_inn, outputs=nq_out, show_progress="hidden")
ini_inn = [setting_hira, setting_kata, setting_c]
ini_out = [quiz_list]
ini_arg = dict(fn=init_question, inputs=ini_inn, outputs=ini_out, show_progress="hidden")
reset_arg = dict(fn=reset_score, outputs=chk_out, show_progress="hidden")
opt_out = [setting_hira, setting_kata, setting_c]
answer.submit(**chk_arg).then(**nq_arg)
apply_btn.click(**ini_arg).then(**reset_arg).then(**nq_arg)
select_all_btn.click(select_all, outputs=opt_out, show_progress="hidden")
deselect_all_btn.click(deselect_all, outputs=opt_out, show_progress="hidden")
app.launch(favicon_path="icon.png")