Spaces:
Running
Running
update layout
Browse files
app.py
CHANGED
@@ -5,50 +5,36 @@ import gradio as gr
|
|
5 |
from romkan import to_hiragana, to_katakana, to_roma
|
6 |
|
7 |
a_base = ["a", "k", "s", "t", "n", "h", "m", "y", "r", "w", "nn"]
|
8 |
-
a_ext1 = ["g", "z", "d", "b"]
|
9 |
-
a_ext2 = ["p"]
|
10 |
-
|
11 |
b_base = ["a", "i", "u", "e", "o"]
|
12 |
-
|
13 |
-
|
14 |
-
c_opt = ["濁音", "半濁音", "拗音"]
|
15 |
|
16 |
invalid = ["yi", "ye", "wi", "wu", "we"]
|
17 |
spec_map = {"cya": "cha", "cyu": "chu", "cyo": "cho"}
|
18 |
|
19 |
|
20 |
-
def next_question(hira, kata,
|
21 |
-
info_flag = False
|
22 |
-
|
23 |
if not quiz_list:
|
24 |
-
|
25 |
-
quiz_list = init_question(hira, kata, c)
|
26 |
|
27 |
if not quiz_list:
|
28 |
gr.Info("請選擇至少一項測驗!")
|
29 |
return None, quiz_list
|
30 |
|
31 |
-
if info_flag:
|
32 |
-
gr.Info("已完成一輪測試!")
|
33 |
-
|
34 |
return quiz_list.pop(), quiz_list
|
35 |
|
36 |
|
37 |
-
def
|
38 |
-
|
39 |
-
|
40 |
-
curr_hira += deepcopy(a_ext2) if "半濁音" in c else []
|
41 |
|
42 |
-
|
43 |
-
|
44 |
-
curr_kata += deepcopy(a_ext1) if "濁音" in c else []
|
45 |
-
curr_kata += deepcopy(a_ext2) if "半濁音" in c else []
|
46 |
|
47 |
-
|
48 |
-
|
49 |
|
50 |
-
hira_list = [to_hira(a, b) for a in
|
51 |
-
kata_list = [to_kata(a, b) for a in
|
52 |
|
53 |
quiz_list = hira_list + kata_list
|
54 |
quiz_list = list(set(quiz_list))
|
@@ -57,6 +43,12 @@ def init_question(hira, kata, c):
|
|
57 |
return quiz_list
|
58 |
|
59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
def is_valid(aa: str, bb: str):
|
61 |
if f"{aa}{bb}" in invalid:
|
62 |
return False
|
@@ -69,6 +61,9 @@ def is_valid(aa: str, bb: str):
|
|
69 |
if bb == "":
|
70 |
return False
|
71 |
|
|
|
|
|
|
|
72 |
return True
|
73 |
|
74 |
|
@@ -88,7 +83,7 @@ def combine(a, b):
|
|
88 |
return f"{a}{b}"
|
89 |
|
90 |
|
91 |
-
def check(kana: str, roma: str, correct, total):
|
92 |
roma = roma.lower()
|
93 |
|
94 |
kana_roma = to_roma(kana)
|
@@ -111,67 +106,113 @@ def check(kana: str, roma: str, correct, total):
|
|
111 |
info = "正確" if ans_correct else f"錯誤 - {kana} ({kana_roma})"
|
112 |
msg = f"{correct}/{total} - " + info
|
113 |
|
114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
|
116 |
|
117 |
def reset_score():
|
118 |
-
return 0, 0, "0/0",
|
119 |
|
120 |
|
121 |
def select_all():
|
122 |
-
return a_base, a_base,
|
123 |
|
124 |
|
125 |
def deselect_all():
|
126 |
-
return [], [], []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
127 |
|
128 |
|
129 |
font = gr.themes.GoogleFont("NotoSans CJK")
|
130 |
-
theme = gr.themes.Soft(font=font)
|
131 |
|
132 |
with gr.Blocks(theme=theme, title="假名小測驗") as app:
|
133 |
correct = gr.State(0)
|
134 |
total = gr.State(0)
|
135 |
-
quiz_list = gr.State(
|
136 |
|
137 |
with gr.Row():
|
138 |
-
with gr.
|
139 |
-
with gr.Tab("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
with gr.Row():
|
141 |
desc = "完成設定後按下「開始測驗」"
|
142 |
question = gr.Textbox(placeholder=desc, label="題目", interactive=False)
|
143 |
score = gr.Textbox("0/0", label="分數")
|
144 |
answer = gr.Textbox(label="作答")
|
145 |
|
146 |
-
|
147 |
-
|
148 |
-
setting_hira = gr.CheckboxGroup(a_base, value=a_base[:5], label="平假名")
|
149 |
-
setting_kata = gr.CheckboxGroup(a_base, label="片假名")
|
150 |
-
setting_c = gr.CheckboxGroup(c_opt, label="延伸")
|
151 |
with gr.Row():
|
152 |
-
|
153 |
-
|
154 |
-
apply_btn = gr.Button("開始測驗")
|
155 |
|
156 |
-
|
157 |
-
|
|
|
|
|
|
|
|
|
|
|
158 |
chk_arg = dict(fn=check, inputs=chk_inn, outputs=chk_out, show_progress="hidden")
|
159 |
|
160 |
-
nq_inn = [setting_hira, setting_kata,
|
161 |
nq_out = [question, quiz_list]
|
162 |
nq_arg = dict(fn=next_question, inputs=nq_inn, outputs=nq_out, show_progress="hidden")
|
163 |
|
164 |
-
ini_inn = [setting_hira, setting_kata,
|
165 |
-
ini_out = [quiz_list]
|
166 |
ini_arg = dict(fn=init_question, inputs=ini_inn, outputs=ini_out, show_progress="hidden")
|
167 |
|
168 |
reset_arg = dict(fn=reset_score, outputs=chk_out, show_progress="hidden")
|
169 |
|
170 |
-
opt_out = [setting_hira, setting_kata, setting_c]
|
171 |
-
|
172 |
answer.submit(**chk_arg).then(**nq_arg)
|
173 |
apply_btn.click(**ini_arg).then(**reset_arg).then(**nq_arg)
|
174 |
-
|
175 |
-
|
|
|
|
|
|
|
|
|
176 |
|
177 |
app.launch(favicon_path="icon.png")
|
|
|
5 |
from romkan import to_hiragana, to_katakana, to_roma
|
6 |
|
7 |
a_base = ["a", "k", "s", "t", "n", "h", "m", "y", "r", "w", "nn"]
|
|
|
|
|
|
|
8 |
b_base = ["a", "i", "u", "e", "o"]
|
9 |
+
ext = ["g", "z", "d", "b", "p", "ya", "yo", "yu"]
|
|
|
|
|
10 |
|
11 |
invalid = ["yi", "ye", "wi", "wu", "we"]
|
12 |
spec_map = {"cya": "cha", "cyu": "chu", "cyo": "cho"}
|
13 |
|
14 |
|
15 |
+
def next_question(hira, hira_ext, kata, kata_ext, quiz_list: list):
|
|
|
|
|
16 |
if not quiz_list:
|
17 |
+
quiz_list = init_question_list(hira, hira_ext, kata, kata_ext)
|
|
|
18 |
|
19 |
if not quiz_list:
|
20 |
gr.Info("請選擇至少一項測驗!")
|
21 |
return None, quiz_list
|
22 |
|
|
|
|
|
|
|
23 |
return quiz_list.pop(), quiz_list
|
24 |
|
25 |
|
26 |
+
def init_question_list(hira, hira_ext, kata, kata_ext):
|
27 |
+
hira_ext_a, hira_ext_b = split_ext(hira_ext)
|
28 |
+
kata_ext_a, kata_ext_b = split_ext(kata_ext)
|
|
|
29 |
|
30 |
+
curr_hira_a = deepcopy(hira) + deepcopy(hira_ext_a)
|
31 |
+
curr_kata_a = deepcopy(kata) + deepcopy(kata_ext_a)
|
|
|
|
|
32 |
|
33 |
+
curr_hira_b = deepcopy(b_base) + deepcopy(hira_ext_b)
|
34 |
+
curr_kata_b = deepcopy(b_base) + deepcopy(kata_ext_b)
|
35 |
|
36 |
+
hira_list = [to_hira(a, b) for a in curr_hira_a for b in curr_hira_b if is_valid(a, b)]
|
37 |
+
kata_list = [to_kata(a, b) for a in curr_kata_a for b in curr_kata_b if is_valid(a, b)]
|
38 |
|
39 |
quiz_list = hira_list + kata_list
|
40 |
quiz_list = list(set(quiz_list))
|
|
|
43 |
return quiz_list
|
44 |
|
45 |
|
46 |
+
def split_ext(ext: list):
|
47 |
+
ext_a = [a for a in ext if a[0] != "y"]
|
48 |
+
ext_b = [b for b in ext if b[0] == "y"]
|
49 |
+
return ext_a, ext_b
|
50 |
+
|
51 |
+
|
52 |
def is_valid(aa: str, bb: str):
|
53 |
if f"{aa}{bb}" in invalid:
|
54 |
return False
|
|
|
61 |
if bb == "":
|
62 |
return False
|
63 |
|
64 |
+
if aa == "a" and bb[0] == "y":
|
65 |
+
return False
|
66 |
+
|
67 |
return True
|
68 |
|
69 |
|
|
|
83 |
return f"{a}{b}"
|
84 |
|
85 |
|
86 |
+
def check(kana: str, roma: str, correct, total, record, quiz_list):
|
87 |
roma = roma.lower()
|
88 |
|
89 |
kana_roma = to_roma(kana)
|
|
|
106 |
info = "正確" if ans_correct else f"錯誤 - {kana} ({kana_roma})"
|
107 |
msg = f"{correct}/{total} - " + info
|
108 |
|
109 |
+
if not ans_correct:
|
110 |
+
record = f"{record}{info}\n"
|
111 |
+
|
112 |
+
tab_idx = gr.Tabs(selected=1)
|
113 |
+
if not quiz_list:
|
114 |
+
record = f"{record}此輪得分 - {correct}/{total}\n\n"
|
115 |
+
correct, total = 0, 0
|
116 |
+
tab_idx = gr.Tabs(selected=2)
|
117 |
+
|
118 |
+
return correct, total, msg, None, record, tab_idx
|
119 |
|
120 |
|
121 |
def reset_score():
|
122 |
+
return 0, 0, "0/0", None, None, gr.Tabs()
|
123 |
|
124 |
|
125 |
def select_all():
|
126 |
+
return a_base, ext, a_base, ext
|
127 |
|
128 |
|
129 |
def deselect_all():
|
130 |
+
return [], [], [], []
|
131 |
+
|
132 |
+
|
133 |
+
def select_all_hira():
|
134 |
+
return a_base, ext, [], []
|
135 |
+
|
136 |
+
|
137 |
+
def select_all_kata():
|
138 |
+
return [], [], a_base, ext
|
139 |
+
|
140 |
+
|
141 |
+
def select_init():
|
142 |
+
return a_base[:5], [], [], []
|
143 |
+
|
144 |
+
|
145 |
+
def back_to_setting():
|
146 |
+
return gr.Tabs(selected=0)
|
147 |
|
148 |
|
149 |
font = gr.themes.GoogleFont("NotoSans CJK")
|
150 |
+
theme = gr.themes.Soft(font=font, text_size=gr.themes.sizes.text_lg)
|
151 |
|
152 |
with gr.Blocks(theme=theme, title="假名小測驗") as app:
|
153 |
correct = gr.State(0)
|
154 |
total = gr.State(0)
|
155 |
+
quiz_list = gr.State(init_question_list(*select_init()))
|
156 |
|
157 |
with gr.Row():
|
158 |
+
with gr.Tabs() as tabs:
|
159 |
+
with gr.Tab("設定", id=0):
|
160 |
+
with gr.Tab("平假名"):
|
161 |
+
setting_hira = gr.CheckboxGroup(a_base, value=a_base[:5], label="基本")
|
162 |
+
setting_hira_ext = gr.CheckboxGroup(ext, label="濁音、半濁音、拗音")
|
163 |
+
|
164 |
+
with gr.Tab("片假名"):
|
165 |
+
setting_kata = gr.CheckboxGroup(a_base, label="基本")
|
166 |
+
setting_kata_ext = gr.CheckboxGroup(ext, label="濁音、半濁音、拗音")
|
167 |
+
|
168 |
+
with gr.Row():
|
169 |
+
select_all_hira_btn = gr.Button("平假名全選")
|
170 |
+
select_all_kata_btn = gr.Button("片假名全選")
|
171 |
+
with gr.Row():
|
172 |
+
select_all_btn = gr.Button("全選")
|
173 |
+
deselect_all_btn = gr.Button("全不選")
|
174 |
+
with gr.Row():
|
175 |
+
apply_btn = gr.Button("開始測驗")
|
176 |
+
|
177 |
+
with gr.Tab("測驗", id=1):
|
178 |
with gr.Row():
|
179 |
desc = "完成設定後按下「開始測驗」"
|
180 |
question = gr.Textbox(placeholder=desc, label="題目", interactive=False)
|
181 |
score = gr.Textbox("0/0", label="分數")
|
182 |
answer = gr.Textbox(label="作答")
|
183 |
|
184 |
+
with gr.Tab("紀錄", id=2):
|
185 |
+
record = gr.TextArea(show_label=False)
|
|
|
|
|
|
|
186 |
with gr.Row():
|
187 |
+
again_btn = gr.Button("再次測驗")
|
188 |
+
back_to_setting_btn = gr.Button("回到設定")
|
|
|
189 |
|
190 |
+
def init_question(set_hira, set_hira_ext, set_kata, set_kata_ext):
|
191 |
+
quiz_list = init_question_list(set_hira, set_hira_ext, set_kata, set_kata_ext)
|
192 |
+
select = gr.Tabs(selected=1) if quiz_list else gr.Tabs(selected=0)
|
193 |
+
return quiz_list, select
|
194 |
+
|
195 |
+
chk_inn = [question, answer, correct, total, record, quiz_list]
|
196 |
+
chk_out = [correct, total, score, answer, record, tabs]
|
197 |
chk_arg = dict(fn=check, inputs=chk_inn, outputs=chk_out, show_progress="hidden")
|
198 |
|
199 |
+
nq_inn = [setting_hira, setting_hira_ext, setting_kata, setting_kata_ext, quiz_list]
|
200 |
nq_out = [question, quiz_list]
|
201 |
nq_arg = dict(fn=next_question, inputs=nq_inn, outputs=nq_out, show_progress="hidden")
|
202 |
|
203 |
+
ini_inn = [setting_hira, setting_hira_ext, setting_kata, setting_kata_ext]
|
204 |
+
ini_out = [quiz_list, tabs]
|
205 |
ini_arg = dict(fn=init_question, inputs=ini_inn, outputs=ini_out, show_progress="hidden")
|
206 |
|
207 |
reset_arg = dict(fn=reset_score, outputs=chk_out, show_progress="hidden")
|
208 |
|
|
|
|
|
209 |
answer.submit(**chk_arg).then(**nq_arg)
|
210 |
apply_btn.click(**ini_arg).then(**reset_arg).then(**nq_arg)
|
211 |
+
again_btn.click(**ini_arg).then(**reset_arg).then(**nq_arg)
|
212 |
+
select_all_hira_btn.click(select_all_hira, outputs=ini_inn, show_progress="hidden")
|
213 |
+
select_all_kata_btn.click(select_all_kata, outputs=ini_inn, show_progress="hidden")
|
214 |
+
select_all_btn.click(select_all, outputs=ini_inn, show_progress="hidden")
|
215 |
+
deselect_all_btn.click(deselect_all, outputs=ini_inn, show_progress="hidden")
|
216 |
+
back_to_setting_btn.click(back_to_setting, None, tabs, show_progress="hidden")
|
217 |
|
218 |
app.launch(favicon_path="icon.png")
|