[MERGE] merged trunk.

bzr revid: vmt@openerp.com-20110523084106-2sodtiqaprvqndjr
This commit is contained in:
Vo Minh Thu
2011-05-23 10:41:06 +02:00
11 changed files with 9842 additions and 358 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -20,6 +20,7 @@
##############################################################################
import time
import logging
from datetime import datetime
from dateutil.relativedelta import relativedelta
import netsvc
@@ -91,10 +92,13 @@ class ir_cron(osv.osv, netsvc.Agent):
if m and hasattr(m, func):
f = getattr(m, func)
try:
netsvc.log('cron', (cr.dbname,uid,'*',model,func)+tuple(args), channel=logging.DEBUG,
depth=(None if self._logger.isEnabledFor(logging.DEBUG_RPC_ANSWER) else 1), fn='object.execute')
f(cr, uid, *args)
except Exception, e:
cr.rollback()
self._logger.exception("Job call of self.pool.get('%s').%s(cr, uid, *%r) failed" % (model, func, args))
logger=logging.getLogger('cron')
logger.exception("Job call of self.pool.get('%s').%s(cr, uid, *%r) failed" % (model, func, args))
def _poolJobs(self, db_name, check=False):
+7 -7
View File
@@ -177,13 +177,13 @@ class ir_ui_menu(osv.osv):
def _action(self, cursor, user, ids, name, arg, context=None):
res = {}
values_obj = self.pool.get('ir.values')
value_ids = values_obj.search(cursor, user, [
ir_values_obj = self.pool.get('ir.values')
value_ids = ir_values_obj.search(cursor, user, [
('model', '=', self._name), ('key', '=', 'action'),
('key2', '=', 'tree_but_open'), ('res_id', 'in', ids)],
context=context)
values_action = {}
for value in values_obj.browse(cursor, user, value_ids, context=context):
for value in ir_values_obj.browse(cursor, user, value_ids, context=context):
values_action[value.res_id] = value.value
for menu_id in ids:
res[menu_id] = values_action.get(menu_id, False)
@@ -195,16 +195,16 @@ class ir_ui_menu(osv.osv):
ctx = context.copy()
if self.CONCURRENCY_CHECK_FIELD in ctx:
del ctx[self.CONCURRENCY_CHECK_FIELD]
values_obj = self.pool.get('ir.values')
values_ids = values_obj.search(cursor, user, [
ir_values_obj = self.pool.get('ir.values')
values_ids = ir_values_obj.search(cursor, user, [
('model', '=', self._name), ('key', '=', 'action'),
('key2', '=', 'tree_but_open'), ('res_id', '=', menu_id)],
context=context)
if values_ids:
values_obj.write(cursor, user, values_ids, {'value': value},
ir_values_obj.write(cursor, user, values_ids, {'value': value},
context=ctx)
else:
values_obj.create(cursor, user, {
ir_values_obj.create(cursor, user, {
'name': 'Menuitem',
'model': self._name,
'value': value,
+150 -45
View File
@@ -21,6 +21,7 @@
import locale
import logging
import re
from osv import fields, osv
from locale import localeconv
@@ -36,16 +37,25 @@ class lang(osv.osv):
_disallowed_datetime_patterns.remove('%y') # this one is in fact allowed, just not good practice
def install_lang(self, cr, uid, **args):
"""
This method is called from openerp/addons/base/base_data.xml to load
some language and set it as the default for every partners. The
language is set via tools.config by the RPC 'create' method on the
'db' object. This is a fragile solution and something else should be
found.
"""
lang = tools.config.get('lang')
if not lang:
return False
lang_ids = self.search(cr, uid, [('code','=', lang)])
values_obj = self.pool.get('ir.values')
ir_values_obj = self.pool.get('ir.values')
if not lang_ids:
lang_id = self.load_lang(cr, uid, lang)
default_value = values_obj.get(cr, uid, 'default', False, 'res.partner')
default_value = ir_values_obj.get(cr, uid, 'default', False, ['res.partner'])
if not default_value:
values_obj.set(cr, uid, 'default', False, 'lang', ['res.partner'], lang)
ir_values_obj.set(cr, uid, 'default', False, 'lang', ['res.partner'], lang)
return True
def load_lang(self, cr, uid, lang, lang_name=None):
@@ -181,46 +191,6 @@ class lang(osv.osv):
trans_obj.unlink(cr, uid, trans_ids, context=context)
return super(lang, self).unlink(cr, uid, ids, context=context)
def _group(self, cr, uid, ids, s, monetary=False, grouping=False, thousands_sep=''):
grouping = eval(grouping)
if not grouping:
return (s, 0)
result = ""
seps = 0
spaces = ""
if s[-1] == ' ':
sp = s.find(' ')
spaces = s[sp:]
s = s[:sp]
while s and grouping:
# if grouping is -1, we are done
if grouping[0] == -1:
break
# 0: re-use last group ad infinitum
elif grouping[0] != 0:
#process last group
group = grouping[0]
grouping = grouping[1:]
if result:
result = s[-group:] + thousands_sep + result
seps += 1
else:
result = s[-group:]
s = s[:-group]
if s and s[-1] not in "0123456789":
# the leading string is only spaces and signs
return s + result + spaces, seps
if not result:
return s + spaces, seps
if s:
result = s + thousands_sep + result
seps += 1
return result + spaces, seps
def format(self, cr, uid, ids, percent, value, grouping=False, monetary=False):
""" Format() will return the language-specific output for float values"""
@@ -228,6 +198,7 @@ class lang(osv.osv):
raise ValueError("format() must be given exactly one %char format specifier")
lang_grouping, thousands_sep, decimal_point = self._lang_data_get(cr, uid, ids[0], monetary)
eval_lang_grouping = eval(lang_grouping)
formatted = percent % value
# floats and decimal ints need special action!
@@ -236,7 +207,7 @@ class lang(osv.osv):
parts = formatted.split('.')
if grouping:
parts[0], seps = self._group(cr,uid,ids,parts[0], monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep)
parts[0], seps = intersperse(parts[0], eval_lang_grouping, thousands_sep)
formatted = decimal_point.join(parts)
while seps:
@@ -246,7 +217,7 @@ class lang(osv.osv):
seps -= 1
elif percent[-1] in 'diu':
if grouping:
formatted = self._group(cr,uid,ids,formatted, monetary=monetary, grouping=lang_grouping, thousands_sep=thousands_sep)[0]
formatted = intersperse(formatted, eval_lang_grouping, thousands_sep)[0]
return formatted
@@ -256,5 +227,139 @@ class lang(osv.osv):
lang()
def original_group(s, grouping, thousands_sep=''):
if not grouping:
return (s, 0)
result = ""
seps = 0
spaces = ""
if s[-1] == ' ':
sp = s.find(' ')
spaces = s[sp:]
s = s[:sp]
while s and grouping:
# if grouping is -1, we are done
if grouping[0] == -1:
break
# 0: re-use last group ad infinitum
elif grouping[0] != 0:
#process last group
group = grouping[0]
grouping = grouping[1:]
if result:
result = s[-group:] + thousands_sep + result
seps += 1
else:
result = s[-group:]
s = s[:-group]
if s and s[-1] not in "0123456789":
# the leading string is only spaces and signs
return s + result + spaces, seps
if not result:
return s + spaces, seps
if s:
result = s + thousands_sep + result
seps += 1
return result + spaces, seps
def split(l, counts):
"""
>>> split("hello world", [])
['hello world']
>>> split("hello world", [1])
['h', 'ello world']
>>> split("hello world", [2])
['he', 'llo world']
>>> split("hello world", [2,3])
['he', 'llo', ' world']
>>> split("hello world", [2,3,0])
['he', 'llo', ' wo', 'rld']
>>> split("hello world", [2,-1,3])
['he', 'llo world']
"""
res = []
saved_count = len(l) # count to use when encoutering a zero
for count in counts:
if count == -1:
break
if count == 0:
while l:
res.append(l[:saved_count])
l = l[saved_count:]
break
res.append(l[:count])
l = l[count:]
saved_count = count
if l:
res.append(l)
return res
intersperse_pat = re.compile('([^0-9]*)([^ ]*)(.*)')
def intersperse(string, counts, separator=''):
"""
See the asserts below for examples.
"""
left, rest, right = intersperse_pat.match(string).groups()
def reverse(s): return s[::-1]
splits = split(reverse(rest), counts)
res = separator.join(map(reverse, reverse(splits)))
return left + res + right, len(splits) > 0 and len(splits) -1 or 0
# TODO rewrite this with a unit test library
def _group_examples():
for g in [original_group, intersperse]:
# print "asserts on", g.func_name
assert g("", []) == ("", 0)
assert g("0", []) == ("0", 0)
assert g("012", []) == ("012", 0)
assert g("1", []) == ("1", 0)
assert g("12", []) == ("12", 0)
assert g("123", []) == ("123", 0)
assert g("1234", []) == ("1234", 0)
assert g("123456789", []) == ("123456789", 0)
assert g("&ab%#@1", []) == ("&ab%#@1", 0)
assert g("0", []) == ("0", 0)
assert g("0", [1]) == ("0", 0)
assert g("0", [2]) == ("0", 0)
assert g("0", [200]) == ("0", 0)
# breaks original_group:
if g.func_name == 'intersperse':
assert g("12345678", [0], '.') == ('12345678', 0)
assert g("", [1], '.') == ('', 0)
assert g("12345678", [1], '.') == ('1234567.8', 1)
assert g("12345678", [1], '.') == ('1234567.8', 1)
assert g("12345678", [2], '.') == ('123456.78', 1)
assert g("12345678", [2,1], '.') == ('12345.6.78', 2)
assert g("12345678", [2,0], '.') == ('12.34.56.78', 3)
assert g("12345678", [-1,2], '.') == ('12345678', 0)
assert g("12345678", [2,-1], '.') == ('123456.78', 1)
assert g("12345678", [2,0,1], '.') == ('12.34.56.78', 3)
assert g("12345678", [2,0,0], '.') == ('12.34.56.78', 3)
assert g("12345678", [2,0,-1], '.') == ('12.34.56.78', 3)
assert original_group("abc1234567xy", [2], '.') == ('abc1234567.xy', 1)
assert original_group("abc1234567xy8", [2], '.') == ('abc1234567xy8', 0) # difference here...
assert original_group("abc12", [3], '.') == ('abc12', 0)
assert original_group("abc12", [2], '.') == ('abc12', 0)
assert original_group("abc12", [1], '.') == ('abc1.2', 1)
assert intersperse("abc1234567xy", [2], '.') == ('abc1234567.xy', 1)
assert intersperse("abc1234567xy8", [2], '.') == ('abc1234567x.y8', 1) # ... w.r.t. here.
assert intersperse("abc12", [3], '.') == ('abc12', 0)
assert intersperse("abc12", [2], '.') == ('abc12', 0)
assert intersperse("abc12", [1], '.') == ('abc1.2', 1)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+12
View File
@@ -0,0 +1,12 @@
import sys
import openerp
# TODO this loop will be exposed as open_openerp_namespace()
# once trunk-cleaning-vmt is merged.
for k, v in list(sys.modules.items()):
if k.startswith('openerp.') and sys.modules.get(k[8:]) is None:
sys.modules[k[8:]] = v
import openerp.addons.base.res.res_lang as res_lang
res_lang._group_examples()
+6 -6
View File
@@ -24,16 +24,16 @@ import openerp.osv as osv
import openerp.pooler as pooler
def ir_set(cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None):
obj = pooler.get_pool(cr.dbname).get('ir.values')
return obj.set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
return ir_values_obj.set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
def ir_del(cr, uid, id):
obj = pooler.get_pool(cr.dbname).get('ir.values')
return obj.unlink(cr, uid, [id])
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
return ir_values_obj.unlink(cr, uid, [id])
def ir_get(cr, uid, key, key2, models, meta=False, context=None, res_id_req=False):
obj = pooler.get_pool(cr.dbname).get('ir.values')
res = obj.get(cr, uid, key, key2, models, meta=meta, context=context, res_id_req=res_id_req)
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
res = ir_values_obj.get(cr, uid, key, key2, models, meta=meta, context=context, res_id_req=res_id_req)
return res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+12 -9
View File
@@ -393,19 +393,22 @@ def replace_request_password(args):
args[2] = '*'
return args
class OpenERPDispatcher:
def log(self, title, msg, channel=logging.DEBUG_RPC, depth=None):
logger = logging.getLogger(title)
if logger.isEnabledFor(channel):
for line in pformat(msg, depth=depth).split('\n'):
logger.log(channel, line)
def log(title, msg, channel=logging.DEBUG_RPC, depth=None, fn=""):
logger = logging.getLogger(title)
if logger.isEnabledFor(channel):
indent=''
indent_after=' '*len(fn)
for line in (fn+pformat(msg, depth=depth)).split('\n'):
logger.log(channel, indent+line)
indent=indent_after
class OpenERPDispatcher:
def log(self, title, msg, channel=logging.DEBUG_RPC, depth=None, fn=""):
log(title, msg, channel=channel, depth=depth, fn=fn)
def dispatch(self, service_name, method, params):
try:
logger = logging.getLogger('result')
self.log('service', service_name)
self.log('method', method)
self.log('params', replace_request_password(params), depth=(None if logger.isEnabledFor(logging.DEBUG_RPC_ANSWER) else 1))
self.log('service', tuple(replace_request_password(params)), depth=(None if logger.isEnabledFor(logging.DEBUG_RPC_ANSWER) else 1), fn='%s.%s'%(service_name,method))
auth = getattr(self, 'auth_provider', None)
result = ExportService.getService(service_name).dispatch(method, auth, params)
self.log('result', result, channel=logging.DEBUG_RPC_ANSWER)
+7 -6
View File
@@ -838,14 +838,15 @@ class orm_template(object):
res = line[i] and float(line[i]) or 0.0
elif fields_def[field[len(prefix)]]['type'] == 'selection':
for key, val in fields_def[field[len(prefix)]]['selection']:
if line[i] in [tools.ustr(key), tools.ustr(val)]:
if tools.ustr(line[i]) in [tools.ustr(key), tools.ustr(val)]:
res = key
break
if line[i] and not res:
logger.notifyChannel("import", netsvc.LOG_WARNING,
_("key '%s' not found in selection field '%s'") % \
(line[i], field[len(prefix)]))
warning += [_("Key/value '%s' not found in selection field '%s'") % (line[i], field[len(prefix)])]
(tools.ustr(line[i]), tools.ustr(field[len(prefix)])))
warning += [_("Key/value '%s' not found in selection field '%s'") % (tools.ustr(line[i]), tools.ustr(field[len(prefix)]))]
else:
res = line[i]
@@ -3229,7 +3230,7 @@ class orm(orm_template):
self.check_access_rule(cr, uid, ids, 'unlink', context=context)
pool_model_data = self.pool.get('ir.model.data')
pool_ir_values = self.pool.get('ir.values')
ir_values_obj = self.pool.get('ir.values')
for sub_ids in cr.split_for_in_conditions(ids):
cr.execute('delete from ' + self._table + ' ' \
'where id IN %s', (sub_ids,))
@@ -3242,11 +3243,11 @@ class orm(orm_template):
pool_model_data.unlink(cr, uid, referenced_ids, context=context)
# For the same reason, removing the record relevant to ir_values
ir_value_ids = pool_ir_values.search(cr, uid,
ir_value_ids = ir_values_obj.search(cr, uid,
['|',('value','in',['%s,%s' % (self._name, sid) for sid in sub_ids]),'&',('res_id','in',list(sub_ids)),('model','=',self._name)],
context=context)
if ir_value_ids:
pool_ir_values.unlink(cr, uid, ir_value_ids, context=context)
ir_values_obj.unlink(cr, uid, ir_value_ids, context=context)
for order, object, store_ids, fields in result_store:
if object != self._name:
+3 -2
View File
@@ -304,9 +304,10 @@ form: module.record_id""" % (xml_id,)
self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids)
def _remove_ir_values(self, cr, name, value, model):
ir_value_ids = self.pool.get('ir.values').search(cr, self.uid, [('name','=',name),('value','=',value),('model','=',model)])
ir_values_obj = self.pool.get('ir.values')
ir_value_ids = ir_values_obj.search(cr, self.uid, [('name','=',name),('value','=',value),('model','=',model)])
if ir_value_ids:
self.pool.get('ir.values').unlink(cr, self.uid, ir_value_ids)
ir_values_obj.unlink(cr, self.uid, ir_value_ids)
self.pool.get('ir.model.data')._unlink(cr, self.uid, 'ir.values', ir_value_ids)
return True
+24 -18
View File
@@ -114,7 +114,7 @@ class RecordDictWrapper(dict):
records do not strictly behave like dict, so we force them to.
"""
def __init__(self, record):
self.record = record
self.record = record
def __getitem__(self, key):
if key in self.record:
return self.record[key]
@@ -174,10 +174,16 @@ class YamlInterpreter(object):
else:
module = self.module
checked_xml_id = xml_id
_, id = self.pool.get('ir.model.data').get_object_reference(self.cr, self.uid, module, checked_xml_id)
self.id_map[xml_id] = id
try:
_, id = self.pool.get('ir.model.data').get_object_reference(self.cr, self.uid, module, checked_xml_id)
self.id_map[xml_id] = id
except ValueError, e:
raise ValueError("""%s not found when processing %s.
This Yaml file appears to depend on missing data. This often happens for
tests that belong to a module's test suite and depend on each other.""" % (checked_xml_id, self.filename))
return id
def get_context(self, node, eval_dict):
context = self.context.copy()
if node.context:
@@ -186,7 +192,7 @@ class YamlInterpreter(object):
def isnoupdate(self, node):
return self.noupdate or node.noupdate or False
def _get_first_result(self, results, default=False):
if len(results):
value = results[0]
@@ -195,7 +201,7 @@ class YamlInterpreter(object):
else:
value = default
return value
def process_comment(self, node):
return node
@@ -419,7 +425,7 @@ class YamlInterpreter(object):
raise
else:
self.assert_report.record(True, python.severity)
def process_workflow(self, node):
workflow, values = node.items()[0]
if self.isnoupdate(workflow) and self.mode != 'init':
@@ -438,7 +444,7 @@ class YamlInterpreter(object):
local_context = {'obj': lambda x: value_model.browse(self.cr, self.uid, x, context=self.context)}
local_context.update(self.id_map)
id = eval(value['eval'], self.eval_context, local_context)
if workflow.uid is not None:
uid = workflow.uid
else:
@@ -450,7 +456,7 @@ class YamlInterpreter(object):
import openerp.netsvc as netsvc
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, workflow.model, id, workflow.action, self.cr)
def _eval_params(self, model, params):
args = []
for i, param in enumerate(params):
@@ -476,7 +482,7 @@ class YamlInterpreter(object):
value = param # scalar value
args.append(value)
return args
def process_function(self, node):
function, params = node.items()[0]
if self.isnoupdate(function) and self.mode != 'init':
@@ -489,7 +495,7 @@ class YamlInterpreter(object):
args = self._eval_params(function.model, params)
method = function.name
getattr(model, method)(self.cr, self.uid, *args)
def _set_group_values(self, node, values):
if node.groups:
group_names = node.groups.split(',')
@@ -570,7 +576,7 @@ class YamlInterpreter(object):
values['icon'] = node.icon
self._set_group_values(node, values)
pid = self.pool.get('ir.model.data')._update(self.cr, 1, \
'ir.ui.menu', self.module, values, node.id, mode=self.mode, \
noupdate=self.isnoupdate(node), res_id=res and res[0] or False)
@@ -640,7 +646,7 @@ class YamlInterpreter(object):
self.pool.get('ir.model.data')._unlink(self.cr, 1, node.model, ids)
else:
self.logger.log(logging.TEST, "Record not deleted.")
def process_url(self, node):
self.validate_xml_id(node.id)
@@ -657,7 +663,7 @@ class YamlInterpreter(object):
self.pool.get('ir.model.data').ir_set(self.cr, 1, 'action', \
keyword, node.url, ["ir.actions.url"], value, replace=replace, \
noupdate=self.isnoupdate(node), isobject=True, xml_id=node.id)
def process_ir_set(self, node):
if not self.mode == 'init':
return False
@@ -708,13 +714,13 @@ class YamlInterpreter(object):
replace = node.replace or True
self.pool.get('ir.model.data').ir_set(self.cr, 1, 'action', \
keyword, values['name'], [values['model']], value, replace=replace, isobject=True, xml_id=xml_id)
def process_none(self):
"""
Empty node or commented node should not pass silently.
"""
self._log_assert_failure(logging.WARNING, "You have an empty block in your tests.")
def process(self, yaml_string):
"""
@@ -732,7 +738,7 @@ class YamlInterpreter(object):
except Exception, e:
self.logger.exception(e)
raise
def _process_node(self, node):
if is_comment(node):
self.process_comment(node)
@@ -770,7 +776,7 @@ class YamlInterpreter(object):
self.process_none()
else:
raise YamlImportException("Can not process YAML block: %s" % node)
def _log(self, node, is_preceded_by_comment):
if is_comment(node):
is_preceded_by_comment = True