21
21
if not save_dir .is_dir ():
22
22
save_dir .mkdir (parents = True )
23
23
24
- default_dict_path = root_dir / "default.csv"
25
- user_dict_path = save_dir / "user_dict.json"
26
- compiled_dict_path = save_dir / "user.dic"
24
+ default_dict_path = root_dir / "default.csv" # VOICEVOXデフォルト辞書ファイルのパス
25
+ user_dict_path = save_dir / "user_dict.json" # ユーザー辞書ファイルのパス
26
+ compiled_dict_path = save_dir / "user.dic" # コンパイル済み辞書ファイルのパス
27
27
28
28
29
+ # 同時書き込みの制御
29
30
mutex_user_dict = threading .Lock ()
30
31
mutex_openjtalk_dict = threading .Lock ()
31
32
32
33
33
34
@mutex_wrapper (mutex_user_dict )
34
- def write_to_json (user_dict : Dict [str , UserDictWord ], user_dict_path : Path ):
35
+ def _write_to_json (user_dict : Dict [str , UserDictWord ], user_dict_path : Path ) -> None :
36
+ """
37
+ ユーザー辞書ファイルへのユーザー辞書データ書き込み
38
+ Parameters
39
+ ----------
40
+ user_dict : Dict[str, UserDictWord]
41
+ ユーザー辞書データ
42
+ user_dict_path : Path
43
+ ユーザー辞書ファイルのパス
44
+ """
35
45
converted_user_dict = {}
36
46
for word_uuid , word in user_dict .items ():
37
47
word_dict = word .dict ()
38
- word_dict ["cost" ] = priority2cost (
48
+ word_dict ["cost" ] = _priority2cost (
39
49
word_dict ["context_id" ], word_dict ["priority" ]
40
50
)
41
51
del word_dict ["priority" ]
42
52
converted_user_dict [word_uuid ] = word_dict
43
53
# 予めjsonに変換できることを確かめる
44
54
user_dict_json = json .dumps (converted_user_dict , ensure_ascii = False )
55
+
56
+ # ユーザー辞書ファイルへの書き込み
45
57
user_dict_path .write_text (user_dict_json , encoding = "utf-8" )
46
58
47
59
@@ -50,21 +62,38 @@ def update_dict(
50
62
default_dict_path : Path = default_dict_path ,
51
63
user_dict_path : Path = user_dict_path ,
52
64
compiled_dict_path : Path = compiled_dict_path ,
53
- ):
65
+ ) -> None :
66
+ """
67
+ 辞書の更新
68
+ Parameters
69
+ ----------
70
+ default_dict_path : Path
71
+ デフォルト辞書ファイルのパス
72
+ user_dict_path : Path
73
+ ユーザー辞書ファイルのパス
74
+ compiled_dict_path : Path
75
+ コンパイル済み辞書ファイルのパス
76
+ """
54
77
random_string = uuid4 ()
55
- tmp_csv_path = save_dir / f".tmp.dict_csv-{ random_string } "
56
- tmp_compiled_path = save_dir / f".tmp.dict_compiled-{ random_string } "
78
+ tmp_csv_path = save_dir / f".tmp.dict_csv-{ random_string } " # csv形式辞書データの一時保存ファイル
79
+ tmp_compiled_path = (
80
+ save_dir / f".tmp.dict_compiled-{ random_string } "
81
+ ) # コンパイル済み辞書データの一時保存ファイル
57
82
58
83
try :
59
84
# 辞書.csvを作成
60
85
csv_text = ""
86
+
87
+ # デフォルト辞書データの追加
61
88
if not default_dict_path .is_file ():
62
89
print ("Warning: Cannot find default dictionary." , file = sys .stderr )
63
90
return
64
91
default_dict = default_dict_path .read_text (encoding = "utf-8" )
65
92
if default_dict == default_dict .rstrip ():
66
93
default_dict += "\n "
67
94
csv_text += default_dict
95
+
96
+ # ユーザー辞書データの追加
68
97
user_dict = read_dict (user_dict_path = user_dict_path )
69
98
for word_uuid in user_dict :
70
99
word = user_dict [word_uuid ]
@@ -77,7 +106,7 @@ def update_dict(
77
106
).format (
78
107
surface = word .surface ,
79
108
context_id = word .context_id ,
80
- cost = priority2cost (word .context_id , word .priority ),
109
+ cost = _priority2cost (word .context_id , word .priority ),
81
110
part_of_speech = word .part_of_speech ,
82
111
part_of_speech_detail_1 = word .part_of_speech_detail_1 ,
83
112
part_of_speech_detail_2 = word .part_of_speech_detail_2 ,
@@ -91,6 +120,7 @@ def update_dict(
91
120
mora_count = word .mora_count ,
92
121
accent_associative_rule = word .accent_associative_rule ,
93
122
)
123
+ # 辞書データを辞書.csv へ一時保存
94
124
tmp_csv_path .write_text (csv_text , encoding = "utf-8" )
95
125
96
126
# 辞書.csvをOpenJTalk用にコンパイル
@@ -119,10 +149,23 @@ def update_dict(
119
149
120
150
@mutex_wrapper (mutex_user_dict )
121
151
def read_dict (user_dict_path : Path = user_dict_path ) -> Dict [str , UserDictWord ]:
152
+ """
153
+ ユーザー辞書の読み出し
154
+ Parameters
155
+ ----------
156
+ user_dict_path : Path
157
+ ユーザー辞書ファイルのパス
158
+ Returns
159
+ -------
160
+ result : Dict[str, UserDictWord]
161
+ ユーザー辞書
162
+ """
163
+ # 指定ユーザー辞書が存在しない場合、空辞書を返す
122
164
if not user_dict_path .is_file ():
123
165
return {}
166
+
124
167
with user_dict_path .open (encoding = "utf-8" ) as f :
125
- result = {}
168
+ result : Dict [ str , UserDictWord ] = {}
126
169
for word_uuid , word in json .load (f ).items ():
127
170
# cost2priorityで変換を行う際にcontext_idが必要となるが、
128
171
# 0.12以前の辞書は、context_idがハードコーディングされていたためにユーザー辞書内に保管されていない
@@ -131,20 +174,39 @@ def read_dict(user_dict_path: Path = user_dict_path) -> Dict[str, UserDictWord]:
131
174
word ["context_id" ] = part_of_speech_data [
132
175
WordTypes .PROPER_NOUN
133
176
].context_id
134
- word ["priority" ] = cost2priority (word ["context_id" ], word ["cost" ])
177
+ word ["priority" ] = _cost2priority (word ["context_id" ], word ["cost" ])
135
178
del word ["cost" ]
136
179
result [str (UUID (word_uuid ))] = UserDictWord (** word )
137
180
138
181
return result
139
182
140
183
141
- def create_word (
184
+ def _create_word (
142
185
surface : str ,
143
186
pronunciation : str ,
144
187
accent_type : int ,
145
188
word_type : Optional [WordTypes ] = None ,
146
189
priority : Optional [int ] = None ,
147
190
) -> UserDictWord :
191
+ """
192
+ 単語オブジェクトの生成
193
+ Parameters
194
+ ----------
195
+ surface : str
196
+ 単語情報
197
+ pronunciation : str
198
+ 単語情報
199
+ accent_type : int
200
+ 単語情報
201
+ word_type : Optional[WordTypes]
202
+ 品詞
203
+ priority : Optional[int]
204
+ 優先度
205
+ Returns
206
+ -------
207
+ : UserDictWord
208
+ 単語オブジェクト
209
+ """
148
210
if word_type is None :
149
211
word_type = WordTypes .PROPER_NOUN
150
212
if word_type not in part_of_speech_data .keys ():
@@ -181,7 +243,31 @@ def apply_word(
181
243
user_dict_path : Path = user_dict_path ,
182
244
compiled_dict_path : Path = compiled_dict_path ,
183
245
) -> str :
184
- word = create_word (
246
+ """
247
+ 新規単語の追加
248
+ Parameters
249
+ ----------
250
+ surface : str
251
+ 単語情報
252
+ pronunciation : str
253
+ 単語情報
254
+ accent_type : int
255
+ 単語情報
256
+ word_type : Optional[WordTypes]
257
+ 品詞
258
+ priority : Optional[int]
259
+ 優先度
260
+ user_dict_path : Path
261
+ ユーザー辞書ファイルのパス
262
+ compiled_dict_path : Path
263
+ コンパイル済み辞書ファイルのパス
264
+ Returns
265
+ -------
266
+ word_uuid : UserDictWord
267
+ 追加された単語に発行されたUUID
268
+ """
269
+ # 新規単語の追加による辞書データの更新
270
+ word = _create_word (
185
271
surface = surface ,
186
272
pronunciation = pronunciation ,
187
273
accent_type = accent_type ,
@@ -191,8 +277,11 @@ def apply_word(
191
277
user_dict = read_dict (user_dict_path = user_dict_path )
192
278
word_uuid = str (uuid4 ())
193
279
user_dict [word_uuid ] = word
194
- write_to_json (user_dict , user_dict_path )
280
+
281
+ # 更新された辞書データの保存と適用
282
+ _write_to_json (user_dict , user_dict_path )
195
283
update_dict (user_dict_path = user_dict_path , compiled_dict_path = compiled_dict_path )
284
+
196
285
return word_uuid
197
286
198
287
@@ -205,32 +294,71 @@ def rewrite_word(
205
294
priority : Optional [int ] = None ,
206
295
user_dict_path : Path = user_dict_path ,
207
296
compiled_dict_path : Path = compiled_dict_path ,
208
- ):
209
- word = create_word (
297
+ ) -> None :
298
+ """
299
+ 既存単語の上書き更新
300
+ Parameters
301
+ ----------
302
+ word_uuid : str
303
+ 単語UUID
304
+ surface : str
305
+ 単語情報
306
+ pronunciation : str
307
+ 単語情報
308
+ accent_type : int
309
+ 単語情報
310
+ word_type : Optional[WordTypes]
311
+ 品詞
312
+ priority : Optional[int]
313
+ 優先度
314
+ user_dict_path : Path
315
+ ユーザー辞書ファイルのパス
316
+ compiled_dict_path : Path
317
+ コンパイル済み辞書ファイルのパス
318
+ """
319
+ word = _create_word (
210
320
surface = surface ,
211
321
pronunciation = pronunciation ,
212
322
accent_type = accent_type ,
213
323
word_type = word_type ,
214
324
priority = priority ,
215
325
)
326
+
327
+ # 既存単語の上書きによる辞書データの更新
216
328
user_dict = read_dict (user_dict_path = user_dict_path )
217
329
if word_uuid not in user_dict :
218
330
raise HTTPException (status_code = 422 , detail = "UUIDに該当するワードが見つかりませんでした" )
219
331
user_dict [word_uuid ] = word
220
- write_to_json (user_dict , user_dict_path )
332
+
333
+ # 更新された辞書データの保存と適用
334
+ _write_to_json (user_dict , user_dict_path )
221
335
update_dict (user_dict_path = user_dict_path , compiled_dict_path = compiled_dict_path )
222
336
223
337
224
338
def delete_word (
225
339
word_uuid : str ,
226
340
user_dict_path : Path = user_dict_path ,
227
341
compiled_dict_path : Path = compiled_dict_path ,
228
- ):
342
+ ) -> None :
343
+ """
344
+ 単語の削除
345
+ Parameters
346
+ ----------
347
+ word_uuid : str
348
+ 単語UUID
349
+ user_dict_path : Path
350
+ ユーザー辞書ファイルのパス
351
+ compiled_dict_path : Path
352
+ コンパイル済み辞書ファイルのパス
353
+ """
354
+ # 既存単語の削除による辞書データの更新
229
355
user_dict = read_dict (user_dict_path = user_dict_path )
230
356
if word_uuid not in user_dict :
231
357
raise HTTPException (status_code = 422 , detail = "IDに該当するワードが見つかりませんでした" )
232
358
del user_dict [word_uuid ]
233
- write_to_json (user_dict , user_dict_path )
359
+
360
+ # 更新された辞書データの保存と適用
361
+ _write_to_json (user_dict , user_dict_path )
234
362
update_dict (user_dict_path = user_dict_path , compiled_dict_path = compiled_dict_path )
235
363
236
364
@@ -240,8 +368,23 @@ def import_user_dict(
240
368
user_dict_path : Path = user_dict_path ,
241
369
default_dict_path : Path = default_dict_path ,
242
370
compiled_dict_path : Path = compiled_dict_path ,
243
- ):
244
- # 念のため型チェックを行う
371
+ ) -> None :
372
+ """
373
+ ユーザー辞書のインポート
374
+ Parameters
375
+ ----------
376
+ dict_data : Dict[str, UserDictWord]
377
+ インポートするユーザー辞書のデータ
378
+ override : bool
379
+ 重複したエントリがあった場合、上書きするかどうか
380
+ user_dict_path : Path
381
+ ユーザー辞書ファイルのパス
382
+ default_dict_path : Path
383
+ デフォルト辞書ファイルのパス
384
+ compiled_dict_path : Path
385
+ コンパイル済み辞書ファイルのパス
386
+ """
387
+ # インポートする辞書データのバリデーション
245
388
for word_uuid , word in dict_data .items ():
246
389
UUID (word_uuid )
247
390
assert isinstance (word , UserDictWord )
@@ -263,36 +406,44 @@ def import_user_dict(
263
406
break
264
407
else :
265
408
raise ValueError ("対応していない品詞です" )
409
+
410
+ # 既存辞書の読み出し
266
411
old_dict = read_dict (user_dict_path = user_dict_path )
412
+
413
+ # 辞書データの更新
414
+ # 重複エントリの上書き
267
415
if override :
268
416
new_dict = {** old_dict , ** dict_data }
417
+ # 重複エントリの保持
269
418
else :
270
419
new_dict = {** dict_data , ** old_dict }
271
- write_to_json (user_dict = new_dict , user_dict_path = user_dict_path )
420
+
421
+ # 更新された辞書データの保存と適用
422
+ _write_to_json (user_dict = new_dict , user_dict_path = user_dict_path )
272
423
update_dict (
273
424
default_dict_path = default_dict_path ,
274
425
user_dict_path = user_dict_path ,
275
426
compiled_dict_path = compiled_dict_path ,
276
427
)
277
428
278
429
279
- def search_cost_candidates (context_id : int ) -> List [int ]:
430
+ def _search_cost_candidates (context_id : int ) -> List [int ]:
280
431
for value in part_of_speech_data .values ():
281
432
if value .context_id == context_id :
282
433
return value .cost_candidates
283
434
raise HTTPException (status_code = 422 , detail = "品詞IDが不正です" )
284
435
285
436
286
- def cost2priority (context_id : int , cost : conint (ge = - 32768 , le = 32767 )) -> int :
287
- cost_candidates = search_cost_candidates (context_id )
437
+ def _cost2priority (context_id : int , cost : conint (ge = - 32768 , le = 32767 )) -> int :
438
+ cost_candidates = _search_cost_candidates (context_id )
288
439
# cost_candidatesの中にある値で最も近い値を元にpriorityを返す
289
440
# 参考: https://qiita.com/Krypf/items/2eada91c37161d17621d
290
441
# この関数とpriority2cost関数によって、辞書ファイルのcostを操作しても最も近いpriorityのcostに上書きされる
291
442
return MAX_PRIORITY - np .argmin (np .abs (np .array (cost_candidates ) - cost ))
292
443
293
444
294
- def priority2cost (
445
+ def _priority2cost (
295
446
context_id : int , priority : conint (ge = MIN_PRIORITY , le = MAX_PRIORITY )
296
447
) -> int :
297
- cost_candidates = search_cost_candidates (context_id )
448
+ cost_candidates = _search_cost_candidates (context_id )
298
449
return cost_candidates [MAX_PRIORITY - priority ]
0 commit comments