jwf-common-python 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jwf_common_python/__init__.py +14 -0
- jwf_common_python/db_utils/__init__.py +6 -0
- jwf_common_python/db_utils/clickhouse.py +124 -0
- jwf_common_python/db_utils/elasticsearch.py +196 -0
- jwf_common_python/db_utils/mysql.py +64 -0
- jwf_common_python/eudic/__init__.py +5 -0
- jwf_common_python/eudic/wordbook.py +211 -0
- jwf_common_python/file_storage/__init__.py +2 -0
- jwf_common_python/file_storage/file_interface.py +29 -0
- jwf_common_python/file_storage/local.py +50 -0
- jwf_common_python/file_storage/tencent_cos.py +65 -0
- jwf_common_python/geography/__init__.py +8 -0
- jwf_common_python/geography/district_amap.py +181 -0
- jwf_common_python/geography/search_amap.py +84 -0
- jwf_common_python/github/__init__.py +5 -0
- jwf_common_python/github/webhooks.py +87 -0
- jwf_common_python/language_utils/__init__.py +34 -0
- jwf_common_python/language_utils/core.py +203 -0
- jwf_common_python/language_utils/files.py +52 -0
- jwf_common_python/language_utils/web.py +117 -0
- jwf_common_python/llm/__init__.py +1 -0
- jwf_common_python/llm/aliyun_qwen.py +10 -0
- jwf_common_python/llm/azure_gpt.py +66 -0
- jwf_common_python/llm/baidu_ernie.py +112 -0
- jwf_common_python/message_queue/__init__.py +7 -0
- jwf_common_python/message_queue/interface.py +49 -0
- jwf_common_python/message_queue/kafka.py +145 -0
- jwf_common_python/message_queue/rabbitmq.py +113 -0
- jwf_common_python/oauth2/__init__.py +0 -0
- jwf_common_python/oauth2/google.py +158 -0
- jwf_common_python/parse_subtitle/__init__.py +6 -0
- jwf_common_python/parse_subtitle/aa.vtt +2364 -0
- jwf_common_python/parse_subtitle/parse_srt_file.py +60 -0
- jwf_common_python/pdf/__init__.py +0 -0
- jwf_common_python/pdf/aaa.py +38 -0
- jwf_common_python/pdf/local_parse.py +259 -0
- jwf_common_python/sms/__init__.py +1 -0
- jwf_common_python/sms/interface.py +7 -0
- jwf_common_python/sms/tencent_sms.py +117 -0
- jwf_common_python/translate/__init__.py +152 -0
- jwf_common_python/translate/_youdao.py +234 -0
- jwf_common_python/translate/microsoft.py +696 -0
- jwf_common_python/translate/tencent.py +194 -0
- jwf_common_python/tts/__init__.py +0 -0
- jwf_common_python/tts/microsoft.py +87 -0
- jwf_common_python/utils/__init__.py +0 -0
- jwf_common_python/utils/image_utils.py +18 -0
- jwf_common_python/utils/tencent_utils.py +51 -0
- jwf_common_python/utils/utils.py +46 -0
- jwf_common_python/video/__init__.py +5 -0
- jwf_common_python/video/merge.py +276 -0
- jwf_common_python/weather/__init__.py +0 -0
- jwf_common_python/weather/amap.py +127 -0
- jwf_common_python/weather/wanwei.py +58 -0
- jwf_common_python/youdao/__init__.py +13 -0
- jwf_common_python/youdao/wordbook.py +88 -0
- jwf_common_python-1.0.0.dist-info/METADATA +669 -0
- jwf_common_python-1.0.0.dist-info/RECORD +60 -0
- jwf_common_python-1.0.0.dist-info/WHEEL +4 -0
- jwf_common_python-1.0.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from jwf_common_python.translate import LanguageCodeOfBackend, SentenceInfo, WordInfo
|
|
6
|
+
from jwf_common_python.tts.microsoft import TTSMicrosoft
|
|
7
|
+
|
|
8
|
+
pos_tag_table = {
|
|
9
|
+
"ADJ": ("Adjectives", "形容词"),
|
|
10
|
+
"ADV": ("Adverbs", "副词"),
|
|
11
|
+
"CONJ": ("Conjunctions", "连词"),
|
|
12
|
+
"DET": ("Determiners", "限定词"),
|
|
13
|
+
"MODAL": ("Verbs", "动词"),
|
|
14
|
+
"NOUN": ("Nouns", "名词"),
|
|
15
|
+
"PREP": ("Prepositions", "介词"),
|
|
16
|
+
"PRON": ("Pronouns", "代词"),
|
|
17
|
+
"VERB": ("Verbs", "动词"),
|
|
18
|
+
"OTHER": ("Other", "其他"),
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TranslateMicrosoft:
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
text_endpoint: str,
|
|
26
|
+
word_endpoint: str,
|
|
27
|
+
word_sample_endpoint: str,
|
|
28
|
+
location_region: str,
|
|
29
|
+
key: str,
|
|
30
|
+
tts_microsoft_config: dict,
|
|
31
|
+
):
|
|
32
|
+
self.text_endpoint = text_endpoint
|
|
33
|
+
self.word_endpoint = word_endpoint
|
|
34
|
+
self.word_samples_endpoint = word_sample_endpoint
|
|
35
|
+
self.location_region = location_region
|
|
36
|
+
self.key = key
|
|
37
|
+
# 这个由配置文件传入
|
|
38
|
+
# self.part_params = {"api-version": "3.0"}
|
|
39
|
+
self.part_params = {}
|
|
40
|
+
self.tts_microsoft_obj = TTSMicrosoft(**tts_microsoft_config)
|
|
41
|
+
|
|
42
|
+
def translate_word(
|
|
43
|
+
self,
|
|
44
|
+
word: str,
|
|
45
|
+
source_language_code: LanguageCodeOfBackend,
|
|
46
|
+
target_language_code: LanguageCodeOfBackend,
|
|
47
|
+
) -> dict:
|
|
48
|
+
"""翻译word,使用字典接口查询"""
|
|
49
|
+
# 1. 获取sentence原生响应
|
|
50
|
+
raw_response: dict = self._send_word_request(
|
|
51
|
+
word, source_language_code, target_language_code
|
|
52
|
+
)
|
|
53
|
+
normalized_word = raw_response["normalizedSource"]
|
|
54
|
+
|
|
55
|
+
# 2. 获取word的tts结果
|
|
56
|
+
word_audio: bytes = self._send_tts_request(normalized_word, None)
|
|
57
|
+
# 补充word单词发音
|
|
58
|
+
raw_response["audio_data"] = word_audio
|
|
59
|
+
|
|
60
|
+
# 3. 获取word翻译的所有例句,返回一个新的翻译结果
|
|
61
|
+
new_translations = self._get_word_translation_all_examples(
|
|
62
|
+
raw_response, source_language_code, target_language_code
|
|
63
|
+
)
|
|
64
|
+
# 替换原始翻译结果
|
|
65
|
+
raw_response["translations"] = new_translations
|
|
66
|
+
|
|
67
|
+
# 返回最终结果
|
|
68
|
+
return raw_response
|
|
69
|
+
|
|
70
|
+
def translate_sentence(
|
|
71
|
+
self,
|
|
72
|
+
text: str,
|
|
73
|
+
source_language_code: LanguageCodeOfBackend,
|
|
74
|
+
target_language_code: LanguageCodeOfBackend,
|
|
75
|
+
) -> dict:
|
|
76
|
+
"""翻译句子,使用文本翻译接口查询
|
|
77
|
+
返回:
|
|
78
|
+
{
|
|
79
|
+
"text":"xxx",
|
|
80
|
+
"to":"xxx",
|
|
81
|
+
"audio_data": bytes
|
|
82
|
+
"sentence": "xxx"
|
|
83
|
+
}
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
# 1. 获取sentence原生响应
|
|
87
|
+
# text 是翻译后的文本, to 是目标语言代码
|
|
88
|
+
raw_result: dict = self._send_sentence_request(
|
|
89
|
+
text, source_language_code, target_language_code
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# 2.1 获取text的tts结果
|
|
93
|
+
text_audio: bytes = self._send_tts_request(text, None)
|
|
94
|
+
# 2.2 增加text的发音
|
|
95
|
+
raw_result["audio_data"] = text_audio
|
|
96
|
+
|
|
97
|
+
# 3. 增加原始文本
|
|
98
|
+
raw_result["sentence"] = text
|
|
99
|
+
|
|
100
|
+
return raw_result
|
|
101
|
+
|
|
102
|
+
# ############################ [类方法]格式化响应结果 ############################
|
|
103
|
+
@classmethod
|
|
104
|
+
def format_word_response(cls, response: dict) -> WordInfo:
|
|
105
|
+
"""将相应结果处理为了WordInfo
|
|
106
|
+
raw_response: 单词的补充处理之后的翻译结果,来自 translate_word 方法返回值
|
|
107
|
+
"""
|
|
108
|
+
normalized_word = response["normalizedSource"]
|
|
109
|
+
display_word = response["displaySource"]
|
|
110
|
+
|
|
111
|
+
# 所有变体.
|
|
112
|
+
_ = {
|
|
113
|
+
"normalizedText": "fly",
|
|
114
|
+
"displayText": "fly",
|
|
115
|
+
"numExamples": 15,
|
|
116
|
+
"frequencyCount": 4995,
|
|
117
|
+
}
|
|
118
|
+
raw_shapes: list[dict] = []
|
|
119
|
+
|
|
120
|
+
# 翻译结果
|
|
121
|
+
translations: list[dict] = []
|
|
122
|
+
|
|
123
|
+
all_raw_translations: list[dict] = response["translations"]
|
|
124
|
+
for raw_translation in all_raw_translations:
|
|
125
|
+
# 添加翻译
|
|
126
|
+
item = {
|
|
127
|
+
"normalized": raw_translation["normalizedTarget"],
|
|
128
|
+
"display": raw_translation["displayTarget"],
|
|
129
|
+
"pos_tag": cls._change_pos_tag(raw_translation["posTag"]),
|
|
130
|
+
"frequency": cls._change_frequency(raw_translation["confidence"]),
|
|
131
|
+
"examples": raw_translation["examples"],
|
|
132
|
+
}
|
|
133
|
+
translations.append(item)
|
|
134
|
+
|
|
135
|
+
# 其他变体
|
|
136
|
+
shape = raw_translation["backTranslations"]
|
|
137
|
+
raw_shapes.extend(shape)
|
|
138
|
+
|
|
139
|
+
# 单词其他形式.变体排序
|
|
140
|
+
sorted_shapes = cls._sort_shapes(raw_shapes)
|
|
141
|
+
|
|
142
|
+
res = {
|
|
143
|
+
"normalized_word": normalized_word,
|
|
144
|
+
"display_word": display_word,
|
|
145
|
+
"translations": translations,
|
|
146
|
+
"shapes": sorted_shapes,
|
|
147
|
+
"audio_data": response["audio_data"],
|
|
148
|
+
"extended_info": {},
|
|
149
|
+
}
|
|
150
|
+
return res
|
|
151
|
+
|
|
152
|
+
@classmethod
|
|
153
|
+
def format_sentence_response(cls, response: dict) -> SentenceInfo:
|
|
154
|
+
"""将相应结果处理为了SentenceInfo"""
|
|
155
|
+
sentence = response["sentence"]
|
|
156
|
+
translation = [response["text"]]
|
|
157
|
+
audio_data = response["audio_data"]
|
|
158
|
+
|
|
159
|
+
res = {
|
|
160
|
+
"sentence": sentence,
|
|
161
|
+
"translations": translation,
|
|
162
|
+
"audio_data": audio_data,
|
|
163
|
+
}
|
|
164
|
+
return res
|
|
165
|
+
|
|
166
|
+
# ############################ [私有方法] 发送word请求 ############################
|
|
167
|
+
def _send_word_request(
|
|
168
|
+
self,
|
|
169
|
+
word: str,
|
|
170
|
+
source_language_code: LanguageCodeOfBackend,
|
|
171
|
+
target_language_code: LanguageCodeOfBackend,
|
|
172
|
+
) -> dict:
|
|
173
|
+
"""发送单词翻译请求
|
|
174
|
+
[文档](https://learn.microsoft.com/en-us/azure/ai-services/translator/reference/v3-0-dictionary-lookup)
|
|
175
|
+
[词典示例](https://learn.microsoft.com/en-us/azure/ai-services/translator/reference/v3-0-dictionary-examples)
|
|
176
|
+
"""
|
|
177
|
+
# 1. 转换语言代码
|
|
178
|
+
source_language_code = self._change_language_code(source_language_code)
|
|
179
|
+
target_language_code = self._change_language_code(target_language_code)
|
|
180
|
+
|
|
181
|
+
# 2. 构造请求查询参数: 源语言,目标语言
|
|
182
|
+
params = self.part_params | {
|
|
183
|
+
"from": source_language_code,
|
|
184
|
+
"to": target_language_code,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# 3.1 获取 access_token
|
|
188
|
+
access_token = self.tts_microsoft_obj.fetch_access_token()
|
|
189
|
+
|
|
190
|
+
# 3. 构造请求头: key,location,content-type
|
|
191
|
+
headers = {
|
|
192
|
+
"Authorization": f"Bearer {access_token}",
|
|
193
|
+
"Ocp-Apim-Subscription-Key": self.key,
|
|
194
|
+
"Ocp-Apim-Subscription-Region": self.location_region,
|
|
195
|
+
"Content-type": "application/json",
|
|
196
|
+
"X-ClientTraceId": str(uuid.uuid4()),
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# 4. 构造请求体: 要翻译的word
|
|
200
|
+
body = [{"Text": word}]
|
|
201
|
+
|
|
202
|
+
# 5. 发送请求
|
|
203
|
+
try:
|
|
204
|
+
response = requests.post(
|
|
205
|
+
self.word_endpoint, params=params, headers=headers, json=body
|
|
206
|
+
)
|
|
207
|
+
except Exception as e:
|
|
208
|
+
_ = f"microsoft 词典翻译请求连接失败: {self.text_endpoint} {params} {headers} {body}"
|
|
209
|
+
raise ValueError(_ + e)
|
|
210
|
+
if response.status_code != 200:
|
|
211
|
+
raise ValueError(
|
|
212
|
+
f"microsoft 词典翻译响应状态: {response.status_code}; 响应具体错误: {response.text};\
|
|
213
|
+
请求参数: {headers} {body}; 实际请求url: {response.url}"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# 6.返回原生响应
|
|
217
|
+
response: list[dict] = response.json()
|
|
218
|
+
|
|
219
|
+
# 7. 断言必须只有一个翻译结果
|
|
220
|
+
assert len(response) == 1, f"microsoft词典翻译响应格式错误: {response}"
|
|
221
|
+
# 返回的结果
|
|
222
|
+
res = response[0]
|
|
223
|
+
_ = {
|
|
224
|
+
# 直接翻译的结果
|
|
225
|
+
"normalizedSource": "fly",
|
|
226
|
+
# 润色之后的结果
|
|
227
|
+
"displaySource": "fly",
|
|
228
|
+
"translations": [
|
|
229
|
+
{
|
|
230
|
+
# 相当于直接翻译的结果
|
|
231
|
+
"normalizedTarget": "飞",
|
|
232
|
+
# 润色之后的
|
|
233
|
+
# 以最适合最终用户显示的形式提供源术语
|
|
234
|
+
"displayTarget": "飞",
|
|
235
|
+
"posTag": "VERB",
|
|
236
|
+
# 一个介于 0.0 和 1.0 之间的值,“训练数据中的概率”
|
|
237
|
+
# 翻译的词频。保留1位,四舍五入进位。
|
|
238
|
+
# round(n*100,1)
|
|
239
|
+
"confidence": 0.2898,
|
|
240
|
+
"prefixWord": "",
|
|
241
|
+
# 单词的其他
|
|
242
|
+
"backTranslations": [
|
|
243
|
+
{
|
|
244
|
+
"normalizedText": "fly",
|
|
245
|
+
"displayText": "fly",
|
|
246
|
+
"numExamples": 15,
|
|
247
|
+
"frequencyCount": 4995,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
"normalizedText": "flying",
|
|
251
|
+
"displayText": "flying",
|
|
252
|
+
"numExamples": 15,
|
|
253
|
+
"frequencyCount": 1931,
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
"normalizedText": "flew",
|
|
257
|
+
"displayText": "flew",
|
|
258
|
+
"numExamples": 15,
|
|
259
|
+
"frequencyCount": 968,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
}
|
|
265
|
+
return res
|
|
266
|
+
|
|
267
|
+
def _send_word_examples_request(
|
|
268
|
+
self,
|
|
269
|
+
normalized_word: str,
|
|
270
|
+
normalized_translation: str,
|
|
271
|
+
source_language_code: LanguageCodeOfBackend,
|
|
272
|
+
target_language_code: LanguageCodeOfBackend,
|
|
273
|
+
) -> list[dict]:
|
|
274
|
+
"""获取单词的例句
|
|
275
|
+
normalized_word: 单词的标准化形式
|
|
276
|
+
normalized_translation: 单词翻译的标准化形式
|
|
277
|
+
"""
|
|
278
|
+
# 1. 转换语言代码
|
|
279
|
+
source_language_code = self._change_language_code(source_language_code)
|
|
280
|
+
target_language_code = self._change_language_code(target_language_code)
|
|
281
|
+
|
|
282
|
+
# 2. 构造请求查询参数: 源语言,目标语言
|
|
283
|
+
params = self.part_params | {
|
|
284
|
+
"from": source_language_code,
|
|
285
|
+
"to": target_language_code,
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
# 3.1 获取 access_token
|
|
289
|
+
access_token = self.tts_microsoft_obj.fetch_access_token()
|
|
290
|
+
|
|
291
|
+
# 3. 构造请求头: key,location,content-type
|
|
292
|
+
headers = {
|
|
293
|
+
"Authorization": f"Bearer {access_token}",
|
|
294
|
+
"Ocp-Apim-Subscription-Key": self.key,
|
|
295
|
+
"Ocp-Apim-Subscription-Region": self.location_region,
|
|
296
|
+
"Content-type": "application/json",
|
|
297
|
+
"X-ClientTraceId": str(uuid.uuid4()),
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# 4. 构造请求体: 要翻译的word
|
|
301
|
+
body = [{"Text": normalized_word, "Translation": normalized_translation}]
|
|
302
|
+
|
|
303
|
+
# 5. 发送请求
|
|
304
|
+
try:
|
|
305
|
+
response = requests.post(
|
|
306
|
+
self.word_samples_endpoint, params=params, headers=headers, json=body
|
|
307
|
+
)
|
|
308
|
+
except Exception as e:
|
|
309
|
+
_ = f"microsoft 词典示例请求连接失败: {self.text_endpoint} {params} {headers} {body}"
|
|
310
|
+
raise ValueError(_ + e)
|
|
311
|
+
if response.status_code != 200:
|
|
312
|
+
raise ValueError(
|
|
313
|
+
f"microsoft 词典示例响应状态: {response.status_code}; 响应具体错误: {response.text};\
|
|
314
|
+
请求参数: {headers} {body};\
|
|
315
|
+
实际请求url: {response.url}"
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
# 6.返回原生响应
|
|
319
|
+
response: list[dict] = response.json()
|
|
320
|
+
|
|
321
|
+
# 7. 断言必须只有一个翻译结果
|
|
322
|
+
assert len(response) == 1, f"microsoft词典翻译响应格式错误: {response}"
|
|
323
|
+
# 返回的结果
|
|
324
|
+
_ = [
|
|
325
|
+
{
|
|
326
|
+
"normalizedSource": "fly",
|
|
327
|
+
"normalizedTarget": "乘 飞机",
|
|
328
|
+
"examples": [
|
|
329
|
+
{
|
|
330
|
+
"sourcePrefix": "Tomorrow we're going to ",
|
|
331
|
+
"sourceTerm": "fly",
|
|
332
|
+
"sourceSuffix": " home.",
|
|
333
|
+
"targetPrefix": "明天我们将",
|
|
334
|
+
"targetTerm": "乘飞机",
|
|
335
|
+
"targetSuffix": "回家",
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"sourcePrefix": "Tomorrow we're going to ",
|
|
339
|
+
"sourceTerm": "fly",
|
|
340
|
+
"sourceSuffix": " home.",
|
|
341
|
+
"targetPrefix": "明天我们就",
|
|
342
|
+
"targetTerm": "乘飞机",
|
|
343
|
+
"targetSuffix": "回家了。",
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"sourcePrefix": "No students ",
|
|
347
|
+
"sourceTerm": "fly",
|
|
348
|
+
"sourceSuffix": " to school.",
|
|
349
|
+
"targetPrefix": "没有学生",
|
|
350
|
+
"targetTerm": "乘飞机",
|
|
351
|
+
"targetSuffix": "上学。",
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
}
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
# 获取示例响应
|
|
358
|
+
sample_response: dict = response[0]
|
|
359
|
+
assert (
|
|
360
|
+
sample_response["normalizedSource"] == normalized_word
|
|
361
|
+
), f"microsoft词典示例响应格式错误: {sample_response}"
|
|
362
|
+
assert (
|
|
363
|
+
sample_response["normalizedTarget"] == normalized_translation
|
|
364
|
+
), f"microsoft词典示例响应格式错误: {sample_response}"
|
|
365
|
+
|
|
366
|
+
# 获取示例结果
|
|
367
|
+
examples: list[dict] = sample_response["examples"]
|
|
368
|
+
_ = (
|
|
369
|
+
[
|
|
370
|
+
{
|
|
371
|
+
"sourcePrefix": "Tomorrow we're going to ",
|
|
372
|
+
"sourceTerm": "fly",
|
|
373
|
+
"sourceSuffix": " home.",
|
|
374
|
+
"targetPrefix": "明天我们将",
|
|
375
|
+
"targetTerm": "乘飞机",
|
|
376
|
+
"targetSuffix": "回家",
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"sourcePrefix": "Tomorrow we're going to ",
|
|
380
|
+
"sourceTerm": "fly",
|
|
381
|
+
"sourceSuffix": " home.",
|
|
382
|
+
"targetPrefix": "明天我们就",
|
|
383
|
+
"targetTerm": "乘飞机",
|
|
384
|
+
"targetSuffix": "回家了。",
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"sourcePrefix": "No students ",
|
|
388
|
+
"sourceTerm": "fly",
|
|
389
|
+
"sourceSuffix": " to school.",
|
|
390
|
+
"targetPrefix": "没有学生",
|
|
391
|
+
"targetTerm": "乘飞机",
|
|
392
|
+
"targetSuffix": "上学。",
|
|
393
|
+
},
|
|
394
|
+
],
|
|
395
|
+
)
|
|
396
|
+
return examples
|
|
397
|
+
|
|
398
|
+
def _unique_word_examples(self, examples_info: list[dict]) -> list[dict]:
|
|
399
|
+
"""将重复的例句进行合并,比如同一个句子有多个翻译的情况
|
|
400
|
+
examples_info 是_send_word_examples_request方法的返回结果
|
|
401
|
+
"""
|
|
402
|
+
_ = [
|
|
403
|
+
{
|
|
404
|
+
"sourcePrefix": "Tomorrow we're going to ",
|
|
405
|
+
"sourceTerm": "fly",
|
|
406
|
+
"sourceSuffix": " home.",
|
|
407
|
+
"targetPrefix": "明天我们将",
|
|
408
|
+
"targetTerm": "乘飞机",
|
|
409
|
+
"targetSuffix": "回家",
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"sourcePrefix": "Tomorrow we're going to ",
|
|
413
|
+
"sourceTerm": "fly",
|
|
414
|
+
"sourceSuffix": " home.",
|
|
415
|
+
"targetPrefix": "明天我们就",
|
|
416
|
+
"targetTerm": "乘飞机",
|
|
417
|
+
"targetSuffix": "回家了。",
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
"sourcePrefix": "No students ",
|
|
421
|
+
"sourceTerm": "fly",
|
|
422
|
+
"sourceSuffix": " to school.",
|
|
423
|
+
"targetPrefix": "没有学生",
|
|
424
|
+
"targetTerm": "乘飞机",
|
|
425
|
+
"targetSuffix": "上学。",
|
|
426
|
+
},
|
|
427
|
+
]
|
|
428
|
+
|
|
429
|
+
# 最终结果的一个中间态存储结构
|
|
430
|
+
"""
|
|
431
|
+
1. 新的句子example结构:
|
|
432
|
+
{
|
|
433
|
+
"sourcePrefix": "前半句",
|
|
434
|
+
"sourceTerm": "关键词",
|
|
435
|
+
"sourceSuffix": "后半句",
|
|
436
|
+
|
|
437
|
+
// 将这里翻译放到了list中,因为部分句子一样,但是翻译不一样
|
|
438
|
+
"target": [
|
|
439
|
+
{
|
|
440
|
+
"targetPrefix": "翻译前半句",
|
|
441
|
+
"targetTerm": "翻译关键词",
|
|
442
|
+
"targetSuffix": "翻译后半句",
|
|
443
|
+
}
|
|
444
|
+
],
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
2. 临时句子就是: "前半句+关键词+后半句"
|
|
448
|
+
|
|
449
|
+
3. final_res_info的结构:
|
|
450
|
+
{
|
|
451
|
+
临时句子: 句子翻译新结构
|
|
452
|
+
}
|
|
453
|
+
"""
|
|
454
|
+
final_res_info = {}
|
|
455
|
+
|
|
456
|
+
for example_info in examples_info:
|
|
457
|
+
# 1. 临时拼接形成完整句子,根据这个句子判断是否已经存在
|
|
458
|
+
temp_sentence: str = (
|
|
459
|
+
example_info["sourcePrefix"]
|
|
460
|
+
+ example_info["sourceTerm"]
|
|
461
|
+
+ example_info["sourceSuffix"]
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
# 1.1 [准备数据] 获取当前翻译的结构:
|
|
465
|
+
# 如果首次添加,后面会在外层套个list,添加;
|
|
466
|
+
# 如果已经存在,则在 `已经存在的翻译` list中追加 `当前翻译的结构`
|
|
467
|
+
current_target = {
|
|
468
|
+
"targetPrefix": example_info["targetPrefix"],
|
|
469
|
+
"targetTerm": example_info["targetTerm"],
|
|
470
|
+
"targetSuffix": example_info["targetSuffix"],
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if temp_sentence not in final_res_info:
|
|
474
|
+
# 当前句子不存在时,直接添加到final_res_info中
|
|
475
|
+
|
|
476
|
+
# 2.1 生成新的sample结构
|
|
477
|
+
new_example = {
|
|
478
|
+
"sourcePrefix": example_info["sourcePrefix"],
|
|
479
|
+
"sourceTerm": example_info["sourceTerm"],
|
|
480
|
+
"sourceSuffix": example_info["sourceSuffix"],
|
|
481
|
+
"target": [current_target],
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
# 2.2 添加到final_res_info中
|
|
485
|
+
final_res_info[temp_sentence] = new_example
|
|
486
|
+
else:
|
|
487
|
+
# 当前句子已经存在时
|
|
488
|
+
# 2.1 获取已经存在的sample [此处是引用获取的]
|
|
489
|
+
exist_example_info: dict = final_res_info[temp_sentence]
|
|
490
|
+
|
|
491
|
+
# 2.1 获取已经存在的sample中的翻译
|
|
492
|
+
exist_example_target: list[dict] = exist_example_info["target"]
|
|
493
|
+
# 2.2 将当前翻译添加到已经存在的翻译中
|
|
494
|
+
exist_example_target.append(current_target)
|
|
495
|
+
|
|
496
|
+
# 2.3 更新已经存在的sample中的翻译
|
|
497
|
+
exist_example_info["target"] = exist_example_target
|
|
498
|
+
|
|
499
|
+
# 3. 将final_res_info中的值取出来,组装成以下结构
|
|
500
|
+
"""
|
|
501
|
+
[
|
|
502
|
+
{
|
|
503
|
+
"sourcePrefix": "前半句",
|
|
504
|
+
"sourceTerm": "关键词",
|
|
505
|
+
"sourceSuffix": "后半句",
|
|
506
|
+
"target": [
|
|
507
|
+
{
|
|
508
|
+
"targetPrefix": "翻译前半句",
|
|
509
|
+
"targetTerm": "翻译关键词",
|
|
510
|
+
"targetSuffix": "翻译后半句",
|
|
511
|
+
}
|
|
512
|
+
],
|
|
513
|
+
]
|
|
514
|
+
"""
|
|
515
|
+
final_res: list[dict] = list(final_res_info.values())
|
|
516
|
+
return final_res
|
|
517
|
+
|
|
518
|
+
def _get_word_translation_all_examples(
|
|
519
|
+
self,
|
|
520
|
+
raw_response: dict,
|
|
521
|
+
source_language_code: LanguageCodeOfBackend,
|
|
522
|
+
target_language_code: LanguageCodeOfBackend,
|
|
523
|
+
) -> list[dict]:
|
|
524
|
+
"""获取单词的所有翻译例句,返回一个新的翻译结果
|
|
525
|
+
raw_response: 单词的原始翻译结果,是_send_word_request方法的返回结果
|
|
526
|
+
"""
|
|
527
|
+
# 1. 获取标准化之后的单词
|
|
528
|
+
normalized_word = raw_response["normalizedSource"]
|
|
529
|
+
|
|
530
|
+
# 2.1 获取原始的翻译结果
|
|
531
|
+
translations_info = raw_response["translations"]
|
|
532
|
+
# 2.2 添加例句之后的新翻译结果
|
|
533
|
+
final_translations_info = []
|
|
534
|
+
|
|
535
|
+
# 遍历每一个翻译结果,获取其翻译例句
|
|
536
|
+
for translation_info in translations_info:
|
|
537
|
+
# 1. 获取翻译的标准化形式 例: "飞"
|
|
538
|
+
normalized_translation: str = translation_info["normalizedTarget"]
|
|
539
|
+
# 2. 获取当前翻译的所有例句
|
|
540
|
+
raw_translation_samples: list[dict] = self._send_word_examples_request(
|
|
541
|
+
normalized_word=normalized_word,
|
|
542
|
+
normalized_translation=normalized_translation,
|
|
543
|
+
source_language_code=source_language_code,
|
|
544
|
+
target_language_code=target_language_code,
|
|
545
|
+
)
|
|
546
|
+
# 去重翻译例句
|
|
547
|
+
translation_samples: list[dict] = self._unique_word_examples(
|
|
548
|
+
raw_translation_samples
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
# 3. 为当前翻译添加所有例句
|
|
552
|
+
translation_info["examples"] = translation_samples
|
|
553
|
+
|
|
554
|
+
# 4. 将新的翻译结果添加到最终结果中
|
|
555
|
+
final_translations_info.append(translation_info)
|
|
556
|
+
|
|
557
|
+
return final_translations_info
|
|
558
|
+
|
|
559
|
+
# ############################ [私有方法] 发送sentence请求 ############################
|
|
560
|
+
def _send_sentence_request(
|
|
561
|
+
self,
|
|
562
|
+
text: str,
|
|
563
|
+
source_language_code: LanguageCodeOfBackend,
|
|
564
|
+
target_language_code: LanguageCodeOfBackend,
|
|
565
|
+
) -> dict:
|
|
566
|
+
"""发送句子翻译请求\n
|
|
567
|
+
[文本翻译接口](https://learn.microsoft.com/en-us/azure/ai-services/translator/text-translation-overview)
|
|
568
|
+
"""
|
|
569
|
+
# 1. 转换语言代码
|
|
570
|
+
source_language_code = self._change_language_code(source_language_code)
|
|
571
|
+
target_language_code = self._change_language_code(target_language_code)
|
|
572
|
+
|
|
573
|
+
# 2. 构造请求查询参数: 源语言,目标语言
|
|
574
|
+
params = self.part_params | {
|
|
575
|
+
"from": source_language_code,
|
|
576
|
+
"to": target_language_code,
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
# 3. 构造请求头: key,location,content-type
|
|
580
|
+
headers = {
|
|
581
|
+
"Ocp-Apim-Subscription-Key": self.key,
|
|
582
|
+
"Ocp-Apim-Subscription-Region": self.location_region,
|
|
583
|
+
"Content-type": "application/json",
|
|
584
|
+
"X-ClientTraceId": str(uuid.uuid4()),
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
# 4. 构造请求体: 要翻译的文本
|
|
588
|
+
body = [{"text": text}]
|
|
589
|
+
|
|
590
|
+
# 5. 发送请求
|
|
591
|
+
try:
|
|
592
|
+
response = requests.post(
|
|
593
|
+
self.text_endpoint, params=params, headers=headers, json=body
|
|
594
|
+
)
|
|
595
|
+
except Exception as e:
|
|
596
|
+
_ = f"microsoft 翻译请求连接失败: {self.text_endpoint} {params} {headers} {body}"
|
|
597
|
+
raise ValueError(_ + e)
|
|
598
|
+
if response.status_code != 200:
|
|
599
|
+
raise ValueError(
|
|
600
|
+
f"microsoft 翻译响应状态: {response.status_code}; 响应具体错误: {response.text}\
|
|
601
|
+
实际请求url: {response.url}; 请求参数: {headers} {body}"
|
|
602
|
+
)
|
|
603
|
+
|
|
604
|
+
# 6.返回原生响应
|
|
605
|
+
response: list[dict] = response.json()
|
|
606
|
+
assert len(response) == 1, f"microsoft翻译响应格式错误1: {response}"
|
|
607
|
+
|
|
608
|
+
# 解析1
|
|
609
|
+
response_dict: dict = response[0]
|
|
610
|
+
assert (
|
|
611
|
+
"translations" in response_dict
|
|
612
|
+
), f"microsoft翻译响应格式错误2: {response_dict}"
|
|
613
|
+
_ = {
|
|
614
|
+
"translations": [
|
|
615
|
+
{
|
|
616
|
+
"text": "J'aimerais vraiment conduire votre voiture autour\
|
|
617
|
+
du pâté de maisons plusieurs fois!",
|
|
618
|
+
"to": "fr",
|
|
619
|
+
},
|
|
620
|
+
]
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
# 2. 解析原生响应
|
|
624
|
+
response_dict2: list[dict] = response_dict["translations"]
|
|
625
|
+
assert len(response_dict2) == 1, f"microsoft翻译响应格式错误3: {response_dict2}"
|
|
626
|
+
_ = [
|
|
627
|
+
{
|
|
628
|
+
"text": "J'aimerais vraiment conduire votre voiture\
|
|
629
|
+
autour du pâté de maisons plusieurs fois!",
|
|
630
|
+
"to": "fr",
|
|
631
|
+
},
|
|
632
|
+
]
|
|
633
|
+
|
|
634
|
+
# 3. 返回结果
|
|
635
|
+
res: dict = response_dict2[0]
|
|
636
|
+
_ = {
|
|
637
|
+
"text": "J'aimerais vraiment conduire votre voiture\
|
|
638
|
+
autour du pâté de maisons plusieurs fois!",
|
|
639
|
+
"to": "fr",
|
|
640
|
+
}
|
|
641
|
+
return res
|
|
642
|
+
|
|
643
|
+
def _send_tts_request(self, text: str, _text_language_code: str) -> bytes:
|
|
644
|
+
"""发送tts请求
|
|
645
|
+
# HACK 当前只能生成英文的音频
|
|
646
|
+
"""
|
|
647
|
+
wav_data = self.tts_microsoft_obj.convert_text_to_speech(text)
|
|
648
|
+
return wav_data
|
|
649
|
+
|
|
650
|
+
# ############################ [静态方法] ############################
|
|
651
|
+
@staticmethod
|
|
652
|
+
def _change_language_code(language_code_of_backend: LanguageCodeOfBackend) -> str:
|
|
653
|
+
"""转换语言代码,将language_backend后端的语言代码转换为microsoft的语言代码
|
|
654
|
+
[microsoft支持的语言](https://learn.microsoft.com/en-us/azure/ai-services/translator/reference/v3-0-languages)
|
|
655
|
+
"""
|
|
656
|
+
match language_code_of_backend:
|
|
657
|
+
case "zh-CN":
|
|
658
|
+
# 简体中文. 将后端的`zh-CN`转换为微软`zh-Hans`
|
|
659
|
+
return "zh-Hans"
|
|
660
|
+
case "zh-TW":
|
|
661
|
+
# 繁体中文. 将后端的`zh-TW`转换为微软`zh-Hant`
|
|
662
|
+
return "zh-Hant"
|
|
663
|
+
case "en" | "ja":
|
|
664
|
+
# en: 英语, ja: 日语 等,不需要转换
|
|
665
|
+
return language_code_of_backend
|
|
666
|
+
case _:
|
|
667
|
+
raise ValueError(
|
|
668
|
+
f"microsoft不支持的语言代码: {language_code_of_backend}"
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
@staticmethod
|
|
672
|
+
def _change_pos_tag(pos_tag: str) -> tuple[str, str]:
|
|
673
|
+
"""将microsft的词性标签转换为人类可读的词性标签
|
|
674
|
+
pos_tag: microsoft的词性标签 "ADJ"
|
|
675
|
+
返回: ("Adjectives", "形容词")
|
|
676
|
+
"""
|
|
677
|
+
|
|
678
|
+
# 将此术语与词性标签关联的字符串
|
|
679
|
+
if pos_tag not in pos_tag_table:
|
|
680
|
+
raise ValueError(f"microsoft不支持的词性标签: {pos_tag}")
|
|
681
|
+
return pos_tag_table[pos_tag]
|
|
682
|
+
|
|
683
|
+
@staticmethod
|
|
684
|
+
def _change_frequency(frequency: float) -> float:
|
|
685
|
+
"""将microsoft的词频转换为人类可读的词频,小数点第二位四舍五入
|
|
686
|
+
frequency: 0.123
|
|
687
|
+
返回: 12.3
|
|
688
|
+
"""
|
|
689
|
+
return round(frequency * 100, 1)
|
|
690
|
+
|
|
691
|
+
@staticmethod
|
|
692
|
+
def _sort_shapes(raw_shapes: list[dict]) -> list[dict]:
|
|
693
|
+
"""对单词的其他形式进行排序.从大到小"""
|
|
694
|
+
return sorted(
|
|
695
|
+
raw_shapes, key=lambda shape: shape["frequencyCount"], reverse=True
|
|
696
|
+
)
|