1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Business Applications
5 # Copyright (C) 2004-2012 OpenERP S.A. (<http://openerp.com>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
27 from openerp.osv import osv, fields
28 from openerp import tools, SUPERUSER_ID
29 from openerp.tools.translate import _
31 _logger = logging.getLogger(__name__)
34 from gengo import Gengo
36 _logger.warning('Gengo library not found, Gengo features disabled. If you plan to use it, please install the gengo library from http://pypi.python.org/pypi/gengo')
38 GENGO_DEFAULT_LIMIT = 20
41 class base_gengo_translations(osv.osv_memory):
42 GENGO_KEY = "Gengo.UUID"
44 _name = 'base.gengo.translations'
46 'sync_type': fields.selection([('send', 'Send New Terms'),
47 ('receive', 'Receive Translation'),
48 ('both', 'Both')], "Sync Type"),
49 'lang_id': fields.many2one('res.lang', 'Language', required=True),
50 'sync_limit': fields.integer("No. of terms to sync"),
58 icp = self.pool['ir.config_parameter']
59 if not icp.get_param(cr, SUPERUSER_ID, self.GENGO_KEY, default=None):
60 icp.set_param(cr, SUPERUSER_ID, self.GENGO_KEY, str(uuid.uuid4()))
62 def get_gengo_key(self, cr):
63 icp = self.pool['ir.config_parameter']
64 return icp.get_param(cr, SUPERUSER_ID, self.GENGO_KEY, default="Undefined")
66 def gengo_authentication(self, cr, uid, context=None):
68 This method tries to open a connection with Gengo. For that, it uses the Public and Private
69 keys that are linked to the company (given by Gengo on subscription). It returns a tuple with
70 * as first element: a boolean depicting if the authentication was a success or not
71 * as second element: the connection, if it was a success, or the error message returned by
72 Gengo when the connection failed.
73 This error message can either be displayed in the server logs (if the authentication was called
74 by the cron) or in a dialog box (if requested by the user), thus it's important to return it
77 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
78 if not user.company_id.gengo_public_key or not user.company_id.gengo_private_key:
79 return (False, _("Gengo `Public Key` or `Private Key` are missing. Enter your Gengo authentication parameters under `Settings > Companies > Gengo Parameters`."))
82 public_key=user.company_id.gengo_public_key.encode('ascii'),
83 private_key=user.company_id.gengo_private_key.encode('ascii'),
84 sandbox=user.company_id.gengo_sandbox,
86 gengo.getAccountStats()
89 _logger.exception('Gengo connection failed')
90 return (False, _("Gengo connection failed with this message:\n``%s``") % e)
92 def act_update(self, cr, uid, ids, context=None):
94 Function called by the wizard.
99 flag, gengo = self.gengo_authentication(cr, uid, context=context)
101 raise osv.except_osv(_('Gengo Authentication Error'), gengo)
102 for wizard in self.browse(cr, uid, ids, context=context):
103 supported_langs = self.pool.get('ir.translation')._get_all_supported_languages(cr, uid, context=context)
104 language = self.pool.get('ir.translation')._get_gengo_corresponding_language(wizard.lang_id.code)
105 if language not in supported_langs:
106 raise osv.except_osv(_("Warning"), _('This language is not supported by the Gengo translation services.'))
109 ctx['gengo_language'] = wizard.lang_id.id
110 if wizard.sync_limit > 200 or wizard.sync_limit < 1:
111 raise osv.except_osv(_("Warning"), _('Sync limit should between 1 to 200 for Gengo translation services.'))
112 if wizard.sync_type in ['send', 'both']:
113 self._sync_request(cr, uid, wizard.sync_limit, context=ctx)
114 if wizard.sync_type in ['receive', 'both']:
115 self._sync_response(cr, uid, wizard.sync_limit, context=ctx)
116 return {'type': 'ir.actions.act_window_close'}
118 def _sync_response(self, cr, uid, limit=GENGO_DEFAULT_LIMIT, context=None):
120 This method will be called by cron services to get translations from
121 Gengo. It will read translated terms and comments from Gengo and will
122 update respective ir.translation in openerp.
124 translation_pool = self.pool.get('ir.translation')
125 flag, gengo = self.gengo_authentication(cr, uid, context=context)
127 _logger.warning("%s", gengo)
130 all_translation_ids = translation_pool.search(cr, uid, [('state', '=', 'inprogress'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('order_id', "!=", False)], context=context)
132 translation_ids = all_translation_ids[offset:offset + limit]
134 if not translation_ids:
138 'gengo_order_ids': set(),
139 'ir_translation_ids': set(),
141 translation_terms = translation_pool.browse(cr, uid, translation_ids, context=context)
142 for term in translation_terms:
143 terms_progress['gengo_order_ids'].add(term.order_id)
144 terms_progress['ir_translation_ids'].add(tools.ustr(term.id))
146 for order_id in terms_progress['gengo_order_ids']:
147 order_response = gengo.getTranslationOrderJobs(id=order_id)
148 jobs_approved = order_response.get('response', []).get('order', []).get('jobs_approved', [])
149 gengo_ids = ','.join(jobs_approved)
151 if gengo_ids: # Need to check, because getTranslationJobBatch don't catch this case and so call the getTranslationJobs because no ids in url
153 job_response = gengo.getTranslationJobBatch(id=gengo_ids)
156 if job_response['opstat'] == 'ok':
157 for job in job_response['response'].get('jobs', []):
158 if job.get('custom_data') in terms_progress['ir_translation_ids']:
159 self._update_terms_job(cr, uid, job, context=context)
162 def _update_terms_job(self, cr, uid, job, context=None):
163 translation_pool = self.pool.get('ir.translation')
164 tid = int(job['custom_data'])
166 if job.get('status', False) in ('queued', 'available', 'pending', 'reviewable'):
167 vals['state'] = 'inprogress'
168 if job.get('body_tgt', False) and job.get('status', False) == 'approved':
169 vals['value'] = job['body_tgt']
170 if job.get('status', False) in ('approved', 'canceled'):
171 vals['state'] = 'translated'
173 translation_pool.write(cr, uid, [tid], vals, context=context)
175 def _update_terms(self, cr, uid, response, term_ids, context=None):
177 Update the terms after their translation were requested to Gengo
179 translation_pool = self.pool.get('ir.translation')
182 'order_id': response.get('order_id', ''),
183 'state': 'inprogress'
186 translation_pool.write(cr, uid, term_ids, vals, context=context)
187 jobs = response.get('jobs', [])
189 for t_id, res in jobs.items():
190 self._update_terms_job(cr, uid, res, context=context)
194 def pack_jobs_request(self, cr, uid, term_ids, context=None):
195 ''' prepare the terms that will be requested to gengo and returns them in a dictionary with following format
202 translation_pool = self.pool.get('ir.translation')
204 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
205 auto_approve = 1 if user.company_id.gengo_auto_approve else 0
206 for term in translation_pool.browse(cr, uid, term_ids, context=context):
207 if re.search(r"\w", term.src or ""):
208 comment = user.company_id.gengo_comment or ''
209 if term.gengo_comment:
210 comment += '\n' + term.gengo_comment
211 jobs[time.strftime('%Y%m%d%H%M%S') + '-' + str(term.id)] = {
213 'slug': 'Single :: English to ' + term.lang,
214 'tier': tools.ustr(term.gengo_translation),
215 'custom_data': str(term.id),
216 'body_src': term.src,
218 'lc_tgt': translation_pool._get_gengo_corresponding_language(term.lang),
219 'auto_approve': auto_approve,
221 'callback_url': self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') + '/website/gengo_callback?pgk=' + self.get_gengo_key(cr)
223 return {'jobs': jobs, 'as_group': 0}
225 def _send_translation_terms(self, cr, uid, term_ids, context=None):
227 Send a request to Gengo with all the term_ids in a different job, get the response and update the terms in
228 database accordingly.
230 flag, gengo = self.gengo_authentication(cr, uid, context=context)
232 request = self.pack_jobs_request(cr, uid, term_ids, context=context)
234 result = gengo.postTranslationJobs(jobs=request)
235 if result['opstat'] == 'ok':
236 self._update_terms(cr, uid, result['response'], term_ids, context=context)
241 def _sync_request(self, cr, uid, limit=GENGO_DEFAULT_LIMIT, context=None):
243 This scheduler will send a job request to the gengo , which terms are
244 waiing to be translated and for which gengo_translation is enabled.
246 A special key 'gengo_language' can be passed in the context in order to
247 request only translations of that language only. Its value is the language
252 language_pool = self.pool.get('res.lang')
253 translation_pool = self.pool.get('ir.translation')
254 domain = [('state', '=', 'to_translate'), ('gengo_translation', 'in', ('machine', 'standard', 'pro', 'ultra')), ('order_id', "=", False)]
255 if context.get('gengo_language', False):
256 lc = language_pool.browse(cr, uid, context['gengo_language'], context=context).code
257 domain.append(('lang', '=', lc))
259 all_term_ids = translation_pool.search(cr, uid, domain, context=context)
263 #search for the n first terms to translate
264 term_ids = all_term_ids[offset:offset + limit]
267 self._send_translation_terms(cr, uid, term_ids, context=context)
268 _logger.info("%s Translation terms have been posted to Gengo successfully", len(term_ids))
269 if not len(term_ids) == limit:
272 _logger.error("%s", e)
274 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: