bzr revid: fp@tinyerp.com-20101221091005-zctt5bwplj0txipc
This commit is contained in:
Fabien Pinckaers
2010-12-21 10:10:05 +01:00
8 changed files with 273 additions and 76 deletions
+4
View File
@@ -225,6 +225,10 @@ def zip_directory(directory, b64enc=True, src=True):
archname = StringIO()
archive = PyZipFile(archname, "w", ZIP_DEFLATED)
# for Python 2.5, ZipFile.write() still expects 8-bit strings (2.6 converts to utf-8)
directory = tools.ustr(directory).encode('utf-8')
archive.writepy(directory)
_zippy(archive, directory, src=src)
archive.close()
+1 -20
View File
@@ -35,25 +35,6 @@ def one_in(setA, setB):
return True
return False
def cond(C, X, Y):
if C: return X
return Y
class many2many_unique(fields.many2many):
def set(self, cr, obj, id, name, values, user=None, context=None):
if not values:
return
val = values[:]
for act in values:
if act[0]==4:
cr.execute('SELECT * FROM '+self._rel+' \
WHERE '+self._id1+'=%s AND '+self._id2+'=%s', (id, act[1]))
if cr.fetchall():
val.remove(act)
return super(many2many_unique, self).set(cr, obj, id, name, val, user=user,
context=context)
class ir_ui_menu(osv.osv):
_name = 'ir.ui.menu'
@@ -298,7 +279,7 @@ class ir_ui_menu(osv.osv):
'sequence': fields.integer('Sequence'),
'child_id' : fields.one2many('ir.ui.menu', 'parent_id','Child IDs'),
'parent_id': fields.many2one('ir.ui.menu', 'Parent Menu', select=True),
'groups_id': many2many_unique('res.groups', 'ir_ui_menu_group_rel',
'groups_id': fields.many2many('res.groups', 'ir_ui_menu_group_rel',
'menu_id', 'gid', 'Groups', help="If you have groups, the visibility of this menu will be based on these groups. "\
"If this field is empty, OpenERP will compute visibility based on the related object's read access."),
'complete_name': fields.function(_get_full_name, method=True,
+5 -5
View File
@@ -173,15 +173,15 @@ class wkf_instance(osv.osv):
_log_access = False
_columns = {
'wkf_id': fields.many2one('workflow', 'Workflow', ondelete='cascade', select=True),
'res_id': fields.integer('Resource ID', select=True),
'res_type': fields.char('Resource Object', size=64, select=True),
'state': fields.char('State', size=32, select=True),
'res_id': fields.integer('Resource ID'),
'res_type': fields.char('Resource Object', size=64),
'state': fields.char('State', size=32),
}
def _auto_init(self, cr, context=None):
super(wkf_instance, self)._auto_init(cr, context)
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_id_res_type_state_index\'')
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_type_res_id_state_index\'')
if not cr.fetchone():
cr.execute('CREATE INDEX wkf_instance_res_id_res_type_state_index ON wkf_instance (res_id, res_type, state)')
cr.execute('CREATE INDEX wkf_instance_res_type_res_id_state_index ON wkf_instance (res_type, res_id, state)')
cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_instance_res_id_wkf_id_index\'')
if not cr.fetchone():
cr.execute('CREATE INDEX wkf_instance_res_id_wkf_id_index ON wkf_instance (res_id, wkf_id)')
+4 -3
View File
@@ -522,13 +522,13 @@ class module(osv.osv):
raise osv.except_osv(_('Error'), _('Module %s: Invalid Quality Certificate') % (mod.name,))
def list_web(self, cr, uid, context=None):
""" list_web(cr, uid, context) -> [module_name]
""" list_web(cr, uid, context) -> [(module_name, module_version)]
Lists all the currently installed modules with a web component.
Returns a list of addon names.
Returns a list of a tuple of addon names and addon versions.
"""
return [
module['name']
(module['name'], module['installed_version'])
for module in self.browse(cr, uid,
self.search(cr, uid,
[('web', '=', True),
@@ -561,6 +561,7 @@ class module(osv.osv):
'to web client', names)
return [
{'name': module.name,
'version': module.installed_version,
'depends': list(self._web_dependencies(
cr, uid, module, context=context)),
'content': addons.zip_directory(
+30 -32
View File
@@ -260,8 +260,11 @@ class browse_record(object):
else:
ref_obj, ref_id = result_line[field_name].split(',')
ref_id = long(ref_id)
obj = self._table.pool.get(ref_obj)
new_data[field_name] = browse_record(self._cr, self._uid, ref_id, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process)
if ref_id:
obj = self._table.pool.get(ref_obj)
new_data[field_name] = browse_record(self._cr, self._uid, ref_id, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process)
else:
new_data[field_name] = browse_null()
else:
new_data[field_name] = browse_null()
else:
@@ -2429,6 +2432,29 @@ class orm(orm_template):
if (val<>False) or (type(val)<>bool):
cr.execute(update_query, (ss[1](val), key))
def _check_selection_field_value(self, cr, uid, field, value, context=None):
"""Raise except_orm if value is not among the valid values for the selection field"""
if self._columns[field]._type == 'reference':
val_model, val_id_str = value.split(',', 1)
val_id = False
try:
val_id = long(val_id_str)
except ValueError:
pass
if not val_id:
raise except_orm(_('ValidateError'),
_('Invalid value for reference field "%s" (last part must be a non-zero integer): "%s"') % (field, value))
val = val_model
else:
val = value
if isinstance(self._columns[field].selection, (tuple, list)):
if val in dict(self._columns[field].selection):
return
elif val in dict(self._columns[field].selection(self, cr, uid, context=context)):
return
raise except_orm(_('ValidateError'),
_('The value "%s" for the field "%s" is not in the selection') % (value, field))
def _check_removed_columns(self, cr, log=False):
# iterate on the database columns to drop the NOT NULL constraints
# of fields which were required but have been removed (or will be added by another module)
@@ -3462,21 +3488,7 @@ class orm(orm_template):
if field in self._columns \
and hasattr(self._columns[field], 'selection') \
and vals[field]:
if self._columns[field]._type == 'reference':
val = vals[field].split(',')[0]
else:
val = vals[field]
if isinstance(self._columns[field].selection, (tuple, list)):
if val not in dict(self._columns[field].selection):
raise except_orm(_('ValidateError'),
_('The value "%s" for the field "%s" is not in the selection') \
% (vals[field], field))
else:
if val not in dict(self._columns[field].selection(
self, cr, user, context=context)):
raise except_orm(_('ValidateError'),
_('The value "%s" for the field "%s" is not in the selection') \
% (vals[field], field))
self._check_selection_field_value(cr, user, field, vals[field], context=context)
if self._log_access:
upd0.append('write_uid=%s')
@@ -3720,21 +3732,7 @@ class orm(orm_template):
if field in self._columns \
and hasattr(self._columns[field], 'selection') \
and vals[field]:
if self._columns[field]._type == 'reference':
val = vals[field].split(',')[0]
else:
val = vals[field]
if isinstance(self._columns[field].selection, (tuple, list)):
if val not in dict(self._columns[field].selection):
raise except_orm(_('ValidateError'),
_('The value "%s" for the field "%s" is not in the selection') \
% (vals[field], field))
else:
if val not in dict(self._columns[field].selection(
self, cr, user, context=context)):
raise except_orm(_('ValidateError'),
_('The value "%s" for the field "%s" is not in the selection') \
% (vals[field], field))
self._check_selection_field_value(cr, user, field, vals[field], context=context)
if self._log_access:
upd0 += ',create_uid,create_date'
upd1 += ',%s,now()'
+10 -6
View File
@@ -733,22 +733,26 @@ class _rml_flowable(object):
from reportlab.graphics.barcode import common
from reportlab.graphics.barcode import fourstate
from reportlab.graphics.barcode import usps
except Exception, e:
from reportlab.graphics.barcode import createBarcodeDrawing
except ImportError:
self._logger.warning("Cannot use barcode renderers:", exc_info=True)
return None
args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'int','quiet':'int','width':'unit','stop':'bool','bearers':'int','barWidth':'float','barHeight':'float'})
codes = {
'codabar': lambda x: common.Codabar(x, **args),
'code11': lambda x: common.Code11(x, **args),
'code128': lambda x: code128.Code128(x, **args),
'standard39': lambda x: code39.Standard39(x, **args),
'standard93': lambda x: code93.Standard93(x, **args),
'code128': lambda x: code128.Code128(str(x), **args),
'standard39': lambda x: code39.Standard39(str(x), **args),
'standard93': lambda x: code93.Standard93(str(x), **args),
'i2of5': lambda x: common.I2of5(x, **args),
'extended39': lambda x: code39.Extended39(x, **args),
'extended93': lambda x: code93.Extended93(x, **args),
'extended39': lambda x: code39.Extended39(str(x), **args),
'extended93': lambda x: code93.Extended93(str(x), **args),
'msi': lambda x: common.MSI(x, **args),
'fim': lambda x: usps.FIM(x, **args),
'postnet': lambda x: usps.POSTNET(x, **args),
'ean13': lambda x: createBarcodeDrawing('EAN13', value=str(x), **args),
'qrcode': lambda x: createBarcodeDrawing('QR', value=x, **args),
}
code = 'code128'
if node.get('code'):
+3 -3
View File
@@ -427,7 +427,7 @@ form: module.record_id""" % (xml_id,)
view_id = False
if rec.get('view_id'):
view_id = self.id_get(cr, rec.get('view_id','').encode('utf-8'))
domain = rec.get('domain','').encode('utf-8') or '{}'
domain = rec.get('domain','').encode('utf-8') or '[]'
res_model = rec.get('res_model','').encode('utf-8')
src_model = rec.get('src_model','').encode('utf-8')
view_type = rec.get('view_type','').encode('utf-8') or 'form'
@@ -705,8 +705,8 @@ form: module.record_id""" % (xml_id,)
' expected count: %d\n' \
' obtained count: %d\n' \
% (rec_string, count, len(ids))
self.logger.log(severity, msg)
sevval = getattr(logging, severity.upper())
self.logger.log(sevval, msg)
if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception
raise Exception('Severe assertion failure')
@@ -737,8 +737,8 @@ form: module.record_id""" % (xml_id,)
' expected value: %r\n' \
' obtained value: %r\n' \
% (rec_string, etree.tostring(test), expected_value, expression_value)
self.logger.log(severity, msg)
sevval = getattr(logging, severity.upper())
self.logger.log(sevval, msg)
if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception
raise Exception('Severe assertion failure')
+216 -7
View File
@@ -28,16 +28,21 @@
import netsvc
import tools
import logging
import pooler
from tools.safe_eval import safe_eval
from subprocess import Popen, PIPE
import os
import tempfile
def try_report(cr, uid, rname, ids, data=None, context=None):
def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None):
""" Try to render a report <rname> with contents of ids
This function should also check for common pitfalls of reports.
"""
log = logging.getLogger('tools.test_reports')
if our_module:
log = logging.getLogger('tests.%s' % our_module)
else:
log = logging.getLogger('tools.test_reports')
if data is None:
data = {}
if context is None:
@@ -46,16 +51,16 @@ def try_report(cr, uid, rname, ids, data=None, context=None):
rname_s = rname[7:]
else:
rname_s = rname
log.debug("Trying %s.create(%r)", rname, ids)
log.log(netsvc.logging.TEST, " - Trying %s.create(%r)", rname, ids)
res = netsvc.LocalService(rname).create(cr, uid, ids, data, context)
if not isinstance(res, tuple):
raise RuntimeError("Result of %s.create() should be a (data,format) tuple, now it is a %s" % \
(rname, type(res)))
(res_data, res_format) = res
if not res_data:
raise ValueError("Report %s produced an empty result!" % rname)
if tools.config['test_report_directory']:
file(os.path.join(tools.config['test_report_directory'], rname+ '.'+res_format), 'wb+').write(res_data)
@@ -63,7 +68,7 @@ def try_report(cr, uid, rname, ids, data=None, context=None):
if res_format == 'pdf':
if res_data[:5] != '%PDF-':
raise ValueError("Report %s produced a non-pdf header, %r" % (rname, res_data[:10]))
res_text = False
try:
fd, rfname = tempfile.mkstemp(suffix=res_format)
@@ -86,6 +91,210 @@ def try_report(cr, uid, rname, ids, data=None, context=None):
pass
else:
log.warning("Report %s produced a \"%s\" chunk, cannot examine it", rname, res_format)
return False
log.log(netsvc.logging.TEST, " + Report %s produced correctly.", rname)
return True
def try_report_action(cr, uid, action_id, active_model=None, active_ids=None,
wiz_data=None, wiz_buttons=None,
context=None, our_module=None):
"""Take an ir.action.act_window and follow it until a report is produced
:param action_id: the integer id of an action, or a reference to xml id
of the act_window (can search [our_module.]+xml_id
:param active_model, active_ids: call the action as if it had been launched
from that model+ids (tree/form view action)
:param wiz_data: a dictionary of values to use in the wizard, if needed.
They will override (or complete) the default values of the
wizard form.
:param wiz_buttons: a list of button names, or button icon strings, which
should be preferred to press during the wizard.
Eg. 'OK' or 'gtk-print'
:param our_module: the name of the calling module (string), like 'account'
"""
if not our_module and isinstance(action_id, basestring):
if '.' in action_id:
our_module = action_id.split('.', 1)[0]
if context is None:
context = {}
else:
context = context.copy() # keep it local
# TODO context fill-up
pool = pooler.get_pool(cr.dbname)
if our_module:
log = logging.getLogger('tests.%s' % our_module)
else:
log = logging.getLogger('tools.test_reports')
def log_test(msg, *args):
log.log(netsvc.logging.TEST, " - " + msg, *args)
datas = {}
if active_model:
datas['model'] = active_model
if active_ids:
datas['ids'] = active_ids
if not wiz_buttons:
wiz_buttons = []
if isinstance(action_id, basestring):
if '.' in action_id:
act_module, act_xmlid = action_id.split('.', 1)
else:
if not our_module:
raise ValueError('You cannot only specify action_id "%s" without a module name' % action_id)
act_module = our_module
act_xmlid = action_id
act_model, act_id = pool.get('ir.model.data').get_object_reference(cr, uid, act_module, act_xmlid)
else:
assert isinstance(action_id, (long, int))
act_model = 'ir.action.act_window' # assume that
act_id = action_id
act_xmlid = '<%s>' % act_id
def _exec_action(action, datas, context):
# taken from client/modules/action/main.py:84 _exec_action()
if isinstance(action, bool) or 'type' not in action:
return
# Updating the context : Adding the context of action in order to use it on Views called from buttons
if datas.get('id',False):
context.update( {'active_id': datas.get('id',False), 'active_ids': datas.get('ids',[]), 'active_model': datas.get('model',False)})
context.update(safe_eval(action.get('context','{}'), context.copy()))
if action['type'] in ['ir.actions.act_window', 'ir.actions.submenu']:
for key in ('res_id', 'res_model', 'view_type', 'view_mode',
'limit', 'auto_refresh', 'search_view', 'auto_search', 'search_view_id'):
datas[key] = action.get(key, datas.get(key, None))
view_id = False
if action.get('views', []):
if isinstance(action['views'],list):
view_id = action['views'][0][0]
datas['view_mode']= action['views'][0][1]
else:
if action.get('view_id', False):
view_id = action['view_id'][0]
elif action.get('view_id', False):
view_id = action['view_id'][0]
assert datas['res_model'], "Cannot use the view without a model"
# Here, we have a view that we need to emulate
log_test("will emulate a %s view: %s#%s",
action['view_type'], datas['res_model'], view_id or '?')
view_res = pool.get(datas['res_model']).fields_view_get(cr, uid, view_id, action['view_type'], context)
assert view_res and view_res.get('arch'), "Did not return any arch for the view"
view_data = {}
if view_res.get('fields',{}).keys():
view_data = pool.get(datas['res_model']).default_get(cr, uid, view_res['fields'].keys(), context)
if datas.get('form'):
view_data.update(datas.get('form'))
if wiz_data:
view_data.update(wiz_data)
log.debug("View data is: %r", view_data)
action_name = action.get('name')
try:
from xml.dom import minidom
cancel_found = False
buttons = []
dom_doc = minidom.parseString(view_res['arch'])
if not action_name:
action_name = dom_doc.documentElement.getAttribute('name')
for button in dom_doc.getElementsByTagName('button'):
button_weight = 0
if button.getAttribute('special') == 'cancel':
cancel_found = True
continue
if button.getAttribute('icon') == 'gtk-cancel':
cancel_found = True
continue
if button.getAttribute('default_focus') == '1':
button_weight += 20
if button.getAttribute('string') in wiz_buttons:
button_weight += 30
elif button.getAttribute('icon') in wiz_buttons:
button_weight += 10
string = button.getAttribute('string') or '?%s' % len(buttons)
buttons.append( { 'name': button.getAttribute('name'),
'string': string,
'type': button.getAttribute('type'),
'weight': button_weight,
})
except Exception, e:
log.warning("Cannot resolve the view arch and locate the buttons!", exc_info=True)
raise AssertionError(e.args[0])
if not datas['res_id']:
# it is probably an orm_memory object, we need to create
# an instance
datas['res_id'] = pool.get(datas['res_model']).create(cr, uid, view_data, context)
if not buttons:
raise AssertionError("view form doesn't have any buttons to press!")
buttons.sort(key=lambda b: b['weight'])
log.debug('Buttons are: %s', ', '.join([ '%s: %d' % (b['string'], b['weight']) for b in buttons]))
res = None
while buttons and not res:
b = buttons.pop()
log_test("in the \"%s\" form, I will press the \"%s\" button.", action_name, b['string'])
if not b['type']:
log_test("the \"%s\" button has no type, cannot use it", b['string'])
continue
if b['type'] == 'object':
#there we are! press the button!
fn = getattr(pool.get(datas['res_model']), b['name'])
if not fn:
log.error("The %s model doesn't have a %s attribute!", datas['res_model'], b['name'])
continue
res = fn(cr, uid, [datas['res_id'],], context)
break
else:
log.warning("in the \"%s\" form, the \"%s\" button has unknown type %s",
action_name, b['string'], b['type'])
return res
elif action['type']=='ir.actions.report.xml':
if 'window' in datas:
del datas['window']
if not datas:
datas = action.get('datas',{})
datas = datas.copy()
ids = datas.get('ids')
if 'ids' in datas:
del datas['ids']
res = try_report(cr, uid, 'report.'+action['report_name'], ids, datas, context, our_module=our_module)
return res
else:
raise Exception("Cannot handle action of type %s" % act_model)
log_test("will be using %s action %s #%d", act_model, act_xmlid, act_id)
action = pool.get(act_model).read(cr, uid, act_id, context=context)
assert action, "Could not read action %s[%s]" %(act_model, act_id)
loop = 0
while action:
loop += 1
# This part tries to emulate the loop of the Gtk client
if loop > 100:
log.error("Passed %d loops, giving up", loop)
raise Exception("Too many loops at action")
log_test("it is an %s action at loop #%d", action.get('type', 'unknown'), loop)
result = _exec_action(action, datas, context)
if not isinstance(result, dict):
break
datas = result.get('datas', {})
if datas:
del result['datas']
action = result
return True
#eof
#eof