256 lines
13 KiB
Python
256 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
|
##############################################################################
|
|
#
|
|
# OpenERP, Open Source Business Applications
|
|
# Copyright (C) 2004-2012 OpenERP S.A. (<http://openerp.com>).
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
##############################################################################
|
|
|
|
import logging
|
|
import re
|
|
import time
|
|
|
|
from openerp.osv import osv, fields
|
|
from openerp import tools
|
|
from openerp.tools.translate import _
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
try:
|
|
from mygengo import MyGengo
|
|
except ImportError:
|
|
_logger.warning('Gengo library not found, Gengo features disabled. If you plan to use it, please install the mygengo library from http://pypi.python.org/pypi/mygengo')
|
|
class MyGengo(object):
|
|
def __init__(self, *args, **kwargs):
|
|
# no context for translations - so don't bother
|
|
raise ImportError('Gengo library not found, please install mygengo from http://pypi.python.org/pypi/mygengo')
|
|
|
|
GENGO_DEFAULT_LIMIT = 20
|
|
|
|
DEFAULT_CRON_VALS = {
|
|
'active': True,
|
|
'interval_number': 20,
|
|
'interval_type': 'minutes',
|
|
'model': "'base.gengo.translations'",
|
|
'args': "'(%s,)'" % (str(GENGO_DEFAULT_LIMIT)),
|
|
}
|
|
|
|
class base_gengo_translations(osv.osv_memory):
|
|
|
|
_name = 'base.gengo.translations'
|
|
_columns = {
|
|
'restart_send_job': fields.boolean("Restart Sending Job"),
|
|
'lang_id': fields.many2one('res.lang', 'Language', required=True),
|
|
}
|
|
|
|
def gengo_authentication(self, cr, uid, context=None):
|
|
'''
|
|
This method tries to open a connection with Gengo. For that, it uses the Public and Private
|
|
keys that are linked to the company (given by Gengo on subscription). It returns a tuple with
|
|
* as first element: a boolean depicting if the authentication was a success or not
|
|
* as second element: the connection, if it was a success, or the error message returned by
|
|
Gengo when the connection failed.
|
|
This error message can either be displayed in the server logs (if the authentication was called
|
|
by the cron) or in a dialog box (if requested by the user), thus it's important to return it
|
|
translated.
|
|
'''
|
|
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
|
if not user.company_id.gengo_public_key or not user.company_id.gengo_private_key:
|
|
return (False, _("Gengo `Public Key` or `Private Key` are missing. Enter your Gengo authentication parameters under `Settings > Companies > Gengo Parameters`."))
|
|
try:
|
|
gengo = MyGengo(
|
|
public_key=user.company_id.gengo_public_key.encode('ascii'),
|
|
private_key=user.company_id.gengo_private_key.encode('ascii'),
|
|
sandbox = user.company_id.gengo_sandbox,
|
|
)
|
|
gengo.getAccountStats()
|
|
return (True, gengo)
|
|
except Exception, e:
|
|
_logger.exception('Gengo connection failed')
|
|
return (False, _("Gengo connection failed with this message:\n``%s``") % e)
|
|
|
|
def do_check_schedular(self, cr, uid, xml_id, name, fn, context=None):
|
|
"""
|
|
This function is used to reset a cron to its default values, or to recreate it if it was deleted.
|
|
"""
|
|
cron_pool = self.pool.get('ir.cron')
|
|
cron_vals = DEFAULT_CRON_VALS.copy()
|
|
cron_vals.update({'name': name, "function": fn})
|
|
try:
|
|
res = []
|
|
_, res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base_gengo', xml_id)
|
|
cron_pool.write(cr, uid, [res], cron_vals, context=context)
|
|
except:
|
|
#the cron job was not found, probably deleted previously, so we create it again using default values
|
|
cron_vals.update({'numbercall': -1})
|
|
return cron_pool.create(cr, uid, cron_vals, context=context)
|
|
|
|
def act_update(self, cr, uid, ids, context=None):
|
|
'''
|
|
Function called by the wizard.
|
|
'''
|
|
if context == None:
|
|
context = {}
|
|
|
|
flag, gengo = self.gengo_authentication(cr, uid, context=context)
|
|
if not flag:
|
|
raise osv.except_osv(_('Gengo Authentication Error'), gengo)
|
|
for wizard in self.browse(cr, uid, ids, context=context):
|
|
supported_langs = self.pool.get('ir.translation')._get_all_supported_languages(cr, uid, context=context)
|
|
language = self.pool.get('ir.translation')._get_gengo_corresponding_language(wizard.lang_id.code)
|
|
if language not in supported_langs:
|
|
raise osv.except_osv(_("Warning"), _('This language is not supported by the Gengo translation services.'))
|
|
|
|
#send immediately a new request for the selected language (if any)
|
|
ctx = context.copy()
|
|
ctx['gengo_language'] = wizard.lang_id.id
|
|
self._sync_request(cr, uid, limit=GENGO_DEFAULT_LIMIT, context=ctx)
|
|
self._sync_response( cr, uid, limit=GENGO_DEFAULT_LIMIT, context=ctx)
|
|
#check the cron jobs and eventually restart/recreate them
|
|
if wizard.restart_send_job:
|
|
self.do_check_schedular(cr, uid, 'gengo_sync_send_request_scheduler', _('Gengo Sync Translation (Request)'), '_sync_request', context=context)
|
|
self.do_check_schedular(cr, uid, 'gengo_sync_receive_request_scheduler', _('Gengo Sync Translation (Response)'), '_sync_response', context=context)
|
|
return {'type': 'ir.actions.act_window_close'}
|
|
|
|
def _sync_response(self, cr, uid, limit=GENGO_DEFAULT_LIMIT, context=None):
|
|
"""
|
|
This method will be called by cron services to get translations from
|
|
Gengo. It will read translated terms and comments from Gengo and will
|
|
update respective ir.translation in openerp.
|
|
"""
|
|
translation_pool = self.pool.get('ir.translation')
|
|
flag, gengo = self.gengo_authentication(cr, uid, context=context)
|
|
if not flag:
|
|
_logger.warning("%s", gengo)
|
|
else:
|
|
translation_id = translation_pool.search(cr, uid, [('state', '=', 'inprogress'), ('gengo_translation', 'in', ('machine','standard','pro','ultra'))], limit=limit, context=context)
|
|
for term in translation_pool.browse(cr, uid, translation_id, context=context):
|
|
up_term = up_comment = 0
|
|
if term.job_id:
|
|
vals={}
|
|
job_response = gengo.getTranslationJob(id=term.job_id)
|
|
if job_response['opstat'] != 'ok':
|
|
_logger.warning("Invalid Response! Skipping translation Terms with `id` %s." % (term.job_id))
|
|
continue
|
|
if job_response['response']['job']['status'] == 'approved':
|
|
vals.update({'state': 'translated',
|
|
'value': job_response['response']['job']['body_tgt']})
|
|
up_term += 1
|
|
job_comment = gengo.getTranslationJobComments(id=term.job_id)
|
|
if job_comment['opstat']=='ok':
|
|
gengo_comments=""
|
|
for comment in job_comment['response']['thread']:
|
|
gengo_comments += _('%s\n\n--\n Commented on %s by %s.') % (comment['body'], time.ctime(comment['ctime']), comment['author'])
|
|
vals.update({'gengo_comment': gengo_comments})
|
|
up_comment += 1
|
|
if vals:
|
|
translation_pool.write(cr, uid, term.id, vals)
|
|
_logger.info("Successfully Updated `%d` terms and %d Comments." % (up_term, up_comment ))
|
|
else:
|
|
_logger.warning("%s", 'Cannot retrieve the Gengo job ID for translation %s: %s' % (term.id, term.src))
|
|
return True
|
|
|
|
def _update_terms(self, cr, uid, response, context=None):
|
|
"""
|
|
Update the terms after their translation were requested to Gengo
|
|
"""
|
|
translation_pool = self.pool.get('ir.translation')
|
|
for jobs in response['jobs']:
|
|
for t_id, res in jobs.items():
|
|
vals = {}
|
|
t_id = int(t_id)
|
|
tier = translation_pool.read(cr, uid, [t_id], ['gengo_translation'], context=context)[0]['gengo_translation']
|
|
if tier == "machine":
|
|
vals.update({'value': res['body_tgt'], 'state': 'translated'})
|
|
else:
|
|
vals.update({'job_id': res['job_id'], 'state': 'inprogress'})
|
|
translation_pool.write(cr, uid, [t_id], vals, context=context)
|
|
return
|
|
|
|
def pack_jobs_request(self, cr, uid, term_ids, context=None):
|
|
''' prepare the terms that will be requested to gengo and returns them in a dictionary with following format
|
|
{'jobs': {
|
|
'term1.id': {...}
|
|
'term2.id': {...}
|
|
}
|
|
}'''
|
|
|
|
translation_pool = self.pool.get('ir.translation')
|
|
jobs = {}
|
|
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
|
|
auto_approve = 1 if user.company_id.gengo_auto_approve else 0
|
|
for term in translation_pool.browse(cr, uid, term_ids, context=context):
|
|
if re.search(r"\w", term.src or ""):
|
|
jobs[term.id] = {'type': 'text',
|
|
'slug': 'single::English to ' + term.lang,
|
|
'tier': tools.ustr(term.gengo_translation),
|
|
'body_src': term.src,
|
|
'lc_src': 'en',
|
|
'lc_tgt': translation_pool._get_gengo_corresponding_language(term.lang),
|
|
'auto_approve': auto_approve,
|
|
'comment': user.company_id.gengo_comment,
|
|
}
|
|
return {'jobs': jobs}
|
|
|
|
|
|
def _send_translation_terms(self, cr, uid, term_ids, context=None):
|
|
"""
|
|
Send a request to Gengo with all the term_ids in a different job, get the response and update the terms in
|
|
database accordingly.
|
|
"""
|
|
flag, gengo = self.gengo_authentication(cr, uid, context=context)
|
|
if flag:
|
|
request = self.pack_jobs_request(cr, uid, term_ids, context=context)
|
|
if request['jobs']:
|
|
result = gengo.postTranslationJobs(jobs=request)
|
|
if result['opstat'] == 'ok':
|
|
self._update_terms(cr, uid, result['response'], context=context)
|
|
else:
|
|
_logger.error(gengo)
|
|
return True
|
|
|
|
def _sync_request(self, cr, uid, limit=GENGO_DEFAULT_LIMIT, context=None):
|
|
"""
|
|
This scheduler will send a job request to the gengo , which terms are
|
|
waiing to be translated and for which gengo_translation is enabled.
|
|
|
|
A special key 'gengo_language' can be passed in the context in order to
|
|
request only translations of that language only. Its value is the language
|
|
ID in openerp.
|
|
"""
|
|
if context is None:
|
|
context = {}
|
|
language_pool = self.pool.get('res.lang')
|
|
translation_pool = self.pool.get('ir.translation')
|
|
try:
|
|
#by default, the request will be made for all terms that needs it, whatever the language
|
|
lang_ids = language_pool.search(cr, uid, [], context=context)
|
|
if context.get('gengo_language'):
|
|
#but if this specific key is given, then we restrict the request on terms of this language only
|
|
lang_ids = [context.get('gengo_language')]
|
|
langs = [lang.code for lang in language_pool.browse(cr, uid, lang_ids, context=context)]
|
|
#search for the n first terms to translate
|
|
term_ids = translation_pool.search(cr, uid, [('state', '=', 'to_translate'), ('gengo_translation', 'in', ('machine','standard','pro','ultra')), ('lang', 'in', langs)], limit=limit, context=context)
|
|
if term_ids:
|
|
self._send_translation_terms(cr, uid, term_ids, context=context)
|
|
_logger.info("%s Translation terms have been posted to Gengo successfully", len(term_ids))
|
|
else:
|
|
_logger.info('No Translation terms to process.')
|
|
except Exception, e:
|
|
_logger.error("%s", e)
|
|
|
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|