src/eric7/Plugins/UiExtensionPlugins/Translator/TranslatorEngines/IbmWatsonEngine.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
8 """ 8 """
9 9
10 import json 10 import json
11 11
12 from PyQt6.QtCore import QUrl, QByteArray, QTimer 12 from PyQt6.QtCore import QUrl, QByteArray, QTimer
13 from PyQt6.QtNetwork import ( 13 from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
14 QNetworkAccessManager, QNetworkRequest, QNetworkReply
15 )
16 14
17 from EricWidgets import EricMessageBox 15 from EricWidgets import EricMessageBox
18 16
19 from EricNetwork.EricNetworkProxyFactory import proxyAuthenticationRequired 17 from EricNetwork.EricNetworkProxyFactory import proxyAuthenticationRequired
20 18
24 class IbmWatsonEngine(TranslationEngine): 22 class IbmWatsonEngine(TranslationEngine):
25 """ 23 """
26 Class implementing the translation engine for the IBM Watson Language 24 Class implementing the translation engine for the IBM Watson Language
27 Translator service. 25 Translator service.
28 """ 26 """
27
29 # Documentation: 28 # Documentation:
30 # https://www.ibm.com/watson/developercloud/language-translator 29 # https://www.ibm.com/watson/developercloud/language-translator
31 # 30 #
32 # Start page: 31 # Start page:
33 # https://www.ibm.com/watson/services/language-translator/ 32 # https://www.ibm.com/watson/services/language-translator/
34 33
35 def __init__(self, plugin, parent=None): 34 def __init__(self, plugin, parent=None):
36 """ 35 """
37 Constructor 36 Constructor
38 37
39 @param plugin reference to the plugin object 38 @param plugin reference to the plugin object
40 @type TranslatorPlugin 39 @type TranslatorPlugin
41 @param parent reference to the parent object 40 @param parent reference to the parent object
42 @type QObject 41 @type QObject
43 """ 42 """
44 super().__init__(plugin, parent) 43 super().__init__(plugin, parent)
45 44
46 self.__ui = parent 45 self.__ui = parent
47 46
48 self.__networkManager = QNetworkAccessManager(self) 47 self.__networkManager = QNetworkAccessManager(self)
49 self.__networkManager.proxyAuthenticationRequired.connect( 48 self.__networkManager.proxyAuthenticationRequired.connect(
50 proxyAuthenticationRequired) 49 proxyAuthenticationRequired
51 50 )
51
52 self.__availableTranslations = {} 52 self.__availableTranslations = {}
53 # dictionary of sets of available translations 53 # dictionary of sets of available translations
54 54
55 self.__replies = [] 55 self.__replies = []
56 56
57 QTimer.singleShot(0, self.__getTranslationModels) 57 QTimer.singleShot(0, self.__getTranslationModels)
58 58
59 def engineName(self): 59 def engineName(self):
60 """ 60 """
61 Public method to return the name of the engine. 61 Public method to return the name of the engine.
62 62
63 @return engine name 63 @return engine name
64 @rtype str 64 @rtype str
65 """ 65 """
66 return "ibm_watson" 66 return "ibm_watson"
67 67
68 def supportedLanguages(self): 68 def supportedLanguages(self):
69 """ 69 """
70 Public method to get the supported languages. 70 Public method to get the supported languages.
71 71
72 @return list of supported language codes 72 @return list of supported language codes
73 @rtype list of str 73 @rtype list of str
74 """ 74 """
75 return list(self.__availableTranslations.keys()) 75 return list(self.__availableTranslations.keys())
76 76
77 def supportedTargetLanguages(self, original): 77 def supportedTargetLanguages(self, original):
78 """ 78 """
79 Public method to get a list of supported target languages for an 79 Public method to get a list of supported target languages for an
80 original language. 80 original language.
81 81
82 @param original original language 82 @param original original language
83 @type str 83 @type str
84 @return list of supported target languages for the given original 84 @return list of supported target languages for the given original
85 @rtype list of str 85 @rtype list of str
86 """ 86 """
87 targets = self.__availableTranslations.get(original, set()) 87 targets = self.__availableTranslations.get(original, set())
88 return list(targets) 88 return list(targets)
89 89
90 def hasTTS(self): 90 def hasTTS(self):
91 """ 91 """
92 Public method indicating the Text-to-Speech capability. 92 Public method indicating the Text-to-Speech capability.
93 93
94 @return flag indicating the Text-to-Speech capability 94 @return flag indicating the Text-to-Speech capability
95 @rtype bool 95 @rtype bool
96 """ 96 """
97 return False 97 return False
98 98
99 def getTranslation(self, requestObject, text, originalLanguage, 99 def getTranslation(
100 translationLanguage): 100 self, requestObject, text, originalLanguage, translationLanguage
101 ):
101 """ 102 """
102 Public method to translate the given text. 103 Public method to translate the given text.
103 104
104 @param requestObject reference to the request object 105 @param requestObject reference to the request object
105 @type TranslatorRequest 106 @type TranslatorRequest
106 @param text text to be translated 107 @param text text to be translated
107 @type str 108 @type str
108 @param originalLanguage language code of the original 109 @param originalLanguage language code of the original
112 @return tuple of translated text and flag indicating success 113 @return tuple of translated text and flag indicating success
113 @rtype tuple of (str, bool) 114 @rtype tuple of (str, bool)
114 """ 115 """
115 apiKey = self.plugin.getPreferences("IbmKey") 116 apiKey = self.plugin.getPreferences("IbmKey")
116 if not apiKey: 117 if not apiKey:
117 return self.tr("IBM Watson: A valid Language Translator key is" 118 return (
118 " required."), False 119 self.tr("IBM Watson: A valid Language Translator key is" " required."),
120 False,
121 )
119 translatorUrl = self.plugin.getPreferences("IbmUrl") 122 translatorUrl = self.plugin.getPreferences("IbmUrl")
120 if not translatorUrl: 123 if not translatorUrl:
121 return self.tr("IBM Watson: A valid Language Translator URL is" 124 return (
122 " required."), False 125 self.tr("IBM Watson: A valid Language Translator URL is" " required."),
123 126 False,
127 )
128
124 params = "?version=2018-05-01" 129 params = "?version=2018-05-01"
125 url = QUrl(translatorUrl + "/v3/translate" + params) 130 url = QUrl(translatorUrl + "/v3/translate" + params)
126 131
127 requestDict = { 132 requestDict = {
128 "text": [text], 133 "text": [text],
129 "source": originalLanguage, 134 "source": originalLanguage,
130 "target": translationLanguage, 135 "target": translationLanguage,
131 } 136 }
132 request = QByteArray(json.dumps(requestDict).encode("utf-8")) 137 request = QByteArray(json.dumps(requestDict).encode("utf-8"))
133 138
134 extraHeaders = [ 139 extraHeaders = [
135 (b"Authorization", 140 (
136 b"Basic " + QByteArray( 141 b"Authorization",
137 b"apikey:" + apiKey.encode("utf-8")).toBase64()) 142 b"Basic " + QByteArray(b"apikey:" + apiKey.encode("utf-8")).toBase64(),
143 )
138 ] 144 ]
139 145
140 response, ok = requestObject.post(url, request, dataType="json", 146 response, ok = requestObject.post(
141 extraHeaders=extraHeaders) 147 url, request, dataType="json", extraHeaders=extraHeaders
148 )
142 if ok: 149 if ok:
143 try: 150 try:
144 responseDict = json.loads(response) 151 responseDict = json.loads(response)
145 except ValueError: 152 except ValueError:
146 return self.tr("IBM Watson: Invalid response received"), False 153 return self.tr("IBM Watson: Invalid response received"), False
147 154
148 if "translations" not in responseDict: 155 if "translations" not in responseDict:
149 return self.tr("IBM Watson: No translation available."), False 156 return self.tr("IBM Watson: No translation available."), False
150 157
151 result = "" 158 result = ""
152 translations = responseDict["translations"] 159 translations = responseDict["translations"]
153 for translation in translations: 160 for translation in translations:
154 result += translation["translation"] 161 result += translation["translation"]
155 if translation != translations[-1]: 162 if translation != translations[-1]:
156 result += "<br/>" 163 result += "<br/>"
157 else: 164 else:
158 result = response 165 result = response
159 return result, ok 166 return result, ok
160 167
161 def __adjustLanguageCode(self, code): 168 def __adjustLanguageCode(self, code):
162 """ 169 """
163 Private method to adjust a given language code. 170 Private method to adjust a given language code.
164 171
165 @param code code to be adjusted 172 @param code code to be adjusted
166 @type str 173 @type str
167 @return adjusted language code 174 @return adjusted language code
168 @rtype str 175 @rtype str
169 """ 176 """
170 if code == "zh": 177 if code == "zh":
171 return "zh-CN" 178 return "zh-CN"
172 else: 179 else:
173 return code 180 return code
174 181
175 def __getTranslationModels(self): 182 def __getTranslationModels(self):
176 """ 183 """
177 Private method to get the translation models supported by IBM Watson 184 Private method to get the translation models supported by IBM Watson
178 Language Translator. 185 Language Translator.
179 """ 186 """
180 apiKey = self.plugin.getPreferences("IbmKey") 187 apiKey = self.plugin.getPreferences("IbmKey")
181 if not apiKey: 188 if not apiKey:
182 EricMessageBox.critical( 189 EricMessageBox.critical(
183 self.__ui, 190 self.__ui,
184 self.tr("Error Getting Available Translations"), 191 self.tr("Error Getting Available Translations"),
185 self.tr("IBM Watson: A valid Language Translator key is" 192 self.tr("IBM Watson: A valid Language Translator key is" " required."),
186 " required.")
187 ) 193 )
188 return 194 return
189 translatorUrl = self.plugin.getPreferences("IbmUrl") 195 translatorUrl = self.plugin.getPreferences("IbmUrl")
190 if not translatorUrl: 196 if not translatorUrl:
191 EricMessageBox.critical( 197 EricMessageBox.critical(
192 self.__ui, 198 self.__ui,
193 self.tr("Error Getting Available Translations"), 199 self.tr("Error Getting Available Translations"),
194 self.tr("IBM Watson: A valid Language Translator URL is" 200 self.tr("IBM Watson: A valid Language Translator URL is" " required."),
195 " required.")
196 ) 201 )
197 return 202 return
198 203
199 params = "?version=2018-05-01" 204 params = "?version=2018-05-01"
200 url = QUrl(translatorUrl + "/v3/models" + params) 205 url = QUrl(translatorUrl + "/v3/models" + params)
201 206
202 extraHeaders = [ 207 extraHeaders = [
203 (b"Authorization", 208 (
204 b"Basic " + QByteArray( 209 b"Authorization",
205 b"apikey:" + apiKey.encode("utf-8")).toBase64()) 210 b"Basic " + QByteArray(b"apikey:" + apiKey.encode("utf-8")).toBase64(),
211 )
206 ] 212 ]
207 213
208 request = QNetworkRequest(url) 214 request = QNetworkRequest(url)
209 if extraHeaders: 215 if extraHeaders:
210 for name, value in extraHeaders: 216 for name, value in extraHeaders:
211 request.setRawHeader(name, value) 217 request.setRawHeader(name, value)
212 reply = self.__networkManager.get(request) 218 reply = self.__networkManager.get(request)
213 reply.finished.connect( 219 reply.finished.connect(lambda: self.__getTranslationModelsReplyFinished(reply))
214 lambda: self.__getTranslationModelsReplyFinished(reply))
215 self.__replies.append(reply) 220 self.__replies.append(reply)
216 221
217 def __getTranslationModelsReplyFinished(self, reply): 222 def __getTranslationModelsReplyFinished(self, reply):
218 """ 223 """
219 Private slot handling the receipt of the available translations. 224 Private slot handling the receipt of the available translations.
220 225
221 @param reply reference to the network reply object 226 @param reply reference to the network reply object
222 @type QNetworkReply 227 @type QNetworkReply
223 """ 228 """
224 if reply in self.__replies: 229 if reply in self.__replies:
225 self.__replies.remove(reply) 230 self.__replies.remove(reply)
226 reply.deleteLater() 231 reply.deleteLater()
227 232
228 if reply.error() != QNetworkReply.NetworkError.NoError: 233 if reply.error() != QNetworkReply.NetworkError.NoError:
229 errorStr = reply.errorString() 234 errorStr = reply.errorString()
230 EricMessageBox.critical( 235 EricMessageBox.critical(
231 self.__ui, 236 self.__ui,
232 self.tr("Error Getting Available Translations"), 237 self.tr("Error Getting Available Translations"),
233 self.tr("IBM Watson: The server sent an error indication." 238 self.tr(
234 "\n Error: {0}").format(errorStr) 239 "IBM Watson: The server sent an error indication."
240 "\n Error: {0}"
241 ).format(errorStr),
235 ) 242 )
236 return 243 return
237 else: 244 else:
238 response = str(reply.readAll(), "utf-8", "replace") 245 response = str(reply.readAll(), "utf-8", "replace")
239 try: 246 try:
240 responseDict = json.loads(response) 247 responseDict = json.loads(response)
241 except ValueError: 248 except ValueError:
242 EricMessageBox.critical( 249 EricMessageBox.critical(
243 self.__ui, 250 self.__ui,
244 self.tr("Error Getting Available Translations"), 251 self.tr("Error Getting Available Translations"),
245 self.tr("IBM Watson: Invalid response received") 252 self.tr("IBM Watson: Invalid response received"),
246 ) 253 )
247 return 254 return
248 255
249 if "models" not in responseDict: 256 if "models" not in responseDict:
250 EricMessageBox.critical( 257 EricMessageBox.critical(
251 self.__ui, 258 self.__ui,
252 self.tr("Error Getting Available Translations"), 259 self.tr("Error Getting Available Translations"),
253 self.tr("IBM Watson: No translation available.") 260 self.tr("IBM Watson: No translation available."),
254 ) 261 )
255 return 262 return
256 263
257 for model in responseDict["models"]: 264 for model in responseDict["models"]:
258 if model["status"] == "available": 265 if model["status"] == "available":
259 source = self.__adjustLanguageCode(model["source"]) 266 source = self.__adjustLanguageCode(model["source"])
260 target = self.__adjustLanguageCode(model["target"]) 267 target = self.__adjustLanguageCode(model["target"])
261 if source not in self.__availableTranslations: 268 if source not in self.__availableTranslations:
262 self.__availableTranslations[source] = set() 269 self.__availableTranslations[source] = set()
263 self.__availableTranslations[source].add(target) 270 self.__availableTranslations[source].add(target)
264 271
265 self.availableTranslationsLoaded.emit() 272 self.availableTranslationsLoaded.emit()

eric ide

mercurial