diff options
| author | Christophe Simonis <christophe@tinyerp.com> | 2008-07-22 16:24:36 +0200 |
|---|---|---|
| committer | Christophe Simonis <christophe@tinyerp.com> | 2008-07-22 16:24:36 +0200 |
| commit | 2dd9723b8b3d38df8c8962300ab67afffa5cdaaa (patch) | |
| tree | 13dbea91b265af5ad775af16efcdef4ae6ef962c | |
| parent | c5c7c2ef07b346b72eb5a48d6e8cc7a433fba2e8 (diff) | |
convert tabs to 4 spaces
bzr revid: christophe@tinyerp.com-20080722142436-143iu4ryy47w3av0
104 files changed, 16552 insertions, 16552 deletions
diff --git a/bin/addons/__init__.py b/bin/addons/__init__.py index cefd6859e6c..a28c87d1c34 100644 --- a/bin/addons/__init__.py +++ b/bin/addons/__init__.py @@ -45,310 +45,310 @@ logger = netsvc.Logger() opj = os.path.join -_ad = os.path.abspath(opj(tools.config['root_path'], 'addons')) # default addons path (base) -ad = os.path.abspath(tools.config['addons_path']) # alternate addons path +_ad = os.path.abspath(opj(tools.config['root_path'], 'addons')) # default addons path (base) +ad = os.path.abspath(tools.config['addons_path']) # alternate addons path sys.path.insert(1, _ad) if ad != _ad: - sys.path.insert(1, ad) + sys.path.insert(1, ad) class Graph(dict): - def addNode(self, name, deps): - max_depth, father = 0, None - for n in [Node(x, self) for x in deps]: - if n.depth >= max_depth: - father = n - max_depth = n.depth - if father: - father.addChild(name) - else: - Node(name, self) - - def __iter__(self): - level = 0 - done = Set(self.keys()) - while done: - level_modules = [(name, module) for name, module in self.items() if module.depth==level] - for name, module in level_modules: - done.remove(name) - yield module - level += 1 + def addNode(self, name, deps): + max_depth, father = 0, None + for n in [Node(x, self) for x in deps]: + if n.depth >= max_depth: + father = n + max_depth = n.depth + if father: + father.addChild(name) + else: + Node(name, self) + + def __iter__(self): + level = 0 + done = Set(self.keys()) + while done: + level_modules = [(name, module) for name, module in self.items() if module.depth==level] + for name, module in level_modules: + done.remove(name) + yield module + level += 1 class Singleton(object): - def __new__(cls, name, graph): - if name in graph: - inst = graph[name] - else: - inst = object.__new__(cls) - inst.name = name - graph[name] = inst - return inst + def __new__(cls, name, graph): + if name in graph: + inst = graph[name] + else: + inst = object.__new__(cls) + inst.name = name + graph[name] = inst + return inst class Node(Singleton): - def __init__(self, name, graph): - self.graph = graph - if not hasattr(self, 'childs'): - self.childs = [] - if not hasattr(self, 'depth'): - self.depth = 0 - - def addChild(self, name): - node = Node(name, self.graph) - node.depth = self.depth + 1 - if node not in self.childs: - self.childs.append(node) - for attr in ('init', 'update', 'demo'): - if hasattr(self, attr): - setattr(node, attr, True) - self.childs.sort(lambda x,y: cmp(x.name, y.name)) - - def hasChild(self, name): - return Node(name, self.graph) in self.childs or \ - bool([c for c in self.childs if c.hasChild(name)]) - - def __setattr__(self, name, value): - super(Singleton, self).__setattr__(name, value) - if name in ('init', 'update', 'demo'): - tools.config[name][self.name] = 1 - for child in self.childs: - setattr(child, name, value) - if name == 'depth': - for child in self.childs: - setattr(child, name, value + 1) - - def __iter__(self): - return itertools.chain(iter(self.childs), *map(iter, self.childs)) - - def __str__(self): - return self._pprint() - - def _pprint(self, depth=0): - s = '%s\n' % self.name - for c in self.childs: - s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1)) - return s + def __init__(self, name, graph): + self.graph = graph + if not hasattr(self, 'childs'): + self.childs = [] + if not hasattr(self, 'depth'): + self.depth = 0 + + def addChild(self, name): + node = Node(name, self.graph) + node.depth = self.depth + 1 + if node not in self.childs: + self.childs.append(node) + for attr in ('init', 'update', 'demo'): + if hasattr(self, attr): + setattr(node, attr, True) + self.childs.sort(lambda x,y: cmp(x.name, y.name)) + + def hasChild(self, name): + return Node(name, self.graph) in self.childs or \ + bool([c for c in self.childs if c.hasChild(name)]) + + def __setattr__(self, name, value): + super(Singleton, self).__setattr__(name, value) + if name in ('init', 'update', 'demo'): + tools.config[name][self.name] = 1 + for child in self.childs: + setattr(child, name, value) + if name == 'depth': + for child in self.childs: + setattr(child, name, value + 1) + + def __iter__(self): + return itertools.chain(iter(self.childs), *map(iter, self.childs)) + + def __str__(self): + return self._pprint() + + def _pprint(self, depth=0): + s = '%s\n' % self.name + for c in self.childs: + s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1)) + return s def get_module_path(module): - """Return the path of the given module. - """ + """Return the path of the given module. + """ - if os.path.exists(opj(ad, module)) or os.path.exists(opj(ad, '%s.zip' % module)): - return opj(ad, module) + if os.path.exists(opj(ad, module)) or os.path.exists(opj(ad, '%s.zip' % module)): + return opj(ad, module) - if os.path.exists(opj(_ad, module)) or os.path.exists(opj(_ad, '%s.zip' % module)): - return opj(_ad, module) + if os.path.exists(opj(_ad, module)) or os.path.exists(opj(_ad, '%s.zip' % module)): + return opj(_ad, module) - raise IOError, 'Module not found : %s' % module + raise IOError, 'Module not found : %s' % module def get_module_resource(module, *args): - """Return the full path of a resource of the given module. + """Return the full path of a resource of the given module. - @param module: the module - @param args: the resource path components + @param module: the module + @param args: the resource path components - @return: absolute path to the resource - """ - return opj(get_module_path(module), *args) + @return: absolute path to the resource + """ + return opj(get_module_path(module), *args) def get_modules(): - """Returns the list of module names - """ + """Returns the list of module names + """ - module_list = os.listdir(ad) - module_names = [os.path.basename(m) for m in module_list] - module_list += [m for m in os.listdir(_ad) if m not in module_names] + module_list = os.listdir(ad) + module_names = [os.path.basename(m) for m in module_list] + module_list += [m for m in os.listdir(_ad) if m not in module_names] - return module_list + return module_list def create_graph(module_list, force=None): - if not force: - force=[] - graph = Graph() - packages = [] - - for module in module_list: - if module[-4:]=='.zip': - module = module[:-4] - mod_path = get_module_path(module) - terp_file = get_module_resource(module, '__terp__.py') - if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path): - try: - info = eval(tools.file_open(terp_file).read()) - except: - logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon:%s:eval file %s' % (module, terp_file)) - raise - if info.get('installable', True): - packages.append((module, info.get('depends', []), info)) - - current,later = Set([p for p, dep, data in packages]), Set() - while packages and current > later: - package, deps, datas = packages[0] - - # if all dependencies of 'package' are already in the graph, add 'package' in the graph - if reduce(lambda x,y: x and y in graph, deps, True): - if not package in current: - packages.pop(0) - continue - later.clear() - current.remove(package) - graph.addNode(package, deps) - node = Node(package, graph) - node.datas = datas - for kind in ('init', 'demo', 'update'): - if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force: - setattr(node, kind, True) - else: - later.add(package) - packages.append((package, deps, datas)) - packages.pop(0) - - for package in later: - logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon:%s:Unmet dependency' % package) - - return graph + if not force: + force=[] + graph = Graph() + packages = [] + + for module in module_list: + if module[-4:]=='.zip': + module = module[:-4] + mod_path = get_module_path(module) + terp_file = get_module_resource(module, '__terp__.py') + if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path): + try: + info = eval(tools.file_open(terp_file).read()) + except: + logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon:%s:eval file %s' % (module, terp_file)) + raise + if info.get('installable', True): + packages.append((module, info.get('depends', []), info)) + + current,later = Set([p for p, dep, data in packages]), Set() + while packages and current > later: + package, deps, datas = packages[0] + + # if all dependencies of 'package' are already in the graph, add 'package' in the graph + if reduce(lambda x,y: x and y in graph, deps, True): + if not package in current: + packages.pop(0) + continue + later.clear() + current.remove(package) + graph.addNode(package, deps) + node = Node(package, graph) + node.datas = datas + for kind in ('init', 'demo', 'update'): + if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force: + setattr(node, kind, True) + else: + later.add(package) + packages.append((package, deps, datas)) + packages.pop(0) + + for package in later: + logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon:%s:Unmet dependency' % package) + + return graph def init_module_objects(cr, module_name, obj_list): - pool = pooler.get_pool(cr.dbname) - logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:creating or updating database tables' % module_name) - for obj in obj_list: - if hasattr(obj, 'init'): - obj.init(cr) - obj._auto_init(cr, {'module': module_name}) - cr.commit() + pool = pooler.get_pool(cr.dbname) + logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:creating or updating database tables' % module_name) + for obj in obj_list: + if hasattr(obj, 'init'): + obj.init(cr) + obj._auto_init(cr, {'module': module_name}) + cr.commit() def load_module_graph(cr, graph, status=None, **kwargs): - # **kwargs is passed directly to convert_xml_import - if not status: - status={} - - status = status.copy() - package_todo = [] - statusi = 0 - for package in graph: - status['progress'] = (float(statusi)+0.1)/len(graph) - m = package.name - logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s' % m) - sys.stdout.flush() - pool = pooler.get_pool(cr.dbname) - modules = pool.instanciate(m, cr) - cr.execute('select state, demo from ir_module_module where name=%s', (m,)) - (package_state, package_demo) = (cr.rowcount and cr.fetchone()) or ('uninstalled', False) - idref = {} - status['progress'] = (float(statusi)+0.4)/len(graph) - if hasattr(package, 'init') or hasattr(package, 'update') or package_state in ('to install', 'to upgrade'): - init_module_objects(cr, m, modules) - for kind in ('init', 'update'): - for filename in package.datas.get('%s_xml' % kind, []): - mode = 'update' - if hasattr(package, 'init') or package_state=='to install': - mode = 'init' - logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:loading %s' % (m, filename)) - name, ext = os.path.splitext(filename) - if ext == '.csv': - tools.convert_csv_import(cr, m, os.path.basename(filename), tools.file_open(opj(m, filename)).read(), idref, mode=mode) - elif ext == '.sql': - queries = tools.file_open(opj(m, filename)).read().split(';') - for query in queries: - new_query = ' '.join(query.split()) - if new_query: - cr.execute(new_query) - else: - tools.convert_xml_import(cr, m, tools.file_open(opj(m, filename)), idref, mode=mode, **kwargs) - if hasattr(package, 'demo') or (package_demo and package_state != 'installed'): - status['progress'] = (float(statusi)+0.75)/len(graph) - for xml in package.datas.get('demo_xml', []): - name, ext = os.path.splitext(xml) - logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:loading %s' % (m, xml)) - if ext == '.csv': - tools.convert_csv_import(cr, m, os.path.basename(xml), tools.file_open(opj(m, xml)).read(), idref, noupdate=True) - else: - tools.convert_xml_import(cr, m, tools.file_open(opj(m, xml)), idref, noupdate=True, **kwargs) - cr.execute('update ir_module_module set demo=%s where name=%s', (True, package.name)) - package_todo.append(package.name) - cr.execute("update ir_module_module set state='installed' where state in ('to upgrade', 'to install') and name=%s", (package.name,)) - cr.commit() - statusi+=1 - - pool = pooler.get_pool(cr.dbname) - cr.execute('select * from ir_model where state=%s', ('manual',)) - for model in cr.dictfetchall(): - pool.get('ir.model').instanciate(cr, 1, model['model'], {}) - - pool.get('ir.model.data')._process_end(cr, 1, package_todo) - cr.commit() + # **kwargs is passed directly to convert_xml_import + if not status: + status={} + + status = status.copy() + package_todo = [] + statusi = 0 + for package in graph: + status['progress'] = (float(statusi)+0.1)/len(graph) + m = package.name + logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s' % m) + sys.stdout.flush() + pool = pooler.get_pool(cr.dbname) + modules = pool.instanciate(m, cr) + cr.execute('select state, demo from ir_module_module where name=%s', (m,)) + (package_state, package_demo) = (cr.rowcount and cr.fetchone()) or ('uninstalled', False) + idref = {} + status['progress'] = (float(statusi)+0.4)/len(graph) + if hasattr(package, 'init') or hasattr(package, 'update') or package_state in ('to install', 'to upgrade'): + init_module_objects(cr, m, modules) + for kind in ('init', 'update'): + for filename in package.datas.get('%s_xml' % kind, []): + mode = 'update' + if hasattr(package, 'init') or package_state=='to install': + mode = 'init' + logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:loading %s' % (m, filename)) + name, ext = os.path.splitext(filename) + if ext == '.csv': + tools.convert_csv_import(cr, m, os.path.basename(filename), tools.file_open(opj(m, filename)).read(), idref, mode=mode) + elif ext == '.sql': + queries = tools.file_open(opj(m, filename)).read().split(';') + for query in queries: + new_query = ' '.join(query.split()) + if new_query: + cr.execute(new_query) + else: + tools.convert_xml_import(cr, m, tools.file_open(opj(m, filename)), idref, mode=mode, **kwargs) + if hasattr(package, 'demo') or (package_demo and package_state != 'installed'): + status['progress'] = (float(statusi)+0.75)/len(graph) + for xml in package.datas.get('demo_xml', []): + name, ext = os.path.splitext(xml) + logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:loading %s' % (m, xml)) + if ext == '.csv': + tools.convert_csv_import(cr, m, os.path.basename(xml), tools.file_open(opj(m, xml)).read(), idref, noupdate=True) + else: + tools.convert_xml_import(cr, m, tools.file_open(opj(m, xml)), idref, noupdate=True, **kwargs) + cr.execute('update ir_module_module set demo=%s where name=%s', (True, package.name)) + package_todo.append(package.name) + cr.execute("update ir_module_module set state='installed' where state in ('to upgrade', 'to install') and name=%s", (package.name,)) + cr.commit() + statusi+=1 + + pool = pooler.get_pool(cr.dbname) + cr.execute('select * from ir_model where state=%s', ('manual',)) + for model in cr.dictfetchall(): + pool.get('ir.model').instanciate(cr, 1, model['model'], {}) + + pool.get('ir.model.data')._process_end(cr, 1, package_todo) + cr.commit() def register_classes(): - module_list = get_modules() - for package in create_graph(module_list): - m = package.name - logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:registering classes' % m) - sys.stdout.flush() - - mod_path = get_module_path(m) - if not os.path.isfile(mod_path+'.zip'): - # XXX must restrict to only addons paths - imp.load_module(m, *imp.find_module(m)) - else: - import zipimport - try: - zimp = zipimport.zipimporter(mod_path+'.zip') - zimp.load_module(m) - except zipimport.ZipImportError: - logger.notifyChannel('init', netsvc.LOG_ERROR, 'Couldn\'t find module %s' % m) + module_list = get_modules() + for package in create_graph(module_list): + m = package.name + logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:registering classes' % m) + sys.stdout.flush() + + mod_path = get_module_path(m) + if not os.path.isfile(mod_path+'.zip'): + # XXX must restrict to only addons paths + imp.load_module(m, *imp.find_module(m)) + else: + import zipimport + try: + zimp = zipimport.zipimporter(mod_path+'.zip') + zimp.load_module(m) + except zipimport.ZipImportError: + logger.notifyChannel('init', netsvc.LOG_ERROR, 'Couldn\'t find module %s' % m) def load_modules(db, force_demo=False, status=None, update_module=False): - if not status: - status={} - cr = db.cursor() - force = [] - if force_demo: - force.append('demo') - if update_module: - cr.execute("select name from ir_module_module where state in ('installed', 'to install', 'to upgrade','to remove')") - else: - cr.execute("select name from ir_module_module where state in ('installed', 'to upgrade', 'to remove')") - module_list = [name for (name,) in cr.fetchall()] - graph = create_graph(module_list, force) - report = tools.assertion_report() - load_module_graph(cr, graph, status, report=report) - if report.get_report(): - logger.notifyChannel('init', netsvc.LOG_INFO, 'assert:%s' % report) - - for kind in ('init', 'demo', 'update'): - tools.config[kind]={} - - cr.commit() - if update_module: - cr.execute("select id,name from ir_module_module where state in ('to remove')") - for mod_id, mod_name in cr.fetchall(): - pool = pooler.get_pool(cr.dbname) - cr.execute('select model,res_id from ir_model_data where not noupdate and module=%s order by id desc', (mod_name,)) - for rmod,rid in cr.fetchall(): - # - # TO BE Improved: - # I can not use the class_pool has _table could be defined in __init__ - # and I can not use the pool has the module could not be loaded in the pool - # - uid = 1 - pool.get(rmod).unlink(cr, uid, [rid]) - cr.commit() - # - # TODO: remove menu without actions of childs - # - cr.execute('''delete from - ir_ui_menu - where - (id not in (select parent_id from ir_ui_menu where parent_id is not null)) - and - (id not in (select res_id from ir_values where model='ir.ui.menu')) - and - (id not in (select res_id from ir_model_data where model='ir.ui.menu'))''') - - cr.execute("update ir_module_module set state=%s where state in ('to remove')", ('uninstalled', )) - cr.commit() - pooler.restart_pool(cr.dbname) - cr.close() + if not status: + status={} + cr = db.cursor() + force = [] + if force_demo: + force.append('demo') + if update_module: + cr.execute("select name from ir_module_module where state in ('installed', 'to install', 'to upgrade','to remove')") + else: + cr.execute("select name from ir_module_module where state in ('installed', 'to upgrade', 'to remove')") + module_list = [name for (name,) in cr.fetchall()] + graph = create_graph(module_list, force) + report = tools.assertion_report() + load_module_graph(cr, graph, status, report=report) + if report.get_report(): + logger.notifyChannel('init', netsvc.LOG_INFO, 'assert:%s' % report) + + for kind in ('init', 'demo', 'update'): + tools.config[kind]={} + + cr.commit() + if update_module: + cr.execute("select id,name from ir_module_module where state in ('to remove')") + for mod_id, mod_name in cr.fetchall(): + pool = pooler.get_pool(cr.dbname) + cr.execute('select model,res_id from ir_model_data where not noupdate and module=%s order by id desc', (mod_name,)) + for rmod,rid in cr.fetchall(): + # + # TO BE Improved: + # I can not use the class_pool has _table could be defined in __init__ + # and I can not use the pool has the module could not be loaded in the pool + # + uid = 1 + pool.get(rmod).unlink(cr, uid, [rid]) + cr.commit() + # + # TODO: remove menu without actions of childs + # + cr.execute('''delete from + ir_ui_menu + where + (id not in (select parent_id from ir_ui_menu where parent_id is not null)) + and + (id not in (select res_id from ir_values where model='ir.ui.menu')) + and + (id not in (select res_id from ir_model_data where model='ir.ui.menu'))''') + + cr.execute("update ir_module_module set state=%s where state in ('to remove')", ('uninstalled', )) + cr.commit() + pooler.restart_pool(cr.dbname) + cr.close() diff --git a/bin/addons/base/__terp__.py b/bin/addons/base/__terp__.py index 0de434c7e31..8903596f5db 100644 --- a/bin/addons/base/__terp__.py +++ b/bin/addons/base/__terp__.py @@ -26,44 +26,44 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ############################################################################### { - "name" : "Base", - "version" : "1.0", - "author" : "Tiny", - "website" : "http://tinyerp.com", - "category" : "Generic Modules/Base", - "description": "The kernel of Tiny ERP, needed for all installation.", - "depends" : [], - "init_xml" : [ - "base_data.xml", - "base_menu.xml", - ], - "demo_xml" : [ - "base_demo.xml", - "res/partner/partner_demo.xml", - "res/partner/crm_demo.xml", - ], - "update_xml" : [ - "base_update.xml", - "ir/wizard/wizard_menu_view.xml", - "ir/ir.xml", - "ir/workflow/workflow_view.xml", - "module/module_data.xml", - "module/module_wizard.xml", - "module/module_view.xml", - "module/module_report.xml", - "res/res_request_view.xml", - "res/res_lang_view.xml", - "res/partner/partner_report.xml", - "res/partner/partner_view.xml", - "res/partner/partner_wizard.xml", - "res/bank_view.xml", - "res/country_view.xml", - "res/res_currency_view.xml", - "res/partner/crm_view.xml", - "res/partner/partner_data.xml", - "res/ir_property_view.xml", - "base_security.xml", - ], - "active": True, - "installable": True, + "name" : "Base", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com", + "category" : "Generic Modules/Base", + "description": "The kernel of Tiny ERP, needed for all installation.", + "depends" : [], + "init_xml" : [ + "base_data.xml", + "base_menu.xml", + ], + "demo_xml" : [ + "base_demo.xml", + "res/partner/partner_demo.xml", + "res/partner/crm_demo.xml", + ], + "update_xml" : [ + "base_update.xml", + "ir/wizard/wizard_menu_view.xml", + "ir/ir.xml", + "ir/workflow/workflow_view.xml", + "module/module_data.xml", + "module/module_wizard.xml", + "module/module_view.xml", + "module/module_report.xml", + "res/res_request_view.xml", + "res/res_lang_view.xml", + "res/partner/partner_report.xml", + "res/partner/partner_view.xml", + "res/partner/partner_wizard.xml", + "res/bank_view.xml", + "res/country_view.xml", + "res/res_currency_view.xml", + "res/partner/crm_view.xml", + "res/partner/partner_data.xml", + "res/ir_property_view.xml", + "base_security.xml", + ], + "active": True, + "installable": True, } diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py index a4a7ae71d1c..9e5f6435408 100644 --- a/bin/addons/base/ir/ir_actions.py +++ b/bin/addons/base/ir/ir_actions.py @@ -32,263 +32,263 @@ import tools import time class actions(osv.osv): - _name = 'ir.actions.actions' - _table = 'ir_actions' - _columns = { - 'name': fields.char('Action Name', required=True, size=64), - 'type': fields.char('Action Type', required=True, size=32), - 'usage': fields.char('Action Usage', size=32), - 'parent_id': fields.many2one('ir.actions.server', 'Parent Action'), - } - _defaults = { - 'usage': lambda *a: False, - } + _name = 'ir.actions.actions' + _table = 'ir_actions' + _columns = { + 'name': fields.char('Action Name', required=True, size=64), + 'type': fields.char('Action Type', required=True, size=32), + 'usage': fields.char('Action Usage', size=32), + 'parent_id': fields.many2one('ir.actions.server', 'Parent Action'), + } + _defaults = { + 'usage': lambda *a: False, + } actions() class report_custom(osv.osv): - _name = 'ir.actions.report.custom' - _table = 'ir_act_report_custom' - _sequence = 'ir_actions_id_seq' - _columns = { - 'name': fields.char('Report Name', size=64, required=True, translate=True), - 'type': fields.char('Report Type', size=32, required=True), - 'model':fields.char('Model', size=64, required=True), - 'report_id': fields.integer('Report Ref.', required=True), - 'usage': fields.char('Action Usage', size=32), - 'multi': fields.boolean('On multiple doc.', help="If set to true, the action will not be displayed on the right toolbar of a form views.") - } - _defaults = { - 'multi': lambda *a: False, - 'type': lambda *a: 'ir.actions.report.custom', - } + _name = 'ir.actions.report.custom' + _table = 'ir_act_report_custom' + _sequence = 'ir_actions_id_seq' + _columns = { + 'name': fields.char('Report Name', size=64, required=True, translate=True), + 'type': fields.char('Report Type', size=32, required=True), + 'model':fields.char('Model', size=64, required=True), + 'report_id': fields.integer('Report Ref.', required=True), + 'usage': fields.char('Action Usage', size=32), + 'multi': fields.boolean('On multiple doc.', help="If set to true, the action will not be displayed on the right toolbar of a form views.") + } + _defaults = { + 'multi': lambda *a: False, + 'type': lambda *a: 'ir.actions.report.custom', + } report_custom() class report_xml(osv.osv): - def _report_content(self, cursor, user, ids, name, arg, context=None): - res = {} - for report in self.browse(cursor, user, ids, context=context): - data = report[name + '_data'] - if not data and report[name[:-8]]: - try: - fp = tools.file_open(report[name[:-8]], mode='rb') - data = fp.read() - except: - data = False - res[report.id] = data - return res + def _report_content(self, cursor, user, ids, name, arg, context=None): + res = {} + for report in self.browse(cursor, user, ids, context=context): + data = report[name + '_data'] + if not data and report[name[:-8]]: + try: + fp = tools.file_open(report[name[:-8]], mode='rb') + data = fp.read() + except: + data = False + res[report.id] = data + return res - def _report_content_inv(self, cursor, user, id, name, value, arg, context=None): - self.write(cursor, user, id, {name+'_data': value}, context=context) + def _report_content_inv(self, cursor, user, id, name, value, arg, context=None): + self.write(cursor, user, id, {name+'_data': value}, context=context) - def _report_sxw(self, cursor, user, ids, name, arg, context=None): - res = {} - for report in self.browse(cursor, user, ids, context=context): - if report.report_rml: - res[report.id] = report.report_rml.replace('.rml', '.sxw') - else: - res[report.id] = False - return res + def _report_sxw(self, cursor, user, ids, name, arg, context=None): + res = {} + for report in self.browse(cursor, user, ids, context=context): + if report.report_rml: + res[report.id] = report.report_rml.replace('.rml', '.sxw') + else: + res[report.id] = False + return res - _name = 'ir.actions.report.xml' - _table = 'ir_act_report_xml' - _sequence = 'ir_actions_id_seq' - _columns = { - 'name': fields.char('Name', size=64, required=True, translate=True), - 'type': fields.char('Report Type', size=32, required=True), - 'model': fields.char('Model', size=64, required=True), - 'report_name': fields.char('Internal Name', size=64, required=True), - 'report_xsl': fields.char('XSL path', size=256), - 'report_xml': fields.char('XML path', size=256), - 'report_rml': fields.char('RML path', size=256, - help="The .rml path of the file or NULL if the content is in report_rml_content"), - 'report_sxw': fields.function(_report_sxw, method=True, type='char', - string='SXW path'), - 'report_sxw_content_data': fields.binary('SXW content'), - 'report_rml_content_data': fields.binary('RML content'), - 'report_sxw_content': fields.function(_report_content, - fnct_inv=_report_content_inv, method=True, - type='binary', string='SXW content',), - 'report_rml_content': fields.function(_report_content, - fnct_inv=_report_content_inv, method=True, - type='binary', string='RML content'), - 'auto': fields.boolean('Automatic XSL:RML', required=True), - 'usage': fields.char('Action Usage', size=32), - 'header': fields.boolean('Add RML header', - help="Add or not the coporate RML header"), - 'multi': fields.boolean('On multiple doc.', - help="If set to true, the action will not be displayed on the right toolbar of a form views."), - 'report_type': fields.selection([ - ('pdf', 'pdf'), - ('html', 'html'), - ('raw', 'raw'), - ('sxw', 'sxw'), - ], string='Type', required=True), - 'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups') - - } - _defaults = { - 'type': lambda *a: 'ir.actions.report.xml', - 'multi': lambda *a: False, - 'auto': lambda *a: True, - 'header': lambda *a: True, - 'report_sxw_content': lambda *a: False, - 'report_type': lambda *a: 'pdf', - } + _name = 'ir.actions.report.xml' + _table = 'ir_act_report_xml' + _sequence = 'ir_actions_id_seq' + _columns = { + 'name': fields.char('Name', size=64, required=True, translate=True), + 'type': fields.char('Report Type', size=32, required=True), + 'model': fields.char('Model', size=64, required=True), + 'report_name': fields.char('Internal Name', size=64, required=True), + 'report_xsl': fields.char('XSL path', size=256), + 'report_xml': fields.char('XML path', size=256), + 'report_rml': fields.char('RML path', size=256, + help="The .rml path of the file or NULL if the content is in report_rml_content"), + 'report_sxw': fields.function(_report_sxw, method=True, type='char', + string='SXW path'), + 'report_sxw_content_data': fields.binary('SXW content'), + 'report_rml_content_data': fields.binary('RML content'), + 'report_sxw_content': fields.function(_report_content, + fnct_inv=_report_content_inv, method=True, + type='binary', string='SXW content',), + 'report_rml_content': fields.function(_report_content, + fnct_inv=_report_content_inv, method=True, + type='binary', string='RML content'), + 'auto': fields.boolean('Automatic XSL:RML', required=True), + 'usage': fields.char('Action Usage', size=32), + 'header': fields.boolean('Add RML header', + help="Add or not the coporate RML header"), + 'multi': fields.boolean('On multiple doc.', + help="If set to true, the action will not be displayed on the right toolbar of a form views."), + 'report_type': fields.selection([ + ('pdf', 'pdf'), + ('html', 'html'), + ('raw', 'raw'), + ('sxw', 'sxw'), + ], string='Type', required=True), + 'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups') + + } + _defaults = { + 'type': lambda *a: 'ir.actions.report.xml', + 'multi': lambda *a: False, + 'auto': lambda *a: True, + 'header': lambda *a: True, + 'report_sxw_content': lambda *a: False, + 'report_type': lambda *a: 'pdf', + } report_xml() class act_window(osv.osv): - _name = 'ir.actions.act_window' - _table = 'ir_act_window' - _sequence = 'ir_actions_id_seq' + _name = 'ir.actions.act_window' + _table = 'ir_act_window' + _sequence = 'ir_actions_id_seq' - def _views_get_fnc(self, cr, uid, ids, name, arg, context={}): - res={} - for act in self.browse(cr, uid, ids): - res[act.id]=[(view.view_id.id, view.view_mode) for view in act.view_ids] - if (not act.view_ids): - modes = act.view_mode.split(',') - find = False - if act.view_id.id: - res[act.id].append((act.view_id.id, act.view_id.type)) - for t in modes: - if act.view_id and (t == act.view_id.type) and not find: - find = True - continue - res[act.id].append((False, t)) + def _views_get_fnc(self, cr, uid, ids, name, arg, context={}): + res={} + for act in self.browse(cr, uid, ids): + res[act.id]=[(view.view_id.id, view.view_mode) for view in act.view_ids] + if (not act.view_ids): + modes = act.view_mode.split(',') + find = False + if act.view_id.id: + res[act.id].append((act.view_id.id, act.view_id.type)) + for t in modes: + if act.view_id and (t == act.view_id.type) and not find: + find = True + continue + res[act.id].append((False, t)) - if 'calendar' not in modes: - mobj = self.pool.get(act.res_model) - if mobj._date_name in mobj._columns: - res[act.id].append((False, 'calendar')) - return res + if 'calendar' not in modes: + mobj = self.pool.get(act.res_model) + if mobj._date_name in mobj._columns: + res[act.id].append((False, 'calendar')) + return res - _columns = { - 'name': fields.char('Action Name', size=64, translate=True), - 'type': fields.char('Action Type', size=32, required=True), - 'view_id': fields.many2one('ir.ui.view', 'View Ref.', ondelete='cascade'), - 'domain': fields.char('Domain Value', size=250), - 'context': fields.char('Context Value', size=250), - 'res_model': fields.char('Model', size=64), - 'src_model': fields.char('Source model', size=64), - 'target': fields.selection([('current','Current Window'),('new','New Window')], 'Target Window'), - 'view_type': fields.selection((('tree','Tree'),('form','Form')),string='Type of view'), - 'view_mode': fields.char('Mode of view', size=250), - 'usage': fields.char('Action Usage', size=32), - 'view_ids': fields.one2many('ir.actions.act_window.view', 'act_window_id', 'Views'), - 'views': fields.function(_views_get_fnc, method=True, type='binary', string='Views'), - 'limit': fields.integer('Limit', help='Default limit for the list view'), - 'auto_refresh': fields.integer('Auto-Refresh', - help='Add an auto-refresh on the view'), - } - _defaults = { - 'type': lambda *a: 'ir.actions.act_window', - 'view_type': lambda *a: 'form', - 'view_mode': lambda *a: 'tree,form', - 'context': lambda *a: '{}', - 'limit': lambda *a: 80, - 'target': lambda *a: 'current', - 'auto_refresh': lambda *a: 0, - } + _columns = { + 'name': fields.char('Action Name', size=64, translate=True), + 'type': fields.char('Action Type', size=32, required=True), + 'view_id': fields.many2one('ir.ui.view', 'View Ref.', ondelete='cascade'), + 'domain': fields.char('Domain Value', size=250), + 'context': fields.char('Context Value', size=250), + 'res_model': fields.char('Model', size=64), + 'src_model': fields.char('Source model', size=64), + 'target': fields.selection([('current','Current Window'),('new','New Window')], 'Target Window'), + 'view_type': fields.selection((('tree','Tree'),('form','Form')),string='Type of view'), + 'view_mode': fields.char('Mode of view', size=250), + 'usage': fields.char('Action Usage', size=32), + 'view_ids': fields.one2many('ir.actions.act_window.view', 'act_window_id', 'Views'), + 'views': fields.function(_views_get_fnc, method=True, type='binary', string='Views'), + 'limit': fields.integer('Limit', help='Default limit for the list view'), + 'auto_refresh': fields.integer('Auto-Refresh', + help='Add an auto-refresh on the view'), + } + _defaults = { + 'type': lambda *a: 'ir.actions.act_window', + 'view_type': lambda *a: 'form', + 'view_mode': lambda *a: 'tree,form', + 'context': lambda *a: '{}', + 'limit': lambda *a: 80, + 'target': lambda *a: 'current', + 'auto_refresh': lambda *a: 0, + } act_window() class act_window_view(osv.osv): - _name = 'ir.actions.act_window.view' - _table = 'ir_act_window_view' - _rec_name = 'view_id' - _columns = { - 'sequence': fields.integer('Sequence'), - 'view_id': fields.many2one('ir.ui.view', 'View'), - 'view_mode': fields.selection(( - ('tree', 'Tree'), - ('form', 'Form'), - ('graph', 'Graph'), - ('calendar', 'Calendar')), string='Type of view', required=True), - 'act_window_id': fields.many2one('ir.actions.act_window', 'Action', ondelete='cascade'), - 'multi': fields.boolean('On multiple doc.', - help="If set to true, the action will not be displayed on the right toolbar of a form views."), - } - _defaults = { - 'multi': lambda *a: False, - } - _order = 'sequence' + _name = 'ir.actions.act_window.view' + _table = 'ir_act_window_view' + _rec_name = 'view_id' + _columns = { + 'sequence': fields.integer('Sequence'), + 'view_id': fields.many2one('ir.ui.view', 'View'), + 'view_mode': fields.selection(( + ('tree', 'Tree'), + ('form', 'Form'), + ('graph', 'Graph'), + ('calendar', 'Calendar')), string='Type of view', required=True), + 'act_window_id': fields.many2one('ir.actions.act_window', 'Action', ondelete='cascade'), + 'multi': fields.boolean('On multiple doc.', + help="If set to true, the action will not be displayed on the right toolbar of a form views."), + } + _defaults = { + 'multi': lambda *a: False, + } + _order = 'sequence' act_window_view() class act_wizard(osv.osv): - _name = 'ir.actions.wizard' - _table = 'ir_act_wizard' - _sequence = 'ir_actions_id_seq' - _columns = { - 'name': fields.char('Wizard info', size=64, required=True, translate=True), - 'type': fields.char('Action type', size=32, required=True), - 'wiz_name': fields.char('Wizard name', size=64, required=True), - 'multi': fields.boolean('Action on multiple doc.', help="If set to true, the wizard will not be displayed on the right toolbar of a form views."), - 'groups_id': fields.many2many('res.groups', 'res_groups_wizard_rel', 'uid', 'gid', 'Groups') - } - _defaults = { - 'type': lambda *a: 'ir.actions.wizard', - 'multi': lambda *a: False, - } + _name = 'ir.actions.wizard' + _table = 'ir_act_wizard' + _sequence = 'ir_actions_id_seq' + _columns = { + 'name': fields.char('Wizard info', size=64, required=True, translate=True), + 'type': fields.char('Action type', size=32, required=True), + 'wiz_name': fields.char('Wizard name', size=64, required=True), + 'multi': fields.boolean('Action on multiple doc.', help="If set to true, the wizard will not be displayed on the right toolbar of a form views."), + 'groups_id': fields.many2many('res.groups', 'res_groups_wizard_rel', 'uid', 'gid', 'Groups') + } + _defaults = { + 'type': lambda *a: 'ir.actions.wizard', + 'multi': lambda *a: False, + } act_wizard() class act_url(osv.osv): - _name = 'ir.actions.url' - _table = 'ir_act_url' - _sequence = 'ir_actions_id_seq' - _columns = { - 'name': fields.char('Action Name', size=64, translate=True), - 'type': fields.char('Action Type', size=32, required=True), - 'url': fields.text('Action Url',required=True), - 'target': fields.selection(( - ('new', 'New Window'), - ('self', 'This Window')), - 'Action Target', required=True - ) - } - _defaults = { - 'type': lambda *a: 'ir.actions.act_url', - 'target': lambda *a: 'new' - } + _name = 'ir.actions.url' + _table = 'ir_act_url' + _sequence = 'ir_actions_id_seq' + _columns = { + 'name': fields.char('Action Name', size=64, translate=True), + 'type': fields.char('Action Type', size=32, required=True), + 'url': fields.text('Action Url',required=True), + 'target': fields.selection(( + ('new', 'New Window'), + ('self', 'This Window')), + 'Action Target', required=True + ) + } + _defaults = { + 'type': lambda *a: 'ir.actions.act_url', + 'target': lambda *a: 'new' + } act_url() # # Actions that are run on the server side # class actions_server(osv.osv): - _name = 'ir.actions.server' - _table = 'ir_act_server' - _sequence = 'ir_actions_id_seq' - _columns = { - 'name': fields.char('Action Name', required=True, size=64), - 'state': fields.selection([ - ('python','Python Code'), - ('dummy','Dummy'), - ('trigger','Trigger'), - ('email','Email'), - ('sms','SMS'), - ('object_create','Create Object'), - ('object_write','Write Object'), - ('client_action','Client Action'), - ('other','Others Actions'), - ], 'Action State', required=True, size=32), - 'code': fields.text('Python Code'), - 'sequence': fields.integer('Sequence'), - 'model_id': fields.many2one('ir.model', 'Model', required=True), - 'trigger_name': fields.char('Trigger Name', size=128), - 'trigger_object': fields.char('Trigger Object', size=128), - 'trigger_object_id': fields.char('Trigger Object ID', size=128), - 'message': fields.text('Message', translate=True), - 'address': fields.char('Email Address', size=128), - 'child_ids': fields.one2many('ir.actions.actions', 'parent_id', 'Others Actions'), - 'usage': fields.char('Action Usage', size=32), - 'type': fields.char('Report Type', size=32, required=True), - } - _defaults = { - 'state': lambda *a: 'dummy', - 'type': lambda *a: 'ir.actions.server', - 'sequence': lambda *a: 0, - 'code': lambda *a: """# You can use the following variables + _name = 'ir.actions.server' + _table = 'ir_act_server' + _sequence = 'ir_actions_id_seq' + _columns = { + 'name': fields.char('Action Name', required=True, size=64), + 'state': fields.selection([ + ('python','Python Code'), + ('dummy','Dummy'), + ('trigger','Trigger'), + ('email','Email'), + ('sms','SMS'), + ('object_create','Create Object'), + ('object_write','Write Object'), + ('client_action','Client Action'), + ('other','Others Actions'), + ], 'Action State', required=True, size=32), + 'code': fields.text('Python Code'), + 'sequence': fields.integer('Sequence'), + 'model_id': fields.many2one('ir.model', 'Model', required=True), + 'trigger_name': fields.char('Trigger Name', size=128), + 'trigger_object': fields.char('Trigger Object', size=128), + 'trigger_object_id': fields.char('Trigger Object ID', size=128), + 'message': fields.text('Message', translate=True), + 'address': fields.char('Email Address', size=128), + 'child_ids': fields.one2many('ir.actions.actions', 'parent_id', 'Others Actions'), + 'usage': fields.char('Action Usage', size=32), + 'type': fields.char('Report Type', size=32, required=True), + } + _defaults = { + 'state': lambda *a: 'dummy', + 'type': lambda *a: 'ir.actions.server', + 'sequence': lambda *a: 0, + 'code': lambda *a: """# You can use the following variables # - object # - object2 # - time @@ -297,43 +297,43 @@ class actions_server(osv.osv): # - ids # If you plan to return an action, assign: action = {...} """ - } - # - # Context should contains: - # ids : original ids - # id : current id of the object - # OUT: - # False : Finnished correctly - # ACTION_ID : Action to launch - def run(self, cr, uid, ids, context={}): - for action in self.browse(cr, uid, ids, context): - if action.state=='python': - localdict = { - 'self': self.pool.get(action.model_id.model), - 'context': context, - 'time': time, - 'ids': ids, - 'cr': cr, - 'uid': uid - } - print action.code - exec action.code in localdict - print localdict.keys() - if 'action' in localdict: - return localdict['action'] - return False + } + # + # Context should contains: + # ids : original ids + # id : current id of the object + # OUT: + # False : Finnished correctly + # ACTION_ID : Action to launch + def run(self, cr, uid, ids, context={}): + for action in self.browse(cr, uid, ids, context): + if action.state=='python': + localdict = { + 'self': self.pool.get(action.model_id.model), + 'context': context, + 'time': time, + 'ids': ids, + 'cr': cr, + 'uid': uid + } + print action.code + exec action.code in localdict + print localdict.keys() + if 'action' in localdict: + return localdict['action'] + return False actions_server() class act_window_close(osv.osv): - _name = 'ir.actions.act_window_close' - _table = 'ir_actions' - _sequence = 'ir_actions_id_seq' - _columns = { - 'name': fields.char('Action Name', size=64, translate=True), - 'type': fields.char('Action Type', size=32, required=True), - } - _defaults = { - 'type': lambda *a: 'ir.actions.act_window_close', - } + _name = 'ir.actions.act_window_close' + _table = 'ir_actions' + _sequence = 'ir_actions_id_seq' + _columns = { + 'name': fields.char('Action Name', size=64, translate=True), + 'type': fields.char('Action Type', size=32, required=True), + } + _defaults = { + 'type': lambda *a: 'ir.actions.act_window_close', + } act_window_close() diff --git a/bin/addons/base/ir/ir_attachment.py b/bin/addons/base/ir/ir_attachment.py index 4c505462473..f4e931826b1 100644 --- a/bin/addons/base/ir/ir_attachment.py +++ b/bin/addons/base/ir/ir_attachment.py @@ -30,16 +30,16 @@ from osv import fields,osv class ir_attachment(osv.osv): - _name = 'ir.attachment' - _columns = { - 'name': fields.char('Attachment Name',size=64, required=True), - 'datas': fields.binary('Data'), - 'datas_fname': fields.char('Data Filename',size=64), - 'description': fields.text('Description'), - # Not required due to the document module ! - 'res_model': fields.char('Resource Model',size=64, readonly=True), - 'res_id': fields.integer('Resource ID', readonly=True), - 'link': fields.char('Link', size=256) - } + _name = 'ir.attachment' + _columns = { + 'name': fields.char('Attachment Name',size=64, required=True), + 'datas': fields.binary('Data'), + 'datas_fname': fields.char('Data Filename',size=64), + 'description': fields.text('Description'), + # Not required due to the document module ! + 'res_model': fields.char('Resource Model',size=64, readonly=True), + 'res_id': fields.integer('Resource ID', readonly=True), + 'link': fields.char('Link', size=256) + } ir_attachment() diff --git a/bin/addons/base/ir/ir_board.py b/bin/addons/base/ir/ir_board.py index 380e7c183a6..f8bf28a4fe1 100644 --- a/bin/addons/base/ir/ir_board.py +++ b/bin/addons/base/ir/ir_board.py @@ -30,12 +30,12 @@ from osv import fields,osv class board(osv.osv): - _name = 'ir.board' - def create(self, cr, user, vals, context={}): - return False - def copy(self, cr, uid, id, default=None, context={}): - return False - _columns = { - 'name': fields.char('Board', size=64), - } + _name = 'ir.board' + def create(self, cr, user, vals, context={}): + return False + def copy(self, cr, uid, id, default=None, context={}): + return False + _columns = { + 'name': fields.char('Board', size=64), + } board() diff --git a/bin/addons/base/ir/ir_cron.py b/bin/addons/base/ir/ir_cron.py index 1f7376b8b28..d04251ae4d3 100644 --- a/bin/addons/base/ir/ir_cron.py +++ b/bin/addons/base/ir/ir_cron.py @@ -28,13 +28,13 @@ ############################################################################## # # SPEC: Execute "model.function(*eval(args))" periodically -# date : date to execute the job or NULL if directly -# delete_after: delete the ir.cron entry after execution -# interval_* : period -# max_repeat : number of execution or NULL if endlessly +# date : date to execute the job or NULL if directly +# delete_after: delete the ir.cron entry after execution +# interval_* : period +# max_repeat : number of execution or NULL if endlessly # # TODO: -# Error treatment: exception, request, ... -> send request to uid +# Error treatment: exception, request, ... -> send request to uid # from mx import DateTime @@ -47,85 +47,85 @@ from osv import fields,osv next_wait = 60 _intervalTypes = { - 'work_days': lambda interval: DateTime.RelativeDateTime(days=interval), - 'days': lambda interval: DateTime.RelativeDateTime(days=interval), - 'hours': lambda interval: DateTime.RelativeDateTime(hours=interval), - 'weeks': lambda interval: DateTime.RelativeDateTime(days=7*interval), - 'months': lambda interval: DateTime.RelativeDateTime(months=interval), - 'minutes': lambda interval: DateTime.RelativeDateTime(minutes=interval), + 'work_days': lambda interval: DateTime.RelativeDateTime(days=interval), + 'days': lambda interval: DateTime.RelativeDateTime(days=interval), + 'hours': lambda interval: DateTime.RelativeDateTime(hours=interval), + 'weeks': lambda interval: DateTime.RelativeDateTime(days=7*interval), + 'months': lambda interval: DateTime.RelativeDateTime(months=interval), + 'minutes': lambda interval: DateTime.RelativeDateTime(minutes=interval), } class ir_cron(osv.osv, netsvc.Agent): - _name = "ir.cron" - _columns = { - 'name': fields.char('Name', size=60, required=True), - 'user_id': fields.many2one('res.users', 'User', required=True), - 'active': fields.boolean('Active'), - 'interval_number': fields.integer('Interval Number'), - 'interval_type': fields.selection( [('minutes', 'Minutes'), - ('hours', 'Hours'), ('work_days','Work Days'), ('days', 'Days'),('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'), - 'numbercall': fields.integer('Number of calls', help='Number of time the function is called,\na negative number indicates that the function will always be called'), - 'doall' : fields.boolean('Repeat missed'), - 'nextcall' : fields.datetime('Next call date', required=True), - 'model': fields.char('Model', size=64), - 'function': fields.char('Function', size=64), - 'args': fields.text('Arguments'), - 'priority': fields.integer('Priority', help='0=Very Urgent\n10=Not urgent') - } + _name = "ir.cron" + _columns = { + 'name': fields.char('Name', size=60, required=True), + 'user_id': fields.many2one('res.users', 'User', required=True), + 'active': fields.boolean('Active'), + 'interval_number': fields.integer('Interval Number'), + 'interval_type': fields.selection( [('minutes', 'Minutes'), + ('hours', 'Hours'), ('work_days','Work Days'), ('days', 'Days'),('weeks', 'Weeks'), ('months', 'Months')], 'Interval Unit'), + 'numbercall': fields.integer('Number of calls', help='Number of time the function is called,\na negative number indicates that the function will always be called'), + 'doall' : fields.boolean('Repeat missed'), + 'nextcall' : fields.datetime('Next call date', required=True), + 'model': fields.char('Model', size=64), + 'function': fields.char('Function', size=64), + 'args': fields.text('Arguments'), + 'priority': fields.integer('Priority', help='0=Very Urgent\n10=Not urgent') + } - _defaults = { - 'nextcall' : lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'priority' : lambda *a: 5, - 'user_id' : lambda obj,cr,uid,context: uid, - 'interval_number' : lambda *a: 1, - 'interval_type' : lambda *a: 'months', - 'numbercall' : lambda *a: 1, - 'active' : lambda *a: 1, - 'doall' : lambda *a: 1 - } + _defaults = { + 'nextcall' : lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'priority' : lambda *a: 5, + 'user_id' : lambda obj,cr,uid,context: uid, + 'interval_number' : lambda *a: 1, + 'interval_type' : lambda *a: 'months', + 'numbercall' : lambda *a: 1, + 'active' : lambda *a: 1, + 'doall' : lambda *a: 1 + } - def _callback(self, cr, uid, model, func, args): - args = (args or []) and eval(args) - m=self.pool.get(model) - if m and hasattr(m, func): - f = getattr(m, func) - f(cr, uid, *args) + def _callback(self, cr, uid, model, func, args): + args = (args or []) and eval(args) + m=self.pool.get(model) + if m and hasattr(m, func): + f = getattr(m, func) + f(cr, uid, *args) - def _poolJobs(self, db_name, check=False): - now = DateTime.now() - #FIXME: multidb. Solution: a l'instanciation d'une nouvelle connection bd (ds pooler) fo que j'instancie - # un nouveau pooljob avec comme parametre la bd - try: - cr = pooler.get_db(db_name).cursor() - except: - return False + def _poolJobs(self, db_name, check=False): + now = DateTime.now() + #FIXME: multidb. Solution: a l'instanciation d'une nouvelle connection bd (ds pooler) fo que j'instancie + # un nouveau pooljob avec comme parametre la bd + try: + cr = pooler.get_db(db_name).cursor() + except: + return False - try: - cr.execute('select * from ir_cron where numbercall<>0 and active and nextcall<=now() order by priority') - for job in cr.dictfetchall(): - nextcall = DateTime.strptime(job['nextcall'], '%Y-%m-%d %H:%M:%S') - numbercall = job['numbercall'] - - ok = False - while nextcall<now and numbercall: - if numbercall > 0: - numbercall -= 1 - if not ok or job['doall']: - self._callback(cr, job['user_id'], job['model'], job['function'], job['args']) - if numbercall: - nextcall += _intervalTypes[job['interval_type']](job['interval_number']) - ok = True - addsql='' - if not numbercall: - addsql = ', active=False' - cr.execute("update ir_cron set nextcall=%s, numbercall=%d"+addsql+" where id=%d", (nextcall.strftime('%Y-%m-%d %H:%M:%S'), numbercall, job['id'])) - cr.commit() - finally: - cr.close() - # - # Can be improved to do at the min(min(nextcalls), time()+next_wait) - # But is this an improvement ? - # - if not check: - self.setAlarm(self._poolJobs, int(time.time())+next_wait, [db_name]) + try: + cr.execute('select * from ir_cron where numbercall<>0 and active and nextcall<=now() order by priority') + for job in cr.dictfetchall(): + nextcall = DateTime.strptime(job['nextcall'], '%Y-%m-%d %H:%M:%S') + numbercall = job['numbercall'] + + ok = False + while nextcall<now and numbercall: + if numbercall > 0: + numbercall -= 1 + if not ok or job['doall']: + self._callback(cr, job['user_id'], job['model'], job['function'], job['args']) + if numbercall: + nextcall += _intervalTypes[job['interval_type']](job['interval_number']) + ok = True + addsql='' + if not numbercall: + addsql = ', active=False' + cr.execute("update ir_cron set nextcall=%s, numbercall=%d"+addsql+" where id=%d", (nextcall.strftime('%Y-%m-%d %H:%M:%S'), numbercall, job['id'])) + cr.commit() + finally: + cr.close() + # + # Can be improved to do at the min(min(nextcalls), time()+next_wait) + # But is this an improvement ? + # + if not check: + self.setAlarm(self._poolJobs, int(time.time())+next_wait, [db_name]) ir_cron() diff --git a/bin/addons/base/ir/ir_default.py b/bin/addons/base/ir/ir_default.py index bec15da4573..dccb410f7ce 100644 --- a/bin/addons/base/ir/ir_default.py +++ b/bin/addons/base/ir/ir_default.py @@ -30,26 +30,26 @@ from osv import fields,osv class ir_default(osv.osv): - _name = 'ir.default' - _columns = { - 'field_tbl': fields.char('Model',size=64), - 'field_name': fields.char('Model field',size=64), - 'value': fields.char('Default Value',size=64), - 'uid': fields.many2one('res.users', 'Users'), - 'page': fields.char('View',size=64), - 'ref_table': fields.char('Table Ref.',size=64), - 'ref_id': fields.integer('ID Ref.',size=64), - 'company_id': fields.many2one('res.company','Company') - } + _name = 'ir.default' + _columns = { + 'field_tbl': fields.char('Model',size=64), + 'field_name': fields.char('Model field',size=64), + 'value': fields.char('Default Value',size=64), + 'uid': fields.many2one('res.users', 'Users'), + 'page': fields.char('View',size=64), + 'ref_table': fields.char('Table Ref.',size=64), + 'ref_id': fields.integer('ID Ref.',size=64), + 'company_id': fields.many2one('res.company','Company') + } - def _get_company_id(self, cr, uid, context={}): - res = self.pool.get('res.users').read(cr, uid, [uid], ['company_id'], context=context) - if res and res[0]['company_id']: - return res[0]['company_id'][0] - return False + def _get_company_id(self, cr, uid, context={}): + res = self.pool.get('res.users').read(cr, uid, [uid], ['company_id'], context=context) + if res and res[0]['company_id']: + return res[0]['company_id'][0] + return False - _defaults = { - 'company_id': _get_company_id, - } + _defaults = { + 'company_id': _get_company_id, + } ir_default() diff --git a/bin/addons/base/ir/ir_exports.py b/bin/addons/base/ir/ir_exports.py index c5329ab5191..5b2cb625d3d 100644 --- a/bin/addons/base/ir/ir_exports.py +++ b/bin/addons/base/ir/ir_exports.py @@ -31,20 +31,20 @@ from osv import fields,osv class ir_exports(osv.osv): - _name = "ir.exports" - _columns = { - 'name': fields.char('Export name', size=128), - 'resource': fields.char('Resource', size=128), - 'export_fields': fields.one2many('ir.exports.line', 'export_id', - 'Export Id'), - } + _name = "ir.exports" + _columns = { + 'name': fields.char('Export name', size=128), + 'resource': fields.char('Resource', size=128), + 'export_fields': fields.one2many('ir.exports.line', 'export_id', + 'Export Id'), + } ir_exports() class ir_exports_line(osv.osv): - _name = 'ir.exports.line' - _columns = { - 'name': fields.char('Field name', size=64), - 'export_id': fields.many2one('ir.exports', 'Exportation', select=True, ondelete='cascade'), - } + _name = 'ir.exports.line' + _columns = { + 'name': fields.char('Field name', size=64), + 'export_id': fields.many2one('ir.exports', 'Exportation', select=True, ondelete='cascade'), + } ir_exports_line() diff --git a/bin/addons/base/ir/ir_model.py b/bin/addons/base/ir/ir_model.py index 0c6bfc7edb1..5d971e079e7 100644 --- a/bin/addons/base/ir/ir_model.py +++ b/bin/addons/base/ir/ir_model.py @@ -37,395 +37,395 @@ import tools import pooler def _get_fields_type(self, cr, uid, context=None): - cr.execute('select distinct ttype,ttype from ir_model_fields') - return cr.fetchall() + cr.execute('select distinct ttype,ttype from ir_model_fields') + return cr.fetchall() class ir_model(osv.osv): - _name = 'ir.model' - _description = "Objects" - _rec_name = 'name' - _columns = { - 'name': fields.char('Model Name', size=64, translate=True, required=True), - 'model': fields.char('Object Name', size=64, required=True, search=1), - 'info': fields.text('Information'), - 'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True), - 'state': fields.selection([('manual','Custom Object'),('base','Base Field')],'Manualy Created',readonly=1), - } - _defaults = { - 'model': lambda *a: 'x_', - 'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base', - } - - def _check_model_name(self, cr, uid, ids): - for model in self.browse(cr, uid, ids): - if model.state=='manual': - if not model.model.startswith('x_'): - return False - if not re.match('^[a-z_A-Z0-9]+$',model.model): - return False - return True + _name = 'ir.model' + _description = "Objects" + _rec_name = 'name' + _columns = { + 'name': fields.char('Model Name', size=64, translate=True, required=True), + 'model': fields.char('Object Name', size=64, required=True, search=1), + 'info': fields.text('Information'), + 'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True), + 'state': fields.selection([('manual','Custom Object'),('base','Base Field')],'Manualy Created',readonly=1), + } + _defaults = { + 'model': lambda *a: 'x_', + 'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base', + } + + def _check_model_name(self, cr, uid, ids): + for model in self.browse(cr, uid, ids): + if model.state=='manual': + if not model.model.startswith('x_'): + return False + if not re.match('^[a-z_A-Z0-9]+$',model.model): + return False + return True - _constraints = [ - (_check_model_name, 'The model name must start with x_ and not contain any special character !', ['model']), - ] - def unlink(self, cr, user, ids, context=None): - for model in self.browse(cr, user, ids, context): - if model.state <> 'manual': - raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(field.name,)) - res = super(ir_model, self).unlink(cr, user, ids, context) - pooler.restart_pool(cr.dbname) - return res + _constraints = [ + (_check_model_name, 'The model name must start with x_ and not contain any special character !', ['model']), + ] + def unlink(self, cr, user, ids, context=None): + for model in self.browse(cr, user, ids, context): + if model.state <> 'manual': + raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(field.name,)) + res = super(ir_model, self).unlink(cr, user, ids, context) + pooler.restart_pool(cr.dbname) + return res - def create(self, cr, user, vals, context=None): - if context and context.get('manual',False): - vals['state']='manual' - res = super(ir_model,self).create(cr, user, vals, context) - if vals.get('state','base')=='manual': - pooler.restart_pool(cr.dbname) - return res + def create(self, cr, user, vals, context=None): + if context and context.get('manual',False): + vals['state']='manual' + res = super(ir_model,self).create(cr, user, vals, context) + if vals.get('state','base')=='manual': + pooler.restart_pool(cr.dbname) + return res - def instanciate(self, cr, user, model, context={}): - class x_custom_model(osv.osv): - pass - x_custom_model._name = model - x_custom_model._module = False - x_custom_model.createInstance(self.pool, '', cr) - if 'x_name' in x_custom_model._columns: - x_custom_model._rec_name = 'x_name' - else: - x_custom_model._rec_name = x_custom_model._columns.keys()[0] + def instanciate(self, cr, user, model, context={}): + class x_custom_model(osv.osv): + pass + x_custom_model._name = model + x_custom_model._module = False + x_custom_model.createInstance(self.pool, '', cr) + if 'x_name' in x_custom_model._columns: + x_custom_model._rec_name = 'x_name' + else: + x_custom_model._rec_name = x_custom_model._columns.keys()[0] ir_model() class ir_model_fields(osv.osv): - _name = 'ir.model.fields' - _description = "Fields" - _columns = { - 'name': fields.char('Name', required=True, size=64, select=1), - 'model': fields.char('Object Name', size=64, required=True), - 'relation': fields.char('Model Relation', size=64), - 'model_id': fields.many2one('ir.model', 'Model id', required=True, select=True, ondelete='cascade'), - 'field_description': fields.char('Field Label', required=True, size=256), - 'relate': fields.boolean('Click and Relate'), + _name = 'ir.model.fields' + _description = "Fields" + _columns = { + 'name': fields.char('Name', required=True, size=64, select=1), + 'model': fields.char('Object Name', size=64, required=True), + 'relation': fields.char('Model Relation', size=64), + 'model_id': fields.many2one('ir.model', 'Model id', required=True, select=True, ondelete='cascade'), + 'field_description': fields.char('Field Label', required=True, size=256), + 'relate': fields.boolean('Click and Relate'), - 'ttype': fields.selection(_get_fields_type, 'Field Type',size=64, required=True), - 'selection': fields.char('Field Selection',size=128), - 'required': fields.boolean('Required'), - 'readonly': fields.boolean('Readonly'), - 'select_level': fields.selection([('0','Not Searchable'),('1','Always Searchable'),('2','Advanced Search')],'Searchable', required=True), - 'translate': fields.boolean('Translate'), - 'size': fields.integer('Size'), - 'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Manualy Created'), - 'on_delete': fields.selection([('cascade','Cascade'),('set null','Set NULL')], 'On delete', help='On delete property for many2one fields'), - 'domain': fields.char('Domain', size=256), + 'ttype': fields.selection(_get_fields_type, 'Field Type',size=64, required=True), + 'selection': fields.char('Field Selection',size=128), + 'required': fields.boolean('Required'), + 'readonly': fields.boolean('Readonly'), + 'select_level': fields.selection([('0','Not Searchable'),('1','Always Searchable'),('2','Advanced Search')],'Searchable', required=True), + 'translate': fields.boolean('Translate'), + 'size': fields.integer('Size'), + 'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Manualy Created'), + 'on_delete': fields.selection([('cascade','Cascade'),('set null','Set NULL')], 'On delete', help='On delete property for many2one fields'), + 'domain': fields.char('Domain', size=256), - 'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'), - 'group_name': fields.char('Group Name', size=128), - 'view_load': fields.boolean('View Auto-Load'), - } - _defaults = { - 'relate': lambda *a: 0, - 'view_load': lambda *a: 0, - 'selection': lambda *a: "[]", - 'domain': lambda *a: "[]", - 'name': lambda *a: 'x_', - 'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base', - 'on_delete': lambda *a: 'set null', - 'select_level': lambda *a: '0', - 'size': lambda *a: 64, - 'field_description': lambda *a: '', - } - _order = "id" - def unlink(self, cr, user, ids, context=None): - for field in self.browse(cr, user, ids, context): - if field.state <> 'manual': - raise except_orm(_('Error'), _("You can not remove the field '%s' !") %(field.name,)) - # - # MAY BE ADD A ALTER TABLE DROP ? - # - return super(ir_model_fields, self).unlink(cr, user, ids, context) + 'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'), + 'group_name': fields.char('Group Name', size=128), + 'view_load': fields.boolean('View Auto-Load'), + } + _defaults = { + 'relate': lambda *a: 0, + 'view_load': lambda *a: 0, + 'selection': lambda *a: "[]", + 'domain': lambda *a: "[]", + 'name': lambda *a: 'x_', + 'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base', + 'on_delete': lambda *a: 'set null', + 'select_level': lambda *a: '0', + 'size': lambda *a: 64, + 'field_description': lambda *a: '', + } + _order = "id" + def unlink(self, cr, user, ids, context=None): + for field in self.browse(cr, user, ids, context): + if field.state <> 'manual': + raise except_orm(_('Error'), _("You can not remove the field '%s' !") %(field.name,)) + # + # MAY BE ADD A ALTER TABLE DROP ? + # + return super(ir_model_fields, self).unlink(cr, user, ids, context) - def create(self, cr, user, vals, context=None): - if 'model_id' in vals: - model_data=self.pool.get('ir.model').read(cr,user,vals['model_id']) - vals['model']=model_data['model'] - if context and context.get('manual',False): - vals['state']='manual' - res = super(ir_model_fields,self).create(cr, user, vals, context) - if vals.get('state','base')=='manual': - if not vals['name'].startswith('x_'): - raise except_orm(_('Error'), _("Custom fields must have a name that starts with 'x_' !")) - if self.pool.get(vals['model']): - self.pool.get(vals['model']).__init__(self.pool, cr) - self.pool.get(vals['model'])._auto_init(cr,{}) - return res + def create(self, cr, user, vals, context=None): + if 'model_id' in vals: + model_data=self.pool.get('ir.model').read(cr,user,vals['model_id']) + vals['model']=model_data['model'] + if context and context.get('manual',False): + vals['state']='manual' + res = super(ir_model_fields,self).create(cr, user, vals, context) + if vals.get('state','base')=='manual': + if not vals['name'].startswith('x_'): + raise except_orm(_('Error'), _("Custom fields must have a name that starts with 'x_' !")) + if self.pool.get(vals['model']): + self.pool.get(vals['model']).__init__(self.pool, cr) + self.pool.get(vals['model'])._auto_init(cr,{}) + return res ir_model_fields() class ir_model_access(osv.osv): - _name = 'ir.model.access' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'model_id': fields.many2one('ir.model', 'Model', required=True), - 'group_id': fields.many2one('res.groups', 'Group'), - 'perm_read': fields.boolean('Read Access'), - 'perm_write': fields.boolean('Write Access'), - 'perm_create': fields.boolean('Create Access'), - 'perm_unlink': fields.boolean('Delete Permission'), - } - - def check_groups(self, cr, uid, group): - res = False - grouparr = group.split('.') - if grouparr: - cr.execute("select * from res_groups_users_rel where uid=" + str(uid) + " and gid in(select res_id from ir_model_data where module='%s' and name='%s')" % (grouparr[0], grouparr[1])) - r = cr.fetchall() - if not r: - res = False - else: - res = True - else: - res = False - return res - - def check(self, cr, uid, model_name, mode='read',raise_exception=True): - assert mode in ['read','write','create','unlink'], 'Invalid access mode for security' - if uid == 1: - return True # TODO: check security: don't allow xml-rpc request with uid == 1 + _name = 'ir.model.access' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'model_id': fields.many2one('ir.model', 'Model', required=True), + 'group_id': fields.many2one('res.groups', 'Group'), + 'perm_read': fields.boolean('Read Access'), + 'perm_write': fields.boolean('Write Access'), + 'perm_create': fields.boolean('Create Access'), + 'perm_unlink': fields.boolean('Delete Permission'), + } + + def check_groups(self, cr, uid, group): + res = False + grouparr = group.split('.') + if grouparr: + cr.execute("select * from res_groups_users_rel where uid=" + str(uid) + " and gid in(select res_id from ir_model_data where module='%s' and name='%s')" % (grouparr[0], grouparr[1])) + r = cr.fetchall() + if not r: + res = False + else: + res = True + else: + res = False + return res + + def check(self, cr, uid, model_name, mode='read',raise_exception=True): + assert mode in ['read','write','create','unlink'], 'Invalid access mode for security' + if uid == 1: + return True # TODO: check security: don't allow xml-rpc request with uid == 1 - cr.execute('SELECT MAX(CASE WHEN perm_'+mode+' THEN 1 else 0 END) ' - 'FROM ir_model_access a ' - 'JOIN ir_model m ' - 'ON (a.model_id=m.id) ' - 'JOIN res_groups_users_rel gu ' - 'ON (gu.gid = a.group_id) ' - 'WHERE m.model = %s AND gu.uid = %s', (model_name, uid,)) - r = cr.fetchall() - if r[0][0] == None: - cr.execute('SELECT MAX(CASE WHEN perm_'+mode+' THEN 1 else 0 END) ' - 'FROM ir_model_access a ' - 'JOIN ir_model m ' - 'ON (a.model_id = m.id) ' - 'WHERE a.group_id IS NULL AND m.model = %s', (model_name,)) - r= cr.fetchall() - if r[0][0] == None: - return False # by default, the user had no access + cr.execute('SELECT MAX(CASE WHEN perm_'+mode+' THEN 1 else 0 END) ' + 'FROM ir_model_access a ' + 'JOIN ir_model m ' + 'ON (a.model_id=m.id) ' + 'JOIN res_groups_users_rel gu ' + 'ON (gu.gid = a.group_id) ' + 'WHERE m.model = %s AND gu.uid = %s', (model_name, uid,)) + r = cr.fetchall() + if r[0][0] == None: + cr.execute('SELECT MAX(CASE WHEN perm_'+mode+' THEN 1 else 0 END) ' + 'FROM ir_model_access a ' + 'JOIN ir_model m ' + 'ON (a.model_id = m.id) ' + 'WHERE a.group_id IS NULL AND m.model = %s', (model_name,)) + r= cr.fetchall() + if r[0][0] == None: + return False # by default, the user had no access - if not r[0][0]: - if raise_exception: - msgs = { - 'read': _('You can not read this document! (%s)'), - 'write': _('You can not write in this document! (%s)'), - 'create': _('You can not create this kind of document! (%s)'), - 'unlink': _('You can not delete this document! (%s)'), - } - # due to the assert at the begin of the function, we will never have a KeyError - raise except_orm(_('AccessError'), msgs[mode] % model_name ) - return r[0][0] + if not r[0][0]: + if raise_exception: + msgs = { + 'read': _('You can not read this document! (%s)'), + 'write': _('You can not write in this document! (%s)'), + 'create': _('You can not create this kind of document! (%s)'), + 'unlink': _('You can not delete this document! (%s)'), + } + # due to the assert at the begin of the function, we will never have a KeyError + raise except_orm(_('AccessError'), msgs[mode] % model_name ) + return r[0][0] - check = tools.cache()(check) + check = tools.cache()(check) - # - # Methods to clean the cache on the Check Method. - # - def write(self, cr, uid, *args, **argv): - res = super(ir_model_access, self).write(cr, uid, *args, **argv) - self.check() - return res - def create(self, cr, uid, *args, **argv): - res = super(ir_model_access, self).create(cr, uid, *args, **argv) - self.check() - return res - def unlink(self, cr, uid, *args, **argv): - res = super(ir_model_access, self).unlink(cr, uid, *args, **argv) - self.check() - return res + # + # Methods to clean the cache on the Check Method. + # + def write(self, cr, uid, *args, **argv): + res = super(ir_model_access, self).write(cr, uid, *args, **argv) + self.check() + return res + def create(self, cr, uid, *args, **argv): + res = super(ir_model_access, self).create(cr, uid, *args, **argv) + self.check() + return res + def unlink(self, cr, uid, *args, **argv): + res = super(ir_model_access, self).unlink(cr, uid, *args, **argv) + self.check() + return res ir_model_access() class ir_model_data(osv.osv): - _name = 'ir.model.data' - _columns = { - 'name': fields.char('XML Identifier', required=True, size=64), - 'model': fields.char('Model', required=True, size=64), - 'module': fields.char('Module', required=True, size=64), - 'res_id': fields.integer('Resource ID'), - 'noupdate': fields.boolean('Non Updatable'), - 'date_update': fields.datetime('Update Date'), - 'date_init': fields.datetime('Init Date') - } - _defaults = { - 'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - 'noupdate': lambda *a: False - } + _name = 'ir.model.data' + _columns = { + 'name': fields.char('XML Identifier', required=True, size=64), + 'model': fields.char('Model', required=True, size=64), + 'module': fields.char('Module', required=True, size=64), + 'res_id': fields.integer('Resource ID'), + 'noupdate': fields.boolean('Non Updatable'), + 'date_update': fields.datetime('Update Date'), + 'date_init': fields.datetime('Init Date') + } + _defaults = { + 'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + 'noupdate': lambda *a: False + } - def __init__(self, pool, cr): - osv.osv.__init__(self, pool, cr) - self.loads = {} - self.doinit = True - self.unlink_mark = {} + def __init__(self, pool, cr): + osv.osv.__init__(self, pool, cr) + self.loads = {} + self.doinit = True + self.unlink_mark = {} - def _get_id(self,cr, uid, module, xml_id): - ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)]) - assert len(ids)==1, '%d reference(s) to %s. You should have only one !' % (len(ids),xml_id) - return ids[0] - _get_id = tools.cache()(_get_id) + def _get_id(self,cr, uid, module, xml_id): + ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)]) + assert len(ids)==1, '%d reference(s) to %s. You should have only one !' % (len(ids),xml_id) + return ids[0] + _get_id = tools.cache()(_get_id) - def _update_dummy(self,cr, uid, model, module, xml_id=False, store=True): - if not xml_id: - return False - try: - id = self.read(cr, uid, [self._get_id(cr, uid, module, xml_id)], ['res_id'])[0]['res_id'] - self.loads[(module,xml_id)] = (model,id) - except: - id = False - return id + def _update_dummy(self,cr, uid, model, module, xml_id=False, store=True): + if not xml_id: + return False + try: + id = self.read(cr, uid, [self._get_id(cr, uid, module, xml_id)], ['res_id'])[0]['res_id'] + self.loads[(module,xml_id)] = (model,id) + except: + id = False + return id - def _update(self,cr, uid, model, module, values, xml_id=False, store=True, noupdate=False, mode='init', res_id=False): - warning = True - model_obj = self.pool.get(model) - context = {} - if xml_id and ('.' in xml_id): - assert len(xml_id.split('.'))==2, _('"%s" contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id') % (xml_id) - warning = False - module, xml_id = xml_id.split('.') - if (not xml_id) and (not self.doinit): - return False - action_id = False - if xml_id: - cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id)) - results = cr.fetchall() - for action_id2,res_id2 in results: - cr.execute('select id from '+self.pool.get(model)._table+' where id=%d', (res_id2,)) - result3 = cr.fetchone() - if not result3: - cr.execute('delete from ir_model_data where id=%d', (action_id2,)) - else: - res_id,action_id = res_id2,action_id2 + def _update(self,cr, uid, model, module, values, xml_id=False, store=True, noupdate=False, mode='init', res_id=False): + warning = True + model_obj = self.pool.get(model) + context = {} + if xml_id and ('.' in xml_id): + assert len(xml_id.split('.'))==2, _('"%s" contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id') % (xml_id) + warning = False + module, xml_id = xml_id.split('.') + if (not xml_id) and (not self.doinit): + return False + action_id = False + if xml_id: + cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id)) + results = cr.fetchall() + for action_id2,res_id2 in results: + cr.execute('select id from '+self.pool.get(model)._table+' where id=%d', (res_id2,)) + result3 = cr.fetchone() + if not result3: + cr.execute('delete from ir_model_data where id=%d', (action_id2,)) + else: + res_id,action_id = res_id2,action_id2 - if action_id and res_id: - model_obj.write(cr, uid, [res_id], values) - self.write(cr, uid, [action_id], { - 'date_update': time.strftime('%Y-%m-%d %H:%M:%S'), - }) - elif res_id: - model_obj.write(cr, uid, [res_id], values) - if xml_id: - self.create(cr, uid, { - 'name': xml_id, - 'model': model, - 'module':module, - 'res_id':res_id, - 'noupdate': noupdate, - }) - if model_obj._inherits: - for table in model_obj._inherits: - inherit_id = model_obj.browse(cr, uid, - res_id)[model_obj._inherits[table]] - self.create(cr, uid, { - 'name': xml_id + '_' + table.replace('.', '_'), - 'model': table, - 'module': module, - 'res_id': inherit_id, - 'noupdate': noupdate, - }) - else: - if mode=='init' or (mode=='update' and xml_id): - res_id = model_obj.create(cr, uid, values) - if xml_id: - self.create(cr, uid, { - 'name': xml_id, - 'model': model, - 'module': module, - 'res_id': res_id, - 'noupdate': noupdate - }) - if model_obj._inherits: - for table in model_obj._inherits: - inherit_id = model_obj.browse(cr, uid, - res_id)[model_obj._inherits[table]] - self.create(cr, uid, { - 'name': xml_id + '_' + table.replace('.', '_'), - 'model': table, - 'module': module, - 'res_id': inherit_id, - 'noupdate': noupdate, - }) - if xml_id: - if res_id: - self.loads[(module, xml_id)] = (model, res_id) - if model_obj._inherits: - for table in model_obj._inherits: - inherit_field = model_obj._inherits[table] - inherit_id = model_obj.read(cr, uid, res_id, - [inherit_field])[inherit_field] - self.loads[(module, xml_id + '_' + \ - table.replace('.', '_'))] = (table, inherit_id) - return res_id + if action_id and res_id: + model_obj.write(cr, uid, [res_id], values) + self.write(cr, uid, [action_id], { + 'date_update': time.strftime('%Y-%m-%d %H:%M:%S'), + }) + elif res_id: + model_obj.write(cr, uid, [res_id], values) + if xml_id: + self.create(cr, uid, { + 'name': xml_id, + 'model': model, + 'module':module, + 'res_id':res_id, + 'noupdate': noupdate, + }) + if model_obj._inherits: + for table in model_obj._inherits: + inherit_id = model_obj.browse(cr, uid, + res_id)[model_obj._inherits[table]] + self.create(cr, uid, { + 'name': xml_id + '_' + table.replace('.', '_'), + 'model': table, + 'module': module, + 'res_id': inherit_id, + 'noupdate': noupdate, + }) + else: + if mode=='init' or (mode=='update' and xml_id): + res_id = model_obj.create(cr, uid, values) + if xml_id: + self.create(cr, uid, { + 'name': xml_id, + 'model': model, + 'module': module, + 'res_id': res_id, + 'noupdate': noupdate + }) + if model_obj._inherits: + for table in model_obj._inherits: + inherit_id = model_obj.browse(cr, uid, + res_id)[model_obj._inherits[table]] + self.create(cr, uid, { + 'name': xml_id + '_' + table.replace('.', '_'), + 'model': table, + 'module': module, + 'res_id': inherit_id, + 'noupdate': noupdate, + }) + if xml_id: + if res_id: + self.loads[(module, xml_id)] = (model, res_id) + if model_obj._inherits: + for table in model_obj._inherits: + inherit_field = model_obj._inherits[table] + inherit_id = model_obj.read(cr, uid, res_id, + [inherit_field])[inherit_field] + self.loads[(module, xml_id + '_' + \ + table.replace('.', '_'))] = (table, inherit_id) + return res_id - def _unlink(self, cr, uid, model, ids, direct=False): - #self.pool.get(model).unlink(cr, uid, ids) - for id in ids: - self.unlink_mark[(model, id)]=False - cr.execute('delete from ir_model_data where res_id=%d and model=\'%s\'', (id,model)) - return True + def _unlink(self, cr, uid, model, ids, direct=False): + #self.pool.get(model).unlink(cr, uid, ids) + for id in ids: + self.unlink_mark[(model, id)]=False + cr.execute('delete from ir_model_data where res_id=%d and model=\'%s\'', (id,model)) + return True - def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False): - obj = self.pool.get('ir.values') - if type(models[0])==type([]) or type(models[0])==type(()): - model,res_id = models[0] - else: - res_id=None - model = models[0] + def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False): + obj = self.pool.get('ir.values') + if type(models[0])==type([]) or type(models[0])==type(()): + model,res_id = models[0] + else: + res_id=None + model = models[0] - if res_id: - where = ' and res_id=%d' % (res_id,) - else: - where = ' and (res_id is null)' + if res_id: + where = ' and res_id=%d' % (res_id,) + else: + where = ' and (res_id is null)' - if key2: - where += ' and key2=\'%s\'' % (key2,) - else: - where += ' and (key2 is null)' + if key2: + where += ' and key2=\'%s\'' % (key2,) + else: + where += ' and (key2 is null)' - cr.execute('select * from ir_values where model=%s and key=%s and name=%s'+where,(model, key, name)) - res = cr.fetchone() - if not res: - res = ir.ir_set(cr, uid, key, key2, name, models, value, replace, isobject, meta) - elif xml_id: - cr.execute('UPDATE ir_values set value=%s WHERE model=%s and key=%s and name=%s'+where,(value, model, key, name)) - return True + cr.execute('select * from ir_values where model=%s and key=%s and name=%s'+where,(model, key, name)) + res = cr.fetchone() + if not res: + res = ir.ir_set(cr, uid, key, key2, name, models, value, replace, isobject, meta) + elif xml_id: + cr.execute('UPDATE ir_values set value=%s WHERE model=%s and key=%s and name=%s'+where,(value, model, key, name)) + return True - def _process_end(self, cr, uid, modules): - if not modules: - return True - module_str = ["'%s'" % m for m in modules] - cr.execute('select id,name,model,res_id,module from ir_model_data where module in ('+','.join(module_str)+') and not noupdate') - wkf_todo = [] - for (id, name, model, res_id,module) in cr.fetchall(): - if (module,name) not in self.loads: - self.unlink_mark[(model,res_id)] = id - if model=='workflow.activity': - cr.execute('select res_type,res_id from wkf_instance where id in (select inst_id from wkf_workitem where act_id=%d)', (res_id,)) - wkf_todo.extend(cr.fetchall()) - cr.execute("update wkf_transition set condition='True', role_id=NULL, signal=NULL,act_to=act_from,act_from=%d where act_to=%d", (res_id,res_id)) - cr.execute("delete from wkf_transition where act_to=%d", (res_id,)) + def _process_end(self, cr, uid, modules): + if not modules: + return True + module_str = ["'%s'" % m for m in modules] + cr.execute('select id,name,model,res_id,module from ir_model_data where module in ('+','.join(module_str)+') and not noupdate') + wkf_todo = [] + for (id, name, model, res_id,module) in cr.fetchall(): + if (module,name) not in self.loads: + self.unlink_mark[(model,res_id)] = id + if model=='workflow.activity': + cr.execute('select res_type,res_id from wkf_instance where id in (select inst_id from wkf_workitem where act_id=%d)', (res_id,)) + wkf_todo.extend(cr.fetchall()) + cr.execute("update wkf_transition set condition='True', role_id=NULL, signal=NULL,act_to=act_from,act_from=%d where act_to=%d", (res_id,res_id)) + cr.execute("delete from wkf_transition where act_to=%d", (res_id,)) - for model,id in wkf_todo: - wf_service = netsvc.LocalService("workflow") - wf_service.trg_write(uid, model, id, cr) + for model,id in wkf_todo: + wf_service = netsvc.LocalService("workflow") + wf_service.trg_write(uid, model, id, cr) - cr.commit() - for (model,id) in self.unlink_mark.keys(): - if self.pool.get(model): - logger = netsvc.Logger() - logger.notifyChannel('init', netsvc.LOG_INFO, 'Deleting %s@%s' % (id, model)) - try: - self.pool.get(model).unlink(cr, uid, [id]) - if self.unlink_mark[(model,id)]: - self.unlink(cr, uid, [self.unlink_mark[(model,id)]]) - cr.execute('DELETE FROM ir_values WHERE value=%s', (model+','+str(id),)) - cr.commit() - except: - logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not delete id: %d of model %s\tThere should be some relation that points to this resource\tYou should manually fix this and restart --update=module' % (id, model)) - return True + cr.commit() + for (model,id) in self.unlink_mark.keys(): + if self.pool.get(model): + logger = netsvc.Logger() + logger.notifyChannel('init', netsvc.LOG_INFO, 'Deleting %s@%s' % (id, model)) + try: + self.pool.get(model).unlink(cr, uid, [id]) + if self.unlink_mark[(model,id)]: + self.unlink(cr, uid, [self.unlink_mark[(model,id)]]) + cr.execute('DELETE FROM ir_values WHERE value=%s', (model+','+str(id),)) + cr.commit() + except: + logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not delete id: %d of model %s\tThere should be some relation that points to this resource\tYou should manually fix this and restart --update=module' % (id, model)) + return True ir_model_data() diff --git a/bin/addons/base/ir/ir_report_custom.py b/bin/addons/base/ir/ir_report_custom.py index af413f54e41..cc8b3acc7e5 100644 --- a/bin/addons/base/ir/ir_report_custom.py +++ b/bin/addons/base/ir/ir_report_custom.py @@ -34,176 +34,176 @@ import report.custom from tools.translate import _ class report_custom(osv.osv): - _name = 'ir.report.custom' - _columns = { - 'name': fields.char('Report Name', size=64, required=True, translate=True), - 'model_id': fields.many2one('ir.model','Model', required=True, change_default=True), - 'type': fields.selection([('table','Tabular'),('pie','Pie Chart'),('bar','Bar Chart'),('line','Line Plot')], "Report Type", size=64, required='True'), - 'title': fields.char("Report title", size=64, required='True', translate=True), - 'print_format': fields.selection((('A4','a4'),('A5','a5')), 'Print format', required=True), - 'print_orientation': fields.selection((('landscape','Landscape'),('portrait','Portrait')), 'Print orientation', required=True, size=16), - 'repeat_header': fields.boolean('Repeat Header'), - 'footer': fields.char('Report Footer', size=64, required=True), - 'sortby': fields.char('Sorted By', size=64), - 'fields_child0': fields.one2many('ir.report.custom.fields', 'report_id','Fields', required=True), - 'field_parent': fields.many2one('ir.model.fields','Child Field'), - 'state': fields.selection([('unsubscribed','Unsubscribed'),('subscribed','Subscribed')], 'State', size=64), - 'frequency': fields.selection([('Y','Yearly'),('M','Monthly'),('D','Daily')], 'Frequency', size=64), - 'limitt': fields.char('Limit', size=9), - 'menu_id': fields.many2one('ir.ui.menu', 'Menu') - } - _defaults = { - 'print_format': lambda *a: 'A4', - 'print_orientation': lambda *a: 'portrait', - 'state': lambda *a: 'unsubscribed', - 'type': lambda *a: 'table', - 'footer': lambda *a: 'Generated by Tiny ERP' - } - - def onchange_model_id(self, cr, uid, ids, model_id): - if not(model_id): - return {} - return {'domain': {'field_parent': [('model_id','=',model_id)]}} + _name = 'ir.report.custom' + _columns = { + 'name': fields.char('Report Name', size=64, required=True, translate=True), + 'model_id': fields.many2one('ir.model','Model', required=True, change_default=True), + 'type': fields.selection([('table','Tabular'),('pie','Pie Chart'),('bar','Bar Chart'),('line','Line Plot')], "Report Type", size=64, required='True'), + 'title': fields.char("Report title", size=64, required='True', translate=True), + 'print_format': fields.selection((('A4','a4'),('A5','a5')), 'Print format', required=True), + 'print_orientation': fields.selection((('landscape','Landscape'),('portrait','Portrait')), 'Print orientation', required=True, size=16), + 'repeat_header': fields.boolean('Repeat Header'), + 'footer': fields.char('Report Footer', size=64, required=True), + 'sortby': fields.char('Sorted By', size=64), + 'fields_child0': fields.one2many('ir.report.custom.fields', 'report_id','Fields', required=True), + 'field_parent': fields.many2one('ir.model.fields','Child Field'), + 'state': fields.selection([('unsubscribed','Unsubscribed'),('subscribed','Subscribed')], 'State', size=64), + 'frequency': fields.selection([('Y','Yearly'),('M','Monthly'),('D','Daily')], 'Frequency', size=64), + 'limitt': fields.char('Limit', size=9), + 'menu_id': fields.many2one('ir.ui.menu', 'Menu') + } + _defaults = { + 'print_format': lambda *a: 'A4', + 'print_orientation': lambda *a: 'portrait', + 'state': lambda *a: 'unsubscribed', + 'type': lambda *a: 'table', + 'footer': lambda *a: 'Generated by Tiny ERP' + } + + def onchange_model_id(self, cr, uid, ids, model_id): + if not(model_id): + return {} + return {'domain': {'field_parent': [('model_id','=',model_id)]}} - def unsubscribe(self, cr, uid, ids, context={}): + def unsubscribe(self, cr, uid, ids, context={}): #TODO: should delete the ir.actions.report.custom for these reports and do an ir_del - self.write(cr, uid, ids, {'state':'unsubscribed'}) - return True + self.write(cr, uid, ids, {'state':'unsubscribed'}) + return True - def subscribe(self, cr, uid, ids, context={}): - for report in self.browse(cr, uid, ids): - report.fields_child0.sort(lambda x,y : x.sequence - y.sequence) - - # required on field0 does not seem to work( cause we use o2m_l ?) - if not report.fields_child0: - raise osv.except_osv(_('Invalid operation'), _('Enter at least one field !')) - - if report.type in ['pie', 'bar', 'line'] and report.field_parent: - raise osv.except_osv(_('Invalid operation'), _('Tree can only be used in tabular reports')) - - # Otherwise it won't build a good tree. See level.pop in custom.py. - if report.type == 'table' and report.field_parent and report.fields_child0 and not report.fields_child0[0].groupby: - raise osv.except_osv('Invalid operation :', 'When creating tree (field child) report, data must be group by the first field') + def subscribe(self, cr, uid, ids, context={}): + for report in self.browse(cr, uid, ids): + report.fields_child0.sort(lambda x,y : x.sequence - y.sequence) + + # required on field0 does not seem to work( cause we use o2m_l ?) + if not report.fields_child0: + raise osv.except_osv(_('Invalid operation'), _('Enter at least one field !')) + + if report.type in ['pie', 'bar', 'line'] and report.field_parent: + raise osv.except_osv(_('Invalid operation'), _('Tree can only be used in tabular reports')) + + # Otherwise it won't build a good tree. See level.pop in custom.py. + if report.type == 'table' and report.field_parent and report.fields_child0 and not report.fields_child0[0].groupby: + raise osv.except_osv('Invalid operation :', 'When creating tree (field child) report, data must be group by the first field') - if report.type == 'pie': - if len(report.fields_child0) != 2: - raise osv.except_osv(_('Invalid operation'), _('Pie charts need exactly two fields')) - else: - c_f = {} - for i in range(2): - c_f[i] = [] - tmp = report.fields_child0[i] - for j in range(3): - c_f[i].append((not isinstance(eval('tmp.field_child'+str(j)), browse_null) and eval('tmp.field_child'+str(j)+'.ttype')) or None) - if not reduce(lambda x,y : x or y, map(lambda x: x in ['integer', 'float'], c_f[1])): - raise osv.except_osv(_('Invalid operation'), _('Second field should be figures')) - - if report.type == 'bar': - if len(report.fields_child0) < 2: - raise osv.except_osv(_('Invalid operation'), _('Bar charts need at least two fields')) - else: - c_f = {} - for i in range(len(report.fields_child0)): - c_f[i] = [] - tmp = report.fields_child0[i] - for j in range(3): - c_f[i].append((not isinstance(eval('tmp.field_child'+str(j)), browse_null) and eval('tmp.field_child'+str(j)+'.ttype')) or None) + if report.type == 'pie': + if len(report.fields_child0) != 2: + raise osv.except_osv(_('Invalid operation'), _('Pie charts need exactly two fields')) + else: + c_f = {} + for i in range(2): + c_f[i] = [] + tmp = report.fields_child0[i] + for j in range(3): + c_f[i].append((not isinstance(eval('tmp.field_child'+str(j)), browse_null) and eval('tmp.field_child'+str(j)+'.ttype')) or None) + if not reduce(lambda x,y : x or y, map(lambda x: x in ['integer', 'float'], c_f[1])): + raise osv.except_osv(_('Invalid operation'), _('Second field should be figures')) + + if report.type == 'bar': + if len(report.fields_child0) < 2: + raise osv.except_osv(_('Invalid operation'), _('Bar charts need at least two fields')) + else: + c_f = {} + for i in range(len(report.fields_child0)): + c_f[i] = [] + tmp = report.fields_child0[i] + for j in range(3): + c_f[i].append((not isinstance(eval('tmp.field_child'+str(j)), browse_null) and eval('tmp.field_child'+str(j)+'.ttype')) or None) - if i == 0: - pass - else: - if not reduce(lambda x,y : x or y, map(lambda x: x in ['integer', 'float'], c_f[i])): - raise osv.except_osv(_('Invalid operation'), _('Field %d should be a figure') %(i,)) + if i == 0: + pass + else: + if not reduce(lambda x,y : x or y, map(lambda x: x in ['integer', 'float'], c_f[i])): + raise osv.except_osv(_('Invalid operation'), _('Field %d should be a figure') %(i,)) - if report.state=='subscribed': - continue + if report.state=='subscribed': + continue - name = report.name - model = report.model_id.model + name = report.name + model = report.model_id.model - action_def = {'report_id':report.id, 'type':'ir.actions.report.custom', 'model':model, 'name':name} - id = self.pool.get('ir.actions.report.custom').create(cr, uid, action_def) - m_id = report.menu_id.id - action = "ir.actions.report.custom,%d" % (id,) - if not report.menu_id: - ir.ir_set(cr, uid, 'action', 'client_print_multi', name, [(model, False)], action, False, True) - else: - ir.ir_set(cr, uid, 'action', 'tree_but_open', 'Menuitem', [('ir.ui.menu', int(m_id))], action, False, True) + action_def = {'report_id':report.id, 'type':'ir.actions.report.custom', 'model':model, 'name':name} + id = self.pool.get('ir.actions.report.custom').create(cr, uid, action_def) + m_id = report.menu_id.id + action = "ir.actions.report.custom,%d" % (id,) + if not report.menu_id: + ir.ir_set(cr, uid, 'action', 'client_print_multi', name, [(model, False)], action, False, True) + else: + ir.ir_set(cr, uid, 'action', 'tree_but_open', 'Menuitem', [('ir.ui.menu', int(m_id))], action, False, True) - self.write(cr, uid, [report.id], {'state':'subscribed'}, context) - return True + self.write(cr, uid, [report.id], {'state':'subscribed'}, context) + return True report_custom() class report_custom_fields(osv.osv): - _name = 'ir.report.custom.fields' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'report_id': fields.many2one('ir.report.custom', 'Report Ref', select=True), - 'field_child0': fields.many2one('ir.model.fields', 'field child0', required=True), - 'fc0_operande': fields.many2one('ir.model.fields', 'Constraint'), - 'fc0_condition': fields.char('Condition', size=64), - 'fc0_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'), - 'field_child1': fields.many2one('ir.model.fields', 'field child1'), - 'fc1_operande': fields.many2one('ir.model.fields', 'Constraint'), - 'fc1_condition': fields.char('condition', size=64), - 'fc1_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'), - 'field_child2': fields.many2one('ir.model.fields', 'field child2'), - 'fc2_operande': fields.many2one('ir.model.fields', 'Constraint'), - 'fc2_condition': fields.char('condition', size=64), - 'fc2_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'), - 'field_child3': fields.many2one('ir.model.fields', 'field child3'), - 'fc3_operande': fields.many2one('ir.model.fields', 'Constraint'), - 'fc3_condition': fields.char('condition', size=64), - 'fc3_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'), - 'alignment': fields.selection((('left','left'),('right','right'),('center','center')), 'Alignment', required=True), - 'sequence': fields.integer('Sequence', required=True), - 'width': fields.integer('Fixed Width'), - 'operation': fields.selection((('none', 'None'),('calc_sum','Calculate Sum'),('calc_avg','Calculate Average'),('calc_count','Calculate Count'),('calc_max', 'Get Max'))), - 'groupby' : fields.boolean('Group by'), - 'bgcolor': fields.char('Background Color', size=64), - 'fontcolor': fields.char('Font color', size=64), - 'cumulate': fields.boolean('Cumulate') - } - _defaults = { - 'alignment': lambda *a: 'left', - 'bgcolor': lambda *a: 'white', - 'fontcolor': lambda *a: 'black', - 'operation': lambda *a: 'none', - } - _order = "sequence" + _name = 'ir.report.custom.fields' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'report_id': fields.many2one('ir.report.custom', 'Report Ref', select=True), + 'field_child0': fields.many2one('ir.model.fields', 'field child0', required=True), + 'fc0_operande': fields.many2one('ir.model.fields', 'Constraint'), + 'fc0_condition': fields.char('Condition', size=64), + 'fc0_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'), + 'field_child1': fields.many2one('ir.model.fields', 'field child1'), + 'fc1_operande': fields.many2one('ir.model.fields', 'Constraint'), + 'fc1_condition': fields.char('condition', size=64), + 'fc1_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'), + 'field_child2': fields.many2one('ir.model.fields', 'field child2'), + 'fc2_operande': fields.many2one('ir.model.fields', 'Constraint'), + 'fc2_condition': fields.char('condition', size=64), + 'fc2_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'), + 'field_child3': fields.many2one('ir.model.fields', 'field child3'), + 'fc3_operande': fields.many2one('ir.model.fields', 'Constraint'), + 'fc3_condition': fields.char('condition', size=64), + 'fc3_op': fields.selection((('>','>'),('<','<'),('==','='),('in','in'),('gety,==','(year)=')), 'Relation'), + 'alignment': fields.selection((('left','left'),('right','right'),('center','center')), 'Alignment', required=True), + 'sequence': fields.integer('Sequence', required=True), + 'width': fields.integer('Fixed Width'), + 'operation': fields.selection((('none', 'None'),('calc_sum','Calculate Sum'),('calc_avg','Calculate Average'),('calc_count','Calculate Count'),('calc_max', 'Get Max'))), + 'groupby' : fields.boolean('Group by'), + 'bgcolor': fields.char('Background Color', size=64), + 'fontcolor': fields.char('Font color', size=64), + 'cumulate': fields.boolean('Cumulate') + } + _defaults = { + 'alignment': lambda *a: 'left', + 'bgcolor': lambda *a: 'white', + 'fontcolor': lambda *a: 'black', + 'operation': lambda *a: 'none', + } + _order = "sequence" - def onchange_any_field_child(self, cr, uid, ids, field_id, level): - if not(field_id): - return {} - next_level_field_name = 'field_child%d' % (level+1) - next_level_operande = 'fc%d_operande' % (level+1) - field = self.pool.get('ir.model.fields').browse(cr, uid, [field_id])[0] - res = self.pool.get(field.model).fields_get(cr, uid, field.name) - if res[field.name].has_key('relation'): - cr.execute('select id from ir_model where model=%s', (res[field.name]['relation'],)) - (id,) = cr.fetchone() or (False,) - if id: - return { - 'domain': { - next_level_field_name: [('model_id', '=', id)], - next_level_operande: [('model_id', '=', id)] - }, - 'required': { - next_level_field_name: True - } - } - else: - print _("Warning: using a relation field which uses an unknown object") #TODO use the logger - return {'required': {next_level_field_name: True}} - else: - return {'domain': {next_level_field_name: []}} - - def get_field_child_onchange_method(level): - return lambda self, cr, uid, ids, field_id: self.onchange_any_field_child(cr, uid, ids, field_id, level) + def onchange_any_field_child(self, cr, uid, ids, field_id, level): + if not(field_id): + return {} + next_level_field_name = 'field_child%d' % (level+1) + next_level_operande = 'fc%d_operande' % (level+1) + field = self.pool.get('ir.model.fields').browse(cr, uid, [field_id])[0] + res = self.pool.get(field.model).fields_get(cr, uid, field.name) + if res[field.name].has_key('relation'): + cr.execute('select id from ir_model where model=%s', (res[field.name]['relation'],)) + (id,) = cr.fetchone() or (False,) + if id: + return { + 'domain': { + next_level_field_name: [('model_id', '=', id)], + next_level_operande: [('model_id', '=', id)] + }, + 'required': { + next_level_field_name: True + } + } + else: + print _("Warning: using a relation field which uses an unknown object") #TODO use the logger + return {'required': {next_level_field_name: True}} + else: + return {'domain': {next_level_field_name: []}} + + def get_field_child_onchange_method(level): + return lambda self, cr, uid, ids, field_id: self.onchange_any_field_child(cr, uid, ids, field_id, level) - onchange_field_child0 = get_field_child_onchange_method(0) - onchange_field_child1 = get_field_child_onchange_method(1) - onchange_field_child2 = get_field_child_onchange_method(2) + onchange_field_child0 = get_field_child_onchange_method(0) + onchange_field_child1 = get_field_child_onchange_method(1) + onchange_field_child2 = get_field_child_onchange_method(2) report_custom_fields() diff --git a/bin/addons/base/ir/ir_rule.py b/bin/addons/base/ir/ir_rule.py index 6f76133341d..bdd5312f291 100644 --- a/bin/addons/base/ir/ir_rule.py +++ b/bin/addons/base/ir/ir_rule.py @@ -33,208 +33,208 @@ import tools class ir_rule_group(osv.osv): - _name = 'ir.rule.group' - - _columns = { - 'name': fields.char('Name', size=128, select=1), - 'model_id': fields.many2one('ir.model', 'Model',select=1, required=True), - 'global': fields.boolean('Global', select=1, help="Make the rule global or it needs to be put on a group or user"), - 'rules': fields.one2many('ir.rule', 'rule_group', 'Tests', help="The rule is satisfied if at least one test is True"), - 'groups': fields.many2many('res.groups', 'group_rule_group_rel', 'rule_group_id', 'group_id', 'Groups'), - 'users': fields.many2many('res.users', 'user_rule_group_rel', 'rule_group_id', 'user_id', 'Users'), - } - - _order = 'model_id, global DESC' - - _defaults={ - 'global': lambda *a: True, - } - - def unlink(self, cr, uid, ids, context=None): - res = super(ir_rule_group, self).unlink(cr, uid, ids, context=context) - # Restart the cache on the domain_get method of ir.rule - self.pool.get('ir.rule').domain_get() - return res - - def create(self, cr, user, vals, context=None): - res = super(ir_rule_group, self).create(cr, user, vals, context=context) - # Restart the cache on the domain_get method of ir.rule - self.pool.get('ir.rule').domain_get() - return res - - def write(self, cr, uid, ids, vals, context=None): - if not context: - context={} - res = super(ir_rule_group, self).write(cr, uid, ids, vals, context=context) - # Restart the cache on the domain_get method of ir.rule - self.pool.get('ir.rule').domain_get() - return res + _name = 'ir.rule.group' + + _columns = { + 'name': fields.char('Name', size=128, select=1), + 'model_id': fields.many2one('ir.model', 'Model',select=1, required=True), + 'global': fields.boolean('Global', select=1, help="Make the rule global or it needs to be put on a group or user"), + 'rules': fields.one2many('ir.rule', 'rule_group', 'Tests', help="The rule is satisfied if at least one test is True"), + 'groups': fields.many2many('res.groups', 'group_rule_group_rel', 'rule_group_id', 'group_id', 'Groups'), + 'users': fields.many2many('res.users', 'user_rule_group_rel', 'rule_group_id', 'user_id', 'Users'), + } + + _order = 'model_id, global DESC' + + _defaults={ + 'global': lambda *a: True, + } + + def unlink(self, cr, uid, ids, context=None): + res = super(ir_rule_group, self).unlink(cr, uid, ids, context=context) + # Restart the cache on the domain_get method of ir.rule + self.pool.get('ir.rule').domain_get() + return res + + def create(self, cr, user, vals, context=None): + res = super(ir_rule_group, self).create(cr, user, vals, context=context) + # Restart the cache on the domain_get method of ir.rule + self.pool.get('ir.rule').domain_get() + return res + + def write(self, cr, uid, ids, vals, context=None): + if not context: + context={} + res = super(ir_rule_group, self).write(cr, uid, ids, vals, context=context) + # Restart the cache on the domain_get method of ir.rule + self.pool.get('ir.rule').domain_get() + return res ir_rule_group() class ir_rule(osv.osv): - _name = 'ir.rule' - _rec_name = 'field_id' - - def _operand(self,cr,uid,context): - - def get(object, level=3, recur=None, root_tech='', root=''): - res = [] - if not recur: - recur = [] - fields = self.pool.get(object).fields_get(cr,uid) - key = fields.keys() - key.sort() - for k in key: - - if fields[k]['type'] in ('many2one'): - res.append((root_tech+'.'+k+'.id', - root+'/'+fields[k]['string'])) - - elif fields[k]['type'] in ('many2many', 'one2many'): - res.append(('\',\'.join(map(lambda x: str(x.id), '+root_tech+'.'+k+'))', - root+'/'+fields[k]['string'])) - - else: - res.append((root_tech+'.'+k, - root+'/'+fields[k]['string'])) - - if (fields[k]['type'] in recur) and (level>0): - res.extend(get(fields[k]['relation'], level-1, - recur, root_tech+'.'+k, root+'/'+fields[k]['string'])) - - return res - - res = [("False", "False"), ("True", "True"), ("user.id", "User")] - res += get('res.users', level=1, - recur=['many2one'], root_tech='user', root='User') - return res - - def _domain_force_get(self, cr, uid, ids, field_name, arg, context={}): - res = {} - for rule in self.browse(cr, uid, ids, context): - if rule.domain_force: - res[rule.id] = eval(rule.domain_force, {'user': self.pool.get('res.users').browse(cr, 1, uid), - 'time':time}) - else: - if rule.operator in ('in', 'child_of'): - dom = eval("[('%s', '%s', [%s])]" % (rule.field_id.name, rule.operator, - rule.operand), {'user': self.pool.get('res.users').browse(cr, 1, uid), - 'time':time}) - else: - dom = eval("[('%s', '%s', %s)]" % (rule.field_id.name, rule.operator, - rule.operand), {'user': self.pool.get('res.users').browse(cr, 1, uid), - 'time':time}) - res[rule.id] = dom - return res - - _columns = { - 'field_id': fields.many2one('ir.model.fields', 'Field',domain= "[('model_id','=', parent.model_id)]", select=1, required=True), - 'operator':fields.selection((('=', '='), ('<>', '<>'), ('<=', '<='), ('>=', '>='), ('in', 'in'), ('child_of', 'child_of')), 'Operator', required=True), - 'operand':fields.selection(_operand,'Operand', size=64, required=True), - 'rule_group': fields.many2one('ir.rule.group', 'Group', select=2, required=True, ondelete="cascade"), - 'domain_force': fields.char('Force Domain', size=250), - 'domain': fields.function(_domain_force_get, method=True, string='Domain', type='char', size=250) - } - - def onchange_all(self, cr, uid, ids, field_id, operator, operand): - if not (field_id or operator or operand): - return {} - - def domain_get(self, cr, uid, model_name): - # root user above constraint - if uid == 1: - return '', [] - - cr.execute("""SELECT r.id FROM - ir_rule r - JOIN (ir_rule_group g - JOIN ir_model m ON (g.model_id = m.id)) - ON (g.id = r.rule_group) - WHERE m.model = %s - AND (g.id IN (SELECT rule_group_id FROM group_rule_group_rel g_rel - JOIN res_groups_users_rel u_rel ON (g_rel.group_id = u_rel.gid) - WHERE u_rel.uid = %d) OR g.global)""", (model_name, uid)) - ids = map(lambda x:x[0], cr.fetchall()) - if not ids: - return '', [] - obj = self.pool.get(model_name) - add = [] - add_str = [] - sub = [] - sub_str = [] - clause={} - clause_global={} - for rule in self.browse(cr, uid, ids): - dom = rule.domain - if rule.rule_group['global']: - clause_global.setdefault(rule.rule_group.id, []) - clause_global[rule.rule_group.id].append(obj._where_calc(cr, uid, dom, active_test=False)) - else: - clause.setdefault(rule.rule_group.id, []) - clause[rule.rule_group.id].append(obj._where_calc(cr, uid, dom, active_test=False)) - - def _query(clause, test): - query = '' - val = [] - for g in clause.values(): - if not g: - continue - if len(query): - query += ' '+test+' ' - query += '(' - first = True - for c in g: - if not first: - query += ' AND ' - first = False - query += '(' - first2 = True - for clause in c[0]: - if not first2: - query += ' AND ' - first2 = False - query += clause - query += ')' - val += c[1] - query += ')' - return query, val - - query, val = _query(clause, 'OR') - query_global, val_global = _query(clause_global, 'OR') - if query_global: - if query: - query = '('+query+') OR '+query_global - val.extend(val_global) - else: - query = query_global - val = val_global - - - if query: - query = '('+query+')' - return query, val - domain_get = tools.cache()(domain_get) - - def unlink(self, cr, uid, ids, context=None): - res = super(ir_rule, self).unlink(cr, uid, ids, context=context) - # Restart the cache on the domain_get method of ir.rule - self.domain_get() - return res - - def create(self, cr, user, vals, context=None): - res = super(ir_rule, self).create(cr, user, vals, context=context) - # Restart the cache on the domain_get method of ir.rule - self.domain_get() - return res - - def write(self, cr, uid, ids, vals, context=None): - if not context: - context={} - res = super(ir_rule, self).write(cr, uid, ids, vals, context=context) - # Restart the cache on the domain_get method - self.domain_get() - return res + _name = 'ir.rule' + _rec_name = 'field_id' + + def _operand(self,cr,uid,context): + + def get(object, level=3, recur=None, root_tech='', root=''): + res = [] + if not recur: + recur = [] + fields = self.pool.get(object).fields_get(cr,uid) + key = fields.keys() + key.sort() + for k in key: + + if fields[k]['type'] in ('many2one'): + res.append((root_tech+'.'+k+'.id', + root+'/'+fields[k]['string'])) + + elif fields[k]['type'] in ('many2many', 'one2many'): + res.append(('\',\'.join(map(lambda x: str(x.id), '+root_tech+'.'+k+'))', + root+'/'+fields[k]['string'])) + + else: + res.append((root_tech+'.'+k, + root+'/'+fields[k]['string'])) + + if (fields[k]['type'] in recur) and (level>0): + res.extend(get(fields[k]['relation'], level-1, + recur, root_tech+'.'+k, root+'/'+fields[k]['string'])) + + return res + + res = [("False", "False"), ("True", "True"), ("user.id", "User")] + res += get('res.users', level=1, + recur=['many2one'], root_tech='user', root='User') + return res + + def _domain_force_get(self, cr, uid, ids, field_name, arg, context={}): + res = {} + for rule in self.browse(cr, uid, ids, context): + if rule.domain_force: + res[rule.id] = eval(rule.domain_force, {'user': self.pool.get('res.users').browse(cr, 1, uid), + 'time':time}) + else: + if rule.operator in ('in', 'child_of'): + dom = eval("[('%s', '%s', [%s])]" % (rule.field_id.name, rule.operator, + rule.operand), {'user': self.pool.get('res.users').browse(cr, 1, uid), + 'time':time}) + else: + dom = eval("[('%s', '%s', %s)]" % (rule.field_id.name, rule.operator, + rule.operand), {'user': self.pool.get('res.users').browse(cr, 1, uid), + 'time':time}) + res[rule.id] = dom + return res + + _columns = { + 'field_id': fields.many2one('ir.model.fields', 'Field',domain= "[('model_id','=', parent.model_id)]", select=1, required=True), + 'operator':fields.selection((('=', '='), ('<>', '<>'), ('<=', '<='), ('>=', '>='), ('in', 'in'), ('child_of', 'child_of')), 'Operator', required=True), + 'operand':fields.selection(_operand,'Operand', size=64, required=True), + 'rule_group': fields.many2one('ir.rule.group', 'Group', select=2, required=True, ondelete="cascade"), + 'domain_force': fields.char('Force Domain', size=250), + 'domain': fields.function(_domain_force_get, method=True, string='Domain', type='char', size=250) + } + + def onchange_all(self, cr, uid, ids, field_id, operator, operand): + if not (field_id or operator or operand): + return {} + + def domain_get(self, cr, uid, model_name): + # root user above constraint + if uid == 1: + return '', [] + + cr.execute("""SELECT r.id FROM + ir_rule r + JOIN (ir_rule_group g + JOIN ir_model m ON (g.model_id = m.id)) + ON (g.id = r.rule_group) + WHERE m.model = %s + AND (g.id IN (SELECT rule_group_id FROM group_rule_group_rel g_rel + JOIN res_groups_users_rel u_rel ON (g_rel.group_id = u_rel.gid) + WHERE u_rel.uid = %d) OR g.global)""", (model_name, uid)) + ids = map(lambda x:x[0], cr.fetchall()) + if not ids: + return '', [] + obj = self.pool.get(model_name) + add = [] + add_str = [] + sub = [] + sub_str = [] + clause={} + clause_global={} + for rule in self.browse(cr, uid, ids): + dom = rule.domain + if rule.rule_group['global']: + clause_global.setdefault(rule.rule_group.id, []) + clause_global[rule.rule_group.id].append(obj._where_calc(cr, uid, dom, active_test=False)) + else: + clause.setdefault(rule.rule_group.id, []) + clause[rule.rule_group.id].append(obj._where_calc(cr, uid, dom, active_test=False)) + + def _query(clause, test): + query = '' + val = [] + for g in clause.values(): + if not g: + continue + if len(query): + query += ' '+test+' ' + query += '(' + first = True + for c in g: + if not first: + query += ' AND ' + first = False + query += '(' + first2 = True + for clause in c[0]: + if not first2: + query += ' AND ' + first2 = False + query += clause + query += ')' + val += c[1] + query += ')' + return query, val + + query, val = _query(clause, 'OR') + query_global, val_global = _query(clause_global, 'OR') + if query_global: + if query: + query = '('+query+') OR '+query_global + val.extend(val_global) + else: + query = query_global + val = val_global + + + if query: + query = '('+query+')' + return query, val + domain_get = tools.cache()(domain_get) + + def unlink(self, cr, uid, ids, context=None): + res = super(ir_rule, self).unlink(cr, uid, ids, context=context) + # Restart the cache on the domain_get method of ir.rule + self.domain_get() + return res + + def create(self, cr, user, vals, context=None): + res = super(ir_rule, self).create(cr, user, vals, context=context) + # Restart the cache on the domain_get method of ir.rule + self.domain_get() + return res + + def write(self, cr, uid, ids, vals, context=None): + if not context: + context={} + res = super(ir_rule, self).write(cr, uid, ids, vals, context=context) + # Restart the cache on the domain_get method + self.domain_get() + return res ir_rule() diff --git a/bin/addons/base/ir/ir_sequence.py b/bin/addons/base/ir/ir_sequence.py index c8941f5c324..7eff555ab10 100644 --- a/bin/addons/base/ir/ir_sequence.py +++ b/bin/addons/base/ir/ir_sequence.py @@ -31,52 +31,52 @@ import time from osv import fields,osv class ir_sequence_type(osv.osv): - _name = 'ir.sequence.type' - _columns = { - 'name': fields.char('Sequence Name',size=64, required=True), - 'code': fields.char('Sequence Code',size=32, required=True), - } + _name = 'ir.sequence.type' + _columns = { + 'name': fields.char('Sequence Name',size=64, required=True), + 'code': fields.char('Sequence Code',size=32, required=True), + } ir_sequence_type() def _code_get(self, cr, uid, context={}): - cr.execute('select code, name from ir_sequence_type') - return cr.fetchall() + cr.execute('select code, name from ir_sequence_type') + return cr.fetchall() class ir_sequence(osv.osv): - _name = 'ir.sequence' - _columns = { - 'name': fields.char('Sequence Name',size=64, required=True), - 'code': fields.selection(_code_get, 'Sequence Code',size=64, required=True), - 'active': fields.boolean('Active'), - 'prefix': fields.char('Prefix',size=64), - 'suffix': fields.char('Suffix',size=64), - 'number_next': fields.integer('Next Number', required=True), - 'number_increment': fields.integer('Increment Number', required=True), - 'padding' : fields.integer('Number padding', required=True), - } - _defaults = { - 'active': lambda *a: True, - 'number_increment': lambda *a: 1, - 'number_next': lambda *a: 1, - 'padding' : lambda *a : 0, - } + _name = 'ir.sequence' + _columns = { + 'name': fields.char('Sequence Name',size=64, required=True), + 'code': fields.selection(_code_get, 'Sequence Code',size=64, required=True), + 'active': fields.boolean('Active'), + 'prefix': fields.char('Prefix',size=64), + 'suffix': fields.char('Suffix',size=64), + 'number_next': fields.integer('Next Number', required=True), + 'number_increment': fields.integer('Increment Number', required=True), + 'padding' : fields.integer('Number padding', required=True), + } + _defaults = { + 'active': lambda *a: True, + 'number_increment': lambda *a: 1, + 'number_next': lambda *a: 1, + 'padding' : lambda *a : 0, + } - def _process(self, s): - return (s or '') % {'year':time.strftime('%Y'), 'month': time.strftime('%m'), 'day':time.strftime('%d')} + def _process(self, s): + return (s or '') % {'year':time.strftime('%Y'), 'month': time.strftime('%m'), 'day':time.strftime('%d')} - def get_id(self, cr, uid, sequence_id, test='id=%d'): - cr.execute('lock table ir_sequence') - cr.execute('select id,number_next,number_increment,prefix,suffix,padding from ir_sequence where '+test+' and active=True', (sequence_id,)) - res = cr.dictfetchone() - if res: - cr.execute('update ir_sequence set number_next=number_next+number_increment where id=%d and active=True', (res['id'],)) - if res['number_next']: - return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix']) - else: - return self._process(res['prefix']) + self._process(res['suffix']) - return False + def get_id(self, cr, uid, sequence_id, test='id=%d'): + cr.execute('lock table ir_sequence') + cr.execute('select id,number_next,number_increment,prefix,suffix,padding from ir_sequence where '+test+' and active=True', (sequence_id,)) + res = cr.dictfetchone() + if res: + cr.execute('update ir_sequence set number_next=number_next+number_increment where id=%d and active=True', (res['id'],)) + if res['number_next']: + return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix']) + else: + return self._process(res['prefix']) + self._process(res['suffix']) + return False - def get(self, cr, uid, code): - return self.get_id(cr, uid, code, test='code=%s') + def get(self, cr, uid, code): + return self.get_id(cr, uid, code, test='code=%s') ir_sequence() diff --git a/bin/addons/base/ir/ir_translation.py b/bin/addons/base/ir/ir_translation.py index ac87f08d76a..bda2654b033 100644 --- a/bin/addons/base/ir/ir_translation.py +++ b/bin/addons/base/ir/ir_translation.py @@ -32,131 +32,131 @@ from osv.osv import Cacheable import tools TRANSLATION_TYPE = [ - ('field', 'Field'), - ('model', 'Model'), - ('rml', 'RML'), - ('selection', 'Selection'), - ('view', 'View'), - ('wizard_button', 'Wizard Button'), - ('wizard_field', 'Wizard Field'), - ('wizard_view', 'Wizard View'), - ('xsl', 'XSL'), - ('help', 'Help'), - ('code', 'Code'), - ('constraint', 'Constraint'), + ('field', 'Field'), + ('model', 'Model'), + ('rml', 'RML'), + ('selection', 'Selection'), + ('view', 'View'), + ('wizard_button', 'Wizard Button'), + ('wizard_field', 'Wizard Field'), + ('wizard_view', 'Wizard View'), + ('xsl', 'XSL'), + ('help', 'Help'), + ('code', 'Code'), + ('constraint', 'Constraint'), ] class ir_translation(osv.osv, Cacheable): - _name = "ir.translation" - _log_access = False + _name = "ir.translation" + _log_access = False - def _get_language(self, cr, uid, context): - lang_obj = self.pool.get('res.lang') - lang_ids = lang_obj.search(cr, uid, [('translatable', '=', True)], - context=context) - langs = lang_obj.browse(cr, uid, lang_ids, context=context) - res = [(lang.code, lang.name) for lang in langs] - for lang_dict in tools.scan_languages(): - if lang_dict not in res: - res.append(lang_dict) - return res + def _get_language(self, cr, uid, context): + lang_obj = self.pool.get('res.lang') + lang_ids = lang_obj.search(cr, uid, [('translatable', '=', True)], + context=context) + langs = lang_obj.browse(cr, uid, lang_ids, context=context) + res = [(lang.code, lang.name) for lang in langs] + for lang_dict in tools.scan_languages(): + if lang_dict not in res: + res.append(lang_dict) + return res - _columns = { - 'name': fields.char('Field Name', size=128, required=True), - 'res_id': fields.integer('Resource ID'), - 'lang': fields.selection(_get_language, string='Language', size=5), - 'type': fields.selection(TRANSLATION_TYPE, string='Type', size=16), - 'src': fields.text('Source'), - 'value': fields.text('Translation Value'), - } - _sql = """ - create index ir_translation_ltn on ir_translation (lang,type,name); - create index ir_translation_res_id on ir_translation (res_id); - """ + _columns = { + 'name': fields.char('Field Name', size=128, required=True), + 'res_id': fields.integer('Resource ID'), + 'lang': fields.selection(_get_language, string='Language', size=5), + 'type': fields.selection(TRANSLATION_TYPE, string='Type', size=16), + 'src': fields.text('Source'), + 'value': fields.text('Translation Value'), + } + _sql = """ + create index ir_translation_ltn on ir_translation (lang,type,name); + create index ir_translation_res_id on ir_translation (res_id); + """ - def _get_ids(self, cr, uid, name, tt, lang, ids): - translations, to_fetch = {}, [] - for id in ids: - trans = self.get((lang, name, id)) - if trans is not None: - translations[id] = trans - else: - to_fetch.append(id) - if to_fetch: - cr.execute('select res_id,value ' \ - 'from ir_translation ' \ - 'where lang=%s ' \ - 'and type=%s ' \ - 'and name=%s ' \ - 'and res_id in ('+','.join(map(str, to_fetch))+')', - (lang,tt,name)) - for res_id, value in cr.fetchall(): - self.add((lang, tt, name, res_id), value) - translations[res_id] = value - for res_id in ids: - if res_id not in translations: - self.add((lang, tt, name, res_id), False) - translations[res_id] = False - return translations + def _get_ids(self, cr, uid, name, tt, lang, ids): + translations, to_fetch = {}, [] + for id in ids: + trans = self.get((lang, name, id)) + if trans is not None: + translations[id] = trans + else: + to_fetch.append(id) + if to_fetch: + cr.execute('select res_id,value ' \ + 'from ir_translation ' \ + 'where lang=%s ' \ + 'and type=%s ' \ + 'and name=%s ' \ + 'and res_id in ('+','.join(map(str, to_fetch))+')', + (lang,tt,name)) + for res_id, value in cr.fetchall(): + self.add((lang, tt, name, res_id), value) + translations[res_id] = value + for res_id in ids: + if res_id not in translations: + self.add((lang, tt, name, res_id), False) + translations[res_id] = False + return translations - def _set_ids(self, cr, uid, name, tt, lang, ids, value): - cr.execute('delete from ir_translation ' \ - 'where lang=%s ' \ - 'and type=%s ' \ - 'and name=%s ' \ - 'and res_id in ('+','.join(map(str,ids))+')', - (lang,tt,name)) - for id in ids: - self.create(cr, uid, { - 'lang':lang, - 'type':tt, - 'name':name, - 'res_id':id, - 'value':value, - }) - return len(ids) + def _set_ids(self, cr, uid, name, tt, lang, ids, value): + cr.execute('delete from ir_translation ' \ + 'where lang=%s ' \ + 'and type=%s ' \ + 'and name=%s ' \ + 'and res_id in ('+','.join(map(str,ids))+')', + (lang,tt,name)) + for id in ids: + self.create(cr, uid, { + 'lang':lang, + 'type':tt, + 'name':name, + 'res_id':id, + 'value':value, + }) + return len(ids) - def _get_source(self, cr, uid, name, tt, lang, source=None): - trans = self.get((lang, tt, name, source)) - if trans is not None: - return trans + def _get_source(self, cr, uid, name, tt, lang, source=None): + trans = self.get((lang, tt, name, source)) + if trans is not None: + return trans - if source: - #if isinstance(source, unicode): - # source = source.encode('utf8') - cr.execute('select value ' \ - 'from ir_translation ' \ - 'where lang=%s ' \ - 'and type=%s ' \ - 'and name=%s ' \ - 'and src=%s', - (lang, tt, str(name), source)) - else: - cr.execute('select value ' \ - 'from ir_translation ' \ - 'where lang=%s ' \ - 'and type=%s ' \ - 'and name=%s', - (lang, tt, str(name))) - res = cr.fetchone() - - trad = res and res[0] or '' - self.add((lang, tt, name, source), trad) - return trad + if source: + #if isinstance(source, unicode): + # source = source.encode('utf8') + cr.execute('select value ' \ + 'from ir_translation ' \ + 'where lang=%s ' \ + 'and type=%s ' \ + 'and name=%s ' \ + 'and src=%s', + (lang, tt, str(name), source)) + else: + cr.execute('select value ' \ + 'from ir_translation ' \ + 'where lang=%s ' \ + 'and type=%s ' \ + 'and name=%s', + (lang, tt, str(name))) + res = cr.fetchone() + + trad = res and res[0] or '' + self.add((lang, tt, name, source), trad) + return trad - def unlink(self, cursor, user, ids, context=None): - self.clear() - return super(ir_translation, self).unlink(cusor, user, ids, - context=context) + def unlink(self, cursor, user, ids, context=None): + self.clear() + return super(ir_translation, self).unlink(cusor, user, ids, + context=context) - def create(self, cursor, user, vals, context=None): - self.clear() - return super(ir_translation, self).create(cursor, user, vals, - context=context) + def create(self, cursor, user, vals, context=None): + self.clear() + return super(ir_translation, self).create(cursor, user, vals, + context=context) - def write(self, cursor, user, ids, vals, context=None): - self.clear() - return super(ir_translation, self).write(cursor, user, ids, vals, - context=context) + def write(self, cursor, user, ids, vals, context=None): + self.clear() + return super(ir_translation, self).write(cursor, user, ids, vals, + context=context) ir_translation() diff --git a/bin/addons/base/ir/ir_ui_menu.py b/bin/addons/base/ir/ir_ui_menu.py index 13445bb8df9..2fa9268776c 100644 --- a/bin/addons/base/ir/ir_ui_menu.py +++ b/bin/addons/base/ir/ir_ui_menu.py @@ -32,12 +32,12 @@ from osv.orm import browse_null, browse_record import re def one_in(setA, setB): - """Check the presence of an element of setA in setB - """ - for x in setA: - if x in setB: - return True - return False + """Check the presence of an element of setA in setB + """ + for x in setA: + if x in setB: + return True + return False icons = map(lambda x: (x,x), ['STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD', 'STOCK_CANCEL', 'STOCK_CDROM', 'STOCK_CLEAR', 'STOCK_CLOSE', 'STOCK_COLOR_PICKER', @@ -68,143 +68,143 @@ icons = map(lambda x: (x,x), ['STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_ ]) 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+'=%d AND '+self._id2+'=%d', (id, act[1])) - if cr.fetchall(): - val.remove(act) - return super(many2many_unique, self).set(cr, obj, id, name, val, user=user, - context=context) + 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+'=%d AND '+self._id2+'=%d', (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' - def search(self, cr, uid, args, offset=0, limit=2000, order=None, - context=None, count=False): - if context is None: - context = {} - ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, - context=context) - if uid==1: - return ids - user_groups = self.pool.get('res.users').read(cr, uid, [uid])[0]['groups_id'] - result = [] - for menu in self.browse(cr, uid, ids): - if not len(menu.groups_id): - result.append(menu.id) - continue - for g in menu.groups_id: - if g.id in user_groups: - result.append(menu.id) - break - return result + _name = 'ir.ui.menu' + def search(self, cr, uid, args, offset=0, limit=2000, order=None, + context=None, count=False): + if context is None: + context = {} + ids = osv.orm.orm.search(self, cr, uid, args, offset, limit, order, + context=context) + if uid==1: + return ids + user_groups = self.pool.get('res.users').read(cr, uid, [uid])[0]['groups_id'] + result = [] + for menu in self.browse(cr, uid, ids): + if not len(menu.groups_id): + result.append(menu.id) + continue + for g in menu.groups_id: + if g.id in user_groups: + result.append(menu.id) + break + return result - def _get_full_name(self, cr, uid, ids, name, args, context): - res = {} - for m in self.browse(cr, uid, ids, context=context): - res[m.id] = self._get_one_full_name(m) - return res + def _get_full_name(self, cr, uid, ids, name, args, context): + res = {} + for m in self.browse(cr, uid, ids, context=context): + res[m.id] = self._get_one_full_name(m) + return res - def _get_one_full_name(self, menu, level=6): - if level<=0: - return '...' - if menu.parent_id: - parent_path = self._get_one_full_name(menu.parent_id, level-1) + "/" - else: - parent_path = '' - return parent_path + menu.name + def _get_one_full_name(self, menu, level=6): + if level<=0: + return '...' + if menu.parent_id: + parent_path = self._get_one_full_name(menu.parent_id, level-1) + "/" + else: + parent_path = '' + return parent_path + menu.name - def copy(self, cr, uid, id, default=None, context=None): - ir_values_obj = self.pool.get('ir.values') - res = super(ir_ui_menu, self).copy(cr, uid, id, context=context) - datas=self.read(cr,uid,[res],['name'])[0] - rex=re.compile('\([0-9]+\)') - concat=rex.findall(datas['name']) - if concat: - next_num=eval(concat[0])+1 - datas['name']=rex.sub(('(%d)'%next_num),datas['name']) - else: - datas['name']=datas['name']+'(1)' - self.write(cr,uid,[res],{'name':datas['name']}) - ids = ir_values_obj.search(cr, uid, [ - ('model', '=', 'ir.ui.menu'), - ('res_id', '=', id), - ]) - for iv in ir_values_obj.browse(cr, uid, ids): - new_id = ir_values_obj.copy(cr, uid, iv.id, - default={'res_id': res}, context=context) - return res + def copy(self, cr, uid, id, default=None, context=None): + ir_values_obj = self.pool.get('ir.values') + res = super(ir_ui_menu, self).copy(cr, uid, id, context=context) + datas=self.read(cr,uid,[res],['name'])[0] + rex=re.compile('\([0-9]+\)') + concat=rex.findall(datas['name']) + if concat: + next_num=eval(concat[0])+1 + datas['name']=rex.sub(('(%d)'%next_num),datas['name']) + else: + datas['name']=datas['name']+'(1)' + self.write(cr,uid,[res],{'name':datas['name']}) + ids = ir_values_obj.search(cr, uid, [ + ('model', '=', 'ir.ui.menu'), + ('res_id', '=', id), + ]) + for iv in ir_values_obj.browse(cr, uid, ids): + new_id = ir_values_obj.copy(cr, uid, iv.id, + default={'res_id': res}, context=context) + return res - 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, [ - ('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): - values_action[value.res_id] = value.value - for menu_id in ids: - res[menu_id] = values_action.get(menu_id, False) - return res + 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, [ + ('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): + values_action[value.res_id] = value.value + for menu_id in ids: + res[menu_id] = values_action.get(menu_id, False) + return res - def _action_inv(self, cursor, user, menu_id, name, value, arg, context=None): - if context is None: - context = {} - ctx = context.copy() - if 'read_delta' in ctx: - del ctx['read_delta'] - values_obj = self.pool.get('ir.values') - values_ids = 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[0], {'value': value}, - context=ctx) - else: - values_obj.create(cursor, user, { - 'name': 'Menuitem', - 'model': self._name, - 'value': value, - 'object': True, - 'key': 'action', - 'key2': 'tree_but_open', - 'res_id': menu_id, - }, context=ctx) + def _action_inv(self, cursor, user, menu_id, name, value, arg, context=None): + if context is None: + context = {} + ctx = context.copy() + if 'read_delta' in ctx: + del ctx['read_delta'] + values_obj = self.pool.get('ir.values') + values_ids = 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[0], {'value': value}, + context=ctx) + else: + values_obj.create(cursor, user, { + 'name': 'Menuitem', + 'model': self._name, + 'value': value, + 'object': True, + 'key': 'action', + 'key2': 'tree_but_open', + 'res_id': menu_id, + }, context=ctx) - _columns = { - 'name': fields.char('Menu', size=64, required=True, translate=True), - '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', - 'menu_id', 'gid', 'Groups'), - 'complete_name': fields.function(_get_full_name, method=True, - string='Complete Name', type='char', size=128), - 'icon': fields.selection(icons, 'Icon', size=64), - 'action': fields.function(_action, fnct_inv=_action_inv, - method=True, type='reference', string='Action', - selection=[ - ('ir.actions.report.custom', 'ir.actions.report.custom'), - ('ir.actions.report.xml', 'ir.actions.report.xml'), - ('ir.actions.act_window', 'ir.actions.act_window'), - ('ir.actions.wizard', 'ir.actions.wizard'), + _columns = { + 'name': fields.char('Menu', size=64, required=True, translate=True), + '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', + 'menu_id', 'gid', 'Groups'), + 'complete_name': fields.function(_get_full_name, method=True, + string='Complete Name', type='char', size=128), + 'icon': fields.selection(icons, 'Icon', size=64), + 'action': fields.function(_action, fnct_inv=_action_inv, + method=True, type='reference', string='Action', + selection=[ + ('ir.actions.report.custom', 'ir.actions.report.custom'), + ('ir.actions.report.xml', 'ir.actions.report.xml'), + ('ir.actions.act_window', 'ir.actions.act_window'), + ('ir.actions.wizard', 'ir.actions.wizard'), ('ir.actions.url', 'ir.actions.act_url'), - ]), - } - _defaults = { - 'icon' : lambda *a: 'STOCK_OPEN', - 'sequence' : lambda *a: 10 - } - _order = "sequence,id" + ]), + } + _defaults = { + 'icon' : lambda *a: 'STOCK_OPEN', + 'sequence' : lambda *a: 10 + } + _order = "sequence,id" ir_ui_menu() diff --git a/bin/addons/base/ir/ir_ui_view.py b/bin/addons/base/ir/ir_ui_view.py index 2ab5fa16895..75d93f04d3a 100644 --- a/bin/addons/base/ir/ir_ui_view.py +++ b/bin/addons/base/ir/ir_ui_view.py @@ -34,89 +34,89 @@ import netsvc import os def _check_xml(self, cr, uid, ids, context={}): - for view in self.browse(cr, uid, ids, context): - eview = etree.fromstring(view.arch) - frng = tools.file_open(os.path.join('base','rng',view.type+'.rng')) - relaxng = etree.RelaxNG(file=frng) - if not relaxng.validate(eview): - logger = netsvc.Logger() - logger.notifyChannel('init', netsvc.LOG_ERROR, 'The view do not fit the required schema !') - logger.notifyChannel('init', netsvc.LOG_ERROR, relaxng.error_log.last_error) - print view.arch - return False - return True + for view in self.browse(cr, uid, ids, context): + eview = etree.fromstring(view.arch) + frng = tools.file_open(os.path.join('base','rng',view.type+'.rng')) + relaxng = etree.RelaxNG(file=frng) + if not relaxng.validate(eview): + logger = netsvc.Logger() + logger.notifyChannel('init', netsvc.LOG_ERROR, 'The view do not fit the required schema !') + logger.notifyChannel('init', netsvc.LOG_ERROR, relaxng.error_log.last_error) + print view.arch + return False + return True class view(osv.osv): - _name = 'ir.ui.view' - _columns = { - 'name': fields.char('View Name',size=64, required=True), - 'model': fields.char('Model', size=64, required=True), - 'priority': fields.integer('Priority', required=True), - 'type': fields.selection(( - ('tree','Tree'), - ('form','Form'), - ('graph', 'Graph'), - ('calendar', 'Calendar')), 'View Type', required=True), - 'arch': fields.text('View Architecture', required=True), - 'inherit_id': fields.many2one('ir.ui.view', 'Inherited View'), - 'field_parent': fields.char('Childs Field',size=64), - } - _defaults = { - 'arch': lambda *a: '<?xml version="1.0"?>\n<tree title="Unknwown">\n\t<field name="name"/>\n</tree>', - 'priority': lambda *a: 16 - } - _order = "priority" - _constraints = [ - (_check_xml, 'Invalid XML for View Architecture!', ['arch']) - ] + _name = 'ir.ui.view' + _columns = { + 'name': fields.char('View Name',size=64, required=True), + 'model': fields.char('Model', size=64, required=True), + 'priority': fields.integer('Priority', required=True), + 'type': fields.selection(( + ('tree','Tree'), + ('form','Form'), + ('graph', 'Graph'), + ('calendar', 'Calendar')), 'View Type', required=True), + 'arch': fields.text('View Architecture', required=True), + 'inherit_id': fields.many2one('ir.ui.view', 'Inherited View'), + 'field_parent': fields.char('Childs Field',size=64), + } + _defaults = { + 'arch': lambda *a: '<?xml version="1.0"?>\n<tree title="Unknwown">\n\t<field name="name"/>\n</tree>', + 'priority': lambda *a: 16 + } + _order = "priority" + _constraints = [ + (_check_xml, 'Invalid XML for View Architecture!', ['arch']) + ] view() #class UserView(osv.osv): -# _name = 'ir.ui.view.user' -# _columns = { -# 'name': fields.char('View Name',size=64, required=True), -# 'model': fields.char('Model', size=64, required=True), -# 'priority': fields.integer('Priority', required=True), -# 'type': fields.selection(( -# ('tree','Tree'), -# ('form','Form'), -# ('graph', 'Graph'), -# ('calendar', 'Calendar')), 'View Type', required=True), -# 'arch': fields.text('View Architecture', required=True), -# 'inherit_id': fields.many2one('ir.ui.view', 'Inherited View'), -# 'field_parent': fields.char('Childs Field',size=64), -# 'user_id': fields.many2one('res.users', 'User'), -# 'ref_id': fields.many2one('ir.ui.view', 'Inherited View'), -# } -# _defaults = { -# 'arch': lambda *a: '<?xml version="1.0"?>\n<tree title="Unknwown">\n\t<field name="name"/>\n</tree>', -# 'priority': lambda *a: 16 -# } -# _order = "priority" -# _constraints = [ -# (_check_xml, 'Invalid XML for View Architecture!', ['arch']) -# ] +# _name = 'ir.ui.view.user' +# _columns = { +# 'name': fields.char('View Name',size=64, required=True), +# 'model': fields.char('Model', size=64, required=True), +# 'priority': fields.integer('Priority', required=True), +# 'type': fields.selection(( +# ('tree','Tree'), +# ('form','Form'), +# ('graph', 'Graph'), +# ('calendar', 'Calendar')), 'View Type', required=True), +# 'arch': fields.text('View Architecture', required=True), +# 'inherit_id': fields.many2one('ir.ui.view', 'Inherited View'), +# 'field_parent': fields.char('Childs Field',size=64), +# 'user_id': fields.many2one('res.users', 'User'), +# 'ref_id': fields.many2one('ir.ui.view', 'Inherited View'), +# } +# _defaults = { +# 'arch': lambda *a: '<?xml version="1.0"?>\n<tree title="Unknwown">\n\t<field name="name"/>\n</tree>', +# 'priority': lambda *a: 16 +# } +# _order = "priority" +# _constraints = [ +# (_check_xml, 'Invalid XML for View Architecture!', ['arch']) +# ] # #UserView() class view_sc(osv.osv): - _name = 'ir.ui.view_sc' - _columns = { - 'name': fields.char('Shortcut Name', size=64, required=True), - 'res_id': fields.many2one('ir.values','Resource Ref.', ondelete='cascade'), - 'sequence': fields.integer('Sequence'), - 'user_id': fields.many2one('res.users', 'User Ref.', required=True, ondelete='cascade'), - 'resource': fields.char('Resource Name', size=64, required=True) - } + _name = 'ir.ui.view_sc' + _columns = { + 'name': fields.char('Shortcut Name', size=64, required=True), + 'res_id': fields.many2one('ir.values','Resource Ref.', ondelete='cascade'), + 'sequence': fields.integer('Sequence'), + 'user_id': fields.many2one('res.users', 'User Ref.', required=True, ondelete='cascade'), + 'resource': fields.char('Resource Name', size=64, required=True) + } - def get_sc(self, cr, uid, user_id, model='ir.ui.menu', context={}): - ids = self.search(cr, uid, [('user_id','=',user_id),('resource','=',model)], context=context) - return self.read(cr, uid, ids, ['res_id','name'], context=context) + def get_sc(self, cr, uid, user_id, model='ir.ui.menu', context={}): + ids = self.search(cr, uid, [('user_id','=',user_id),('resource','=',model)], context=context) + return self.read(cr, uid, ids, ['res_id','name'], context=context) - _order = 'sequence' - _defaults = { - 'resource': lambda *a: 'ir.ui.menu', - 'user_id': lambda obj, cr, uid, context: uid, - } + _order = 'sequence' + _defaults = { + 'resource': lambda *a: 'ir.ui.menu', + 'user_id': lambda obj, cr, uid, context: uid, + } view_sc() diff --git a/bin/addons/base/ir/ir_values.py b/bin/addons/base/ir/ir_values.py index 84fafefa3f4..2681e562ec7 100644 --- a/bin/addons/base/ir/ir_values.py +++ b/bin/addons/base/ir/ir_values.py @@ -32,221 +32,221 @@ import pickle from tools.translate import _ class ir_values(osv.osv): - _name = 'ir.values' - - def _value_unpickle(self, cursor, user, ids, name, arg, context=None): - res = {} - for report in self.browse(cursor, user, ids, context=context): - value = report[name[:-9]] - if not report.object and value: - try: - value = str(pickle.loads(value)) - except: - pass - res[report.id] = value - return res - - def _value_pickle(self, cursor, user, id, name, value, arg, context=None): - if context is None: - context = {} - ctx = context.copy() - if 'read_delta' in ctx: - del ctx['read_delta'] - if not self.browse(cursor, user, id, context=context).object: - value = pickle.dumps(eval(value)) - self.write(cursor, user, id, {name[:-9]: value}, context=ctx) - - _columns = { - 'name': fields.char('Name', size=128), - 'model': fields.char('Model', size=128), - 'value': fields.text('Value'), - 'value_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle, - method=True, type='text', string='Value'), - 'object': fields.boolean('Is Object'), - 'key': fields.char('Type', size=128), - 'key2': fields.char('Value', size=256), - 'meta': fields.text('Meta Datas'), - 'meta_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle, - method=True, type='text', string='Meta Datas'), - 'res_id': fields.integer('Resource ID'), - 'user_id': fields.many2one('res.users', 'User', ondelete='cascade'), - 'company_id': fields.many2one('res.company', 'Company') - } - _defaults = { - 'key': lambda *a: 'action', - 'key2': lambda *a: 'tree_but_open', - 'company_id': lambda *a: False - } - - def _auto_init(self, cr, context={}): - super(ir_values, self)._auto_init(cr, context) - cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_values_key_model_key2_index\'') - if not cr.fetchone(): - cr.execute('CREATE INDEX ir_values_key_model_key2_index ON ir_values (key, model, key2)') - cr.commit() - - def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False): - if type(value)==type(u''): - value = value.encode('utf8') - if not isobject: - value = pickle.dumps(value) - if meta: - meta = pickle.dumps(meta) - ids_res = [] - for model in models: - if type(model)==type([]) or type(model)==type(()): - model,res_id = model - else: - res_id=False - if replace: - if key in ('meta', 'default'): - ids = self.search(cr, uid, [ - ('key', '=', key), - ('key2', '=', key2), - ('name', '=', name), - ('model', '=', model), - ('res_id', '=', res_id), - ('user_id', '=', preserve_user and uid) - ]) - else: - ids = self.search(cr, uid, [ - ('key', '=', key), - ('key2', '=', key2), - ('value', '=', value), - ('model', '=', model), - ('res_id', '=', res_id), - ('user_id', '=', preserve_user and uid) - ]) - self.unlink(cr, uid, ids) - vals = { - 'name': name, - 'value': value, - 'model': model, - 'object': isobject, - 'key': key, - 'key2': key2 and key2[:200], - 'meta': meta, - 'user_id': preserve_user and uid, - } - if company: - cid = self.pool.get('res.users').browse(cr, uid, uid, context={}).company_id.id - vals['company_id']=cid - if res_id: - vals['res_id']= res_id - ids_res.append(self.create(cr, uid, vals)) - return ids_res - - def get(self, cr, uid, key, key2, models, meta=False, context={}, res_id_req=False, without_user=True, key2_req=True): - result = [] - for m in models: - if type(m)==type([]) or type(m)==type(()): - m,res_id = m - else: - res_id=False - - where1 = ['key=%s','model=%s'] - where2 = [key,str(m)] - where_opt = [] - if key2: - where1.append('key2=%s') - where2.append(key2[:200]) - else: - dest = where1 - if not key2_req or meta: - dest=where_opt - dest.append('key2 is null') - - if res_id_req and (models[-1][0]==m): - if res_id: - where1.append('res_id=%d' % (res_id,)) - else: - where1.append('(res_id is NULL)') - elif res_id: - if (models[-1][0]==m): - where1.append('(res_id=%d or (res_id is null))' % (res_id,)) - where_opt.append('res_id=%d' % (res_id,)) - else: - where1.append('res_id=%d' % (res_id,)) - -# if not without_user: - where_opt.append('user_id=%d' % (uid,)) - - - result = [] - ok = True - while ok: - if not where_opt: - cr.execute('select id from ir_values where ' +\ - ' and '.join(where1)+' and user_id is null', where2) - else: - cr.execute('select id from ir_values where ' +\ - ' and '.join(where1+where_opt), where2) - result.extend([x[0] for x in cr.fetchall()]) - if len(where_opt): - where_opt.pop() - else: - ok = False - - if result: - break - - if not result: - return [] - cid = self.pool.get('res.users').browse(cr, uid, uid, context={}).company_id.id - cr.execute('select id,name,value,object,meta, key ' \ - 'from ir_values ' \ - 'where id in ('+','.join(map(str,result))+') ' \ - 'and (company_id is null or company_id = %d) '\ - 'ORDER BY user_id', (cid,)) - result = cr.fetchall() - - def _result_get(x, keys): - if x[1] in keys: - return False - keys.append(x[1]) - if x[3]: - model,id = x[2].split(',') - try: - id = int(id) - datas = self.pool.get(model).read(cr, uid, [id], False, context) - except: - return False - if not len(datas): - #ir_del(cr, uid, x[0]) - return False - def clean(x): - for key in ('report_sxw_content', 'report_rml_content', - 'report_sxw', 'report_rml', 'report_sxw_content_data', - 'report_rml_content_data'): - if key in x: - del x[key] - return x - datas = clean(datas[0]) - else: - datas = pickle.loads(x[2]) - if meta: - meta2 = pickle.loads(x[4]) - return (x[0],x[1],datas,meta2) - return (x[0],x[1],datas) - keys = [] - res = filter(bool, map(lambda x: _result_get(x, keys), list(result))) - res2 = res[:] - for r in res: - if type(r) == type([]): - if r[2]['type'] == 'ir.actions.report.xml' or r[2]['type'] == 'ir.actions.report.xml': - print - if r[2].has_key('groups_id'): - groups = r[2]['groups_id'] - if len(groups) > 0: - group_ids = ','.join([ str(x) for x in r[2]['groups_id']]) - cr.execute("select count(*) from res_groups_users_rel where gid in (%s) and uid='%s'" % (group_ids, uid)) - gr_ids = cr.fetchall() - if not gr_ids[0][0] > 0: - res2.remove(r) - else: - #raise osv.except_osv('Error !','You have not permission to perform operation !!!') - res2.remove(r) - return res2 + _name = 'ir.values' + + def _value_unpickle(self, cursor, user, ids, name, arg, context=None): + res = {} + for report in self.browse(cursor, user, ids, context=context): + value = report[name[:-9]] + if not report.object and value: + try: + value = str(pickle.loads(value)) + except: + pass + res[report.id] = value + return res + + def _value_pickle(self, cursor, user, id, name, value, arg, context=None): + if context is None: + context = {} + ctx = context.copy() + if 'read_delta' in ctx: + del ctx['read_delta'] + if not self.browse(cursor, user, id, context=context).object: + value = pickle.dumps(eval(value)) + self.write(cursor, user, id, {name[:-9]: value}, context=ctx) + + _columns = { + 'name': fields.char('Name', size=128), + 'model': fields.char('Model', size=128), + 'value': fields.text('Value'), + 'value_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle, + method=True, type='text', string='Value'), + 'object': fields.boolean('Is Object'), + 'key': fields.char('Type', size=128), + 'key2': fields.char('Value', size=256), + 'meta': fields.text('Meta Datas'), + 'meta_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle, + method=True, type='text', string='Meta Datas'), + 'res_id': fields.integer('Resource ID'), + 'user_id': fields.many2one('res.users', 'User', ondelete='cascade'), + 'company_id': fields.many2one('res.company', 'Company') + } + _defaults = { + 'key': lambda *a: 'action', + 'key2': lambda *a: 'tree_but_open', + 'company_id': lambda *a: False + } + + def _auto_init(self, cr, context={}): + super(ir_values, self)._auto_init(cr, context) + cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_values_key_model_key2_index\'') + if not cr.fetchone(): + cr.execute('CREATE INDEX ir_values_key_model_key2_index ON ir_values (key, model, key2)') + cr.commit() + + def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False): + if type(value)==type(u''): + value = value.encode('utf8') + if not isobject: + value = pickle.dumps(value) + if meta: + meta = pickle.dumps(meta) + ids_res = [] + for model in models: + if type(model)==type([]) or type(model)==type(()): + model,res_id = model + else: + res_id=False + if replace: + if key in ('meta', 'default'): + ids = self.search(cr, uid, [ + ('key', '=', key), + ('key2', '=', key2), + ('name', '=', name), + ('model', '=', model), + ('res_id', '=', res_id), + ('user_id', '=', preserve_user and uid) + ]) + else: + ids = self.search(cr, uid, [ + ('key', '=', key), + ('key2', '=', key2), + ('value', '=', value), + ('model', '=', model), + ('res_id', '=', res_id), + ('user_id', '=', preserve_user and uid) + ]) + self.unlink(cr, uid, ids) + vals = { + 'name': name, + 'value': value, + 'model': model, + 'object': isobject, + 'key': key, + 'key2': key2 and key2[:200], + 'meta': meta, + 'user_id': preserve_user and uid, + } + if company: + cid = self.pool.get('res.users').browse(cr, uid, uid, context={}).company_id.id + vals['company_id']=cid + if res_id: + vals['res_id']= res_id + ids_res.append(self.create(cr, uid, vals)) + return ids_res + + def get(self, cr, uid, key, key2, models, meta=False, context={}, res_id_req=False, without_user=True, key2_req=True): + result = [] + for m in models: + if type(m)==type([]) or type(m)==type(()): + m,res_id = m + else: + res_id=False + + where1 = ['key=%s','model=%s'] + where2 = [key,str(m)] + where_opt = [] + if key2: + where1.append('key2=%s') + where2.append(key2[:200]) + else: + dest = where1 + if not key2_req or meta: + dest=where_opt + dest.append('key2 is null') + + if res_id_req and (models[-1][0]==m): + if res_id: + where1.append('res_id=%d' % (res_id,)) + else: + where1.append('(res_id is NULL)') + elif res_id: + if (models[-1][0]==m): + where1.append('(res_id=%d or (res_id is null))' % (res_id,)) + where_opt.append('res_id=%d' % (res_id,)) + else: + where1.append('res_id=%d' % (res_id,)) + +# if not without_user: + where_opt.append('user_id=%d' % (uid,)) + + + result = [] + ok = True + while ok: + if not where_opt: + cr.execute('select id from ir_values where ' +\ + ' and '.join(where1)+' and user_id is null', where2) + else: + cr.execute('select id from ir_values where ' +\ + ' and '.join(where1+where_opt), where2) + result.extend([x[0] for x in cr.fetchall()]) + if len(where_opt): + where_opt.pop() + else: + ok = False + + if result: + break + + if not result: + return [] + cid = self.pool.get('res.users').browse(cr, uid, uid, context={}).company_id.id + cr.execute('select id,name,value,object,meta, key ' \ + 'from ir_values ' \ + 'where id in ('+','.join(map(str,result))+') ' \ + 'and (company_id is null or company_id = %d) '\ + 'ORDER BY user_id', (cid,)) + result = cr.fetchall() + + def _result_get(x, keys): + if x[1] in keys: + return False + keys.append(x[1]) + if x[3]: + model,id = x[2].split(',') + try: + id = int(id) + datas = self.pool.get(model).read(cr, uid, [id], False, context) + except: + return False + if not len(datas): + #ir_del(cr, uid, x[0]) + return False + def clean(x): + for key in ('report_sxw_content', 'report_rml_content', + 'report_sxw', 'report_rml', 'report_sxw_content_data', + 'report_rml_content_data'): + if key in x: + del x[key] + return x + datas = clean(datas[0]) + else: + datas = pickle.loads(x[2]) + if meta: + meta2 = pickle.loads(x[4]) + return (x[0],x[1],datas,meta2) + return (x[0],x[1],datas) + keys = [] + res = filter(bool, map(lambda x: _result_get(x, keys), list(result))) + res2 = res[:] + for r in res: + if type(r) == type([]): + if r[2]['type'] == 'ir.actions.report.xml' or r[2]['type'] == 'ir.actions.report.xml': + print + if r[2].has_key('groups_id'): + groups = r[2]['groups_id'] + if len(groups) > 0: + group_ids = ','.join([ str(x) for x in r[2]['groups_id']]) + cr.execute("select count(*) from res_groups_users_rel where gid in (%s) and uid='%s'" % (group_ids, uid)) + gr_ids = cr.fetchall() + if not gr_ids[0][0] > 0: + res2.remove(r) + else: + #raise osv.except_osv('Error !','You have not permission to perform operation !!!') + res2.remove(r) + return res2 ir_values() diff --git a/bin/addons/base/ir/wizard/wizard_menu.py b/bin/addons/base/ir/wizard/wizard_menu.py index 966bb84430b..32d25e3c849 100644 --- a/bin/addons/base/ir/wizard/wizard_menu.py +++ b/bin/addons/base/ir/wizard/wizard_menu.py @@ -30,57 +30,57 @@ from osv import fields,osv class wizard_model_menu(osv.osv_memory): - _name = 'wizard.ir.model.menu.create' - _columns = { - 'model_id': fields.many2one('ir.model','Model', required=True), - 'menu_id': fields.many2one('ir.ui.menu', 'Parent Menu', required=True), - 'name': fields.char('Menu Name', size=64, required=True), - 'view_ids': fields.one2many('wizard.ir.model.menu.create.line', 'wizard_id', 'Views'), - } - _defaults = { - 'model_id': lambda self,cr,uid,ctx: ctx.get('model_id', False) - } - def menu_create(self, cr, uid, ids, context={}): - for menu in self.browse(cr, uid, ids, context): - view_mode = [] - views = [] - for view in menu.view_ids: - view_mode.append(view.view_type) - views.append( (0,0,{ - 'view_id': view.view_id and view.view_id.id or False, - 'view_mode': view.view_type, - 'sequence': view.sequence - })) - val = { - 'name': menu.name, - 'res_model': menu.model_id.model, - 'view_type': 'form', - 'view_mode': ','.join(view_mode) - } - if views: - val['view_ids'] = views - else: - val['view_mode'] = 'tree,form' - action_id = self.pool.get('ir.actions.act_window').create(cr, uid, val) - self.pool.get('ir.ui.menu').create(cr, uid, { - 'name': menu.name, - 'parent_id': menu.menu_id.id, - 'action': 'ir.actions.act_window,%d' % (action_id,), - 'icon': 'STOCK_INDENT' - }, context) - return {'type':'ir.actions.act_window_close'} + _name = 'wizard.ir.model.menu.create' + _columns = { + 'model_id': fields.many2one('ir.model','Model', required=True), + 'menu_id': fields.many2one('ir.ui.menu', 'Parent Menu', required=True), + 'name': fields.char('Menu Name', size=64, required=True), + 'view_ids': fields.one2many('wizard.ir.model.menu.create.line', 'wizard_id', 'Views'), + } + _defaults = { + 'model_id': lambda self,cr,uid,ctx: ctx.get('model_id', False) + } + def menu_create(self, cr, uid, ids, context={}): + for menu in self.browse(cr, uid, ids, context): + view_mode = [] + views = [] + for view in menu.view_ids: + view_mode.append(view.view_type) + views.append( (0,0,{ + 'view_id': view.view_id and view.view_id.id or False, + 'view_mode': view.view_type, + 'sequence': view.sequence + })) + val = { + 'name': menu.name, + 'res_model': menu.model_id.model, + 'view_type': 'form', + 'view_mode': ','.join(view_mode) + } + if views: + val['view_ids'] = views + else: + val['view_mode'] = 'tree,form' + action_id = self.pool.get('ir.actions.act_window').create(cr, uid, val) + self.pool.get('ir.ui.menu').create(cr, uid, { + 'name': menu.name, + 'parent_id': menu.menu_id.id, + 'action': 'ir.actions.act_window,%d' % (action_id,), + 'icon': 'STOCK_INDENT' + }, context) + return {'type':'ir.actions.act_window_close'} wizard_model_menu() class wizard_model_menu_line(osv.osv_memory): - _name = 'wizard.ir.model.menu.create.line' - _columns = { - 'wizard_id': fields.many2one('wizard.ir.model.menu.create','Wizard'), - 'sequence': fields.integer('Sequence'), - 'view_type': fields.selection([('tree','Tree'),('form','Form'),('graph','Graph'),('calendar','Calendar')],'View Type',required=True), - 'view_id': fields.many2one('ir.ui.view', 'View'), - } - _defaults = { - 'view_type': lambda self,cr,uid,ctx: 'tree' - } + _name = 'wizard.ir.model.menu.create.line' + _columns = { + 'wizard_id': fields.many2one('wizard.ir.model.menu.create','Wizard'), + 'sequence': fields.integer('Sequence'), + 'view_type': fields.selection([('tree','Tree'),('form','Form'),('graph','Graph'),('calendar','Calendar')],'View Type',required=True), + 'view_id': fields.many2one('ir.ui.view', 'View'), + } + _defaults = { + 'view_type': lambda self,cr,uid,ctx: 'tree' + } wizard_model_menu_line() diff --git a/bin/addons/base/ir/workflow/print_instance.py b/bin/addons/base/ir/workflow/print_instance.py index 74f2bd31733..428df57ac0e 100644 --- a/bin/addons/base/ir/workflow/print_instance.py +++ b/bin/addons/base/ir/workflow/print_instance.py @@ -34,175 +34,175 @@ import report,pooler,tools def graph_get(cr, graph, wkf_id, nested=False, workitem={}): - import pydot - cr.execute('select * from wkf_activity where wkf_id=%d', (wkf_id,)) - nodes = cr.dictfetchall() - activities = {} - actfrom = {} - actto = {} - for n in nodes: - activities[n['id']] = n - if n['subflow_id'] and nested: - cr.execute('select * from wkf where id=%d', (n['subflow_id'],)) - wkfinfo = cr.dictfetchone() - graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize=12, label = "Subflow: "+n['name']+'\\nOSV: '+wkfinfo['osv']) - (s1,s2) = graph_get(cr, graph2, n['subflow_id'], nested,workitem) - graph.add_subgraph(graph2) - actfrom[n['id']] = s2 - actto[n['id']] = s1 - else: - args = {} - if n['flow_start'] or n['flow_stop']: - args['style']='filled' - args['color']='lightgrey' - args['label']=n['name'] - if n['subflow_id']: - args['shape'] = 'box' - if n['id'] in workitem: - args['label']+='\\nx '+str(workitem[n['id']]) - args['color'] = "red" - graph.add_node(pydot.Node(n['id'], **args)) - actfrom[n['id']] = (n['id'],{}) - actto[n['id']] = (n['id'],{}) - cr.execute('select * from wkf_transition where act_from in ('+','.join(map(lambda x: str(x['id']),nodes))+')') - transitions = cr.dictfetchall() - for t in transitions: - args = {} - args['label'] = str(t['condition']).replace(' or ', '\\nor ').replace(' and ', '\\nand ') - if t['signal']: - args['label'] += '\\n'+str(t['signal']) - args['style'] = 'bold' - - if activities[t['act_from']]['split_mode']=='AND': - args['arrowtail']='box' - elif str(activities[t['act_from']]['split_mode'])=='OR ': - args['arrowtail']='inv' - - if activities[t['act_to']]['join_mode']=='AND': - args['arrowhead']='crow' - - activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0]) - activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0]) - graph.add_edge(pydot.Edge( activity_from ,activity_to, fontsize=10, **args)) - nodes = cr.dictfetchall() - cr.execute('select id from wkf_activity where flow_start=True and wkf_id=%d limit 1', (wkf_id,)) - start = cr.fetchone()[0] - cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id=%d", (wkf_id,)) - stop = cr.fetchall() - stop = (stop[0][1], dict(stop)) - return ((start,{}),stop) + import pydot + cr.execute('select * from wkf_activity where wkf_id=%d', (wkf_id,)) + nodes = cr.dictfetchall() + activities = {} + actfrom = {} + actto = {} + for n in nodes: + activities[n['id']] = n + if n['subflow_id'] and nested: + cr.execute('select * from wkf where id=%d', (n['subflow_id'],)) + wkfinfo = cr.dictfetchone() + graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize=12, label = "Subflow: "+n['name']+'\\nOSV: '+wkfinfo['osv']) + (s1,s2) = graph_get(cr, graph2, n['subflow_id'], nested,workitem) + graph.add_subgraph(graph2) + actfrom[n['id']] = s2 + actto[n['id']] = s1 + else: + args = {} + if n['flow_start'] or n['flow_stop']: + args['style']='filled' + args['color']='lightgrey' + args['label']=n['name'] + if n['subflow_id']: + args['shape'] = 'box' + if n['id'] in workitem: + args['label']+='\\nx '+str(workitem[n['id']]) + args['color'] = "red" + graph.add_node(pydot.Node(n['id'], **args)) + actfrom[n['id']] = (n['id'],{}) + actto[n['id']] = (n['id'],{}) + cr.execute('select * from wkf_transition where act_from in ('+','.join(map(lambda x: str(x['id']),nodes))+')') + transitions = cr.dictfetchall() + for t in transitions: + args = {} + args['label'] = str(t['condition']).replace(' or ', '\\nor ').replace(' and ', '\\nand ') + if t['signal']: + args['label'] += '\\n'+str(t['signal']) + args['style'] = 'bold' + + if activities[t['act_from']]['split_mode']=='AND': + args['arrowtail']='box' + elif str(activities[t['act_from']]['split_mode'])=='OR ': + args['arrowtail']='inv' + + if activities[t['act_to']]['join_mode']=='AND': + args['arrowhead']='crow' + + activity_from = actfrom[t['act_from']][1].get(t['signal'], actfrom[t['act_from']][0]) + activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0]) + graph.add_edge(pydot.Edge( activity_from ,activity_to, fontsize=10, **args)) + nodes = cr.dictfetchall() + cr.execute('select id from wkf_activity where flow_start=True and wkf_id=%d limit 1', (wkf_id,)) + start = cr.fetchone()[0] + cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id=%d", (wkf_id,)) + stop = cr.fetchall() + stop = (stop[0][1], dict(stop)) + return ((start,{}),stop) def graph_instance_get(cr, graph, inst_id, nested=False): - workitems = {} - cr.execute('select * from wkf_instance where id=%d', (inst_id,)) - inst = cr.dictfetchone() + workitems = {} + cr.execute('select * from wkf_instance where id=%d', (inst_id,)) + inst = cr.dictfetchone() - def workitem_get(instance): - cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%d group by act_id', (instance,)) - workitems = dict(cr.fetchall()) + def workitem_get(instance): + cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%d group by act_id', (instance,)) + workitems = dict(cr.fetchall()) - cr.execute('select subflow_id from wkf_workitem where inst_id=%d', (instance,)) - for (subflow_id,) in cr.fetchall(): - workitems.update(workitem_get(subflow_id)) - return workitems - graph_get(cr, graph, inst['wkf_id'], nested, workitem_get(inst_id)) + cr.execute('select subflow_id from wkf_workitem where inst_id=%d', (instance,)) + for (subflow_id,) in cr.fetchall(): + workitems.update(workitem_get(subflow_id)) + return workitems + graph_get(cr, graph, inst['wkf_id'], nested, workitem_get(inst_id)) # # TODO: pas clean: concurrent !!! # class report_graph_instance(object): - def __init__(self, cr, uid, ids, data): - logger = netsvc.Logger() - try: - import pydot - except Exception,e: - logger.notifyChannel('workflow', netsvc.LOG_WARNING, - 'Import Error for pydot, you will not be able to render workflows\n' - 'Consider Installing PyDot or dependencies: http://dkbza.org/pydot.html') - raise e - self.done = False - - try: - cr.execute('select * from wkf where osv=%s limit 1', - (data['model'],)) - wkfinfo = cr.dictfetchone() - if not wkfinfo: - ps_string = '''%PS-Adobe-3.0 + def __init__(self, cr, uid, ids, data): + logger = netsvc.Logger() + try: + import pydot + except Exception,e: + logger.notifyChannel('workflow', netsvc.LOG_WARNING, + 'Import Error for pydot, you will not be able to render workflows\n' + 'Consider Installing PyDot or dependencies: http://dkbza.org/pydot.html') + raise e + self.done = False + + try: + cr.execute('select * from wkf where osv=%s limit 1', + (data['model'],)) + wkfinfo = cr.dictfetchone() + if not wkfinfo: + ps_string = '''%PS-Adobe-3.0 /inch {72 mul} def /Times-Roman findfont 50 scalefont setfont 1.5 inch 15 inch moveto (No workflow defined) show showpage''' - else: - cr.execute('SELECT id FROM wkf_instance \ - WHERE res_id=%d AND wkf_id=%d \ - ORDER BY state LIMIT 1', - (data['id'], wkfinfo['id'])) - inst_id = cr.fetchone() - if not inst_id: - ps_string = '''%PS-Adobe-3.0 + else: + cr.execute('SELECT id FROM wkf_instance \ + WHERE res_id=%d AND wkf_id=%d \ + ORDER BY state LIMIT 1', + (data['id'], wkfinfo['id'])) + inst_id = cr.fetchone() + if not inst_id: + ps_string = '''%PS-Adobe-3.0 /inch {72 mul} def /Times-Roman findfont 50 scalefont setfont 1.5 inch 15 inch moveto (No workflow instance defined) show showpage''' - else: - inst_id = inst_id[0] - graph = pydot.Dot(fontsize=16, label="\\n\\nWorkflow: %s\\n OSV: %s" % (wkfinfo['name'],wkfinfo['osv'])) - graph.set('size', '10.7,7.3') - graph.set('center', '1') - graph.set('ratio', 'auto') - graph.set('rotate', '90') - graph.set('rankdir', 'LR') - graph_instance_get(cr, graph, inst_id, data.get('nested', False)) - ps_string = graph.create_ps(prog='dot') - except Exception, e: - import traceback, sys - tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) - logger.notifyChannel('workflow', netsvc.LOG_ERROR, 'Exception in call: ' + tb_s) - # string is in PS, like the success message would have been - ps_string = '''%PS-Adobe-3.0 + else: + inst_id = inst_id[0] + graph = pydot.Dot(fontsize=16, label="\\n\\nWorkflow: %s\\n OSV: %s" % (wkfinfo['name'],wkfinfo['osv'])) + graph.set('size', '10.7,7.3') + graph.set('center', '1') + graph.set('ratio', 'auto') + graph.set('rotate', '90') + graph.set('rankdir', 'LR') + graph_instance_get(cr, graph, inst_id, data.get('nested', False)) + ps_string = graph.create_ps(prog='dot') + except Exception, e: + import traceback, sys + tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + logger.notifyChannel('workflow', netsvc.LOG_ERROR, 'Exception in call: ' + tb_s) + # string is in PS, like the success message would have been + ps_string = '''%PS-Adobe-3.0 /inch {72 mul} def /Times-Roman findfont 50 scalefont setfont 1.5 inch 15 inch moveto (No workflow available) show showpage''' - if os.name == "nt": - prog = 'ps2pdf.bat' - else: - prog = 'ps2pdf' - args = (prog, '-', '-') - input, output = tools.exec_command_pipe(*args) - input.write(ps_string) - input.close() - self.result = output.read() - output.close() - self.done = True - - def is_done(self): - return self.done - - def get(self): - if self.done: - return self.result - else: - return None + if os.name == "nt": + prog = 'ps2pdf.bat' + else: + prog = 'ps2pdf' + args = (prog, '-', '-') + input, output = tools.exec_command_pipe(*args) + input.write(ps_string) + input.close() + self.result = output.read() + output.close() + self.done = True + + def is_done(self): + return self.done + + def get(self): + if self.done: + return self.result + else: + return None class report_graph(report.interface.report_int): - def __init__(self, name, table): - report.interface.report_int.__init__(self, name) - self.table = table - - def result(self): - if self.obj.is_done(): - return (True, self.obj.get(), 'pdf') - else: - return (False, False, False) - - def create(self, cr, uid, ids, data, context={}): - self.obj = report_graph_instance(cr, uid, ids, data) - return (self.obj.get(), 'pdf') + def __init__(self, name, table): + report.interface.report_int.__init__(self, name) + self.table = table + + def result(self): + if self.obj.is_done(): + return (True, self.obj.get(), 'pdf') + else: + return (False, False, False) + + def create(self, cr, uid, ids, data, context={}): + self.obj = report_graph_instance(cr, uid, ids, data) + return (self.obj.get(), 'pdf') report_graph('report.workflow.instance.graph', 'ir.workflow') diff --git a/bin/addons/base/ir/workflow/pydot/__init__.py b/bin/addons/base/ir/workflow/pydot/__init__.py index 6232f794cdd..6232f794cdd 100755..100644 --- a/bin/addons/base/ir/workflow/pydot/__init__.py +++ b/bin/addons/base/ir/workflow/pydot/__init__.py diff --git a/bin/addons/base/ir/workflow/pydot/dot_parser.py b/bin/addons/base/ir/workflow/pydot/dot_parser.py index 30007ed47de..67a763144c7 100755..100644 --- a/bin/addons/base/ir/workflow/pydot/dot_parser.py +++ b/bin/addons/base/ir/workflow/pydot/dot_parser.py @@ -14,339 +14,339 @@ import glob import pydot from pyparsing import __version__ as pyparsing_version -from pyparsing import Literal, CaselessLiteral, Word, \ - Upcase, OneOrMore, ZeroOrMore, Forward, NotAny, \ - delimitedList, oneOf, Group, Optional, Combine, \ - alphas, nums, restOfLine, cStyleComment, nums, \ - alphanums, printables, empty, quotedString, \ - ParseException, ParseResults, CharsNotIn, _noncomma +from pyparsing import Literal, CaselessLiteral, Word, \ + Upcase, OneOrMore, ZeroOrMore, Forward, NotAny, \ + delimitedList, oneOf, Group, Optional, Combine, \ + alphas, nums, restOfLine, cStyleComment, nums, \ + alphanums, printables, empty, quotedString, \ + ParseException, ParseResults, CharsNotIn, _noncomma class P_AttrList: - def __init__(self, toks): - self.attrs = {} - i = 0 - while i < len(toks): - attrname = toks[i] - attrvalue = toks[i+1] - self.attrs[attrname] = attrvalue - i += 2 + def __init__(self, toks): + self.attrs = {} + i = 0 + while i < len(toks): + attrname = toks[i] + attrvalue = toks[i+1] + self.attrs[attrname] = attrvalue + i += 2 - def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self.attrs) + def __repr__(self): + return "%s(%r)" % (self.__class__.__name__, self.attrs) class DefaultStatement(P_AttrList): - def __init__(self, default_type, attrs): - self.default_type = default_type - self.attrs = attrs + def __init__(self, default_type, attrs): + self.default_type = default_type + self.attrs = attrs - def __repr__(self): - return "%s(%s, %r)" % \ - (self.__class__.__name__, self.default_type, self.attrs) + def __repr__(self): + return "%s(%s, %r)" % \ + (self.__class__.__name__, self.default_type, self.attrs) def push_top_graph_stmt(str, loc, toks): - attrs = {} - g = None - - for element in toks: - if isinstance(element, ParseResults) or \ - isinstance(element, tuple) or \ - isinstance(element, list): - - element = element[0] - - if element == 'strict': - attrs['strict'] = True - elif element in ['graph', 'digraph']: - attrs['graph_type'] = element - elif type(element) == type(''): - attrs['graph_name'] = element - elif isinstance(element, pydot.Graph): - g = pydot.Graph(**attrs) - g.__dict__.update(element.__dict__) - for e in g.get_edge_list(): - e.parent_graph = g - for e in g.get_node_list(): - e.parent_graph = g - for e in g.get_subgraph_list(): - e.set_graph_parent(g) - - elif isinstance(element, P_AttrList): - attrs.update(element.attrs) - else: - raise ValueError, "Unknown element statement: %r " % element - - if g is not None: - g.__dict__.update(attrs) - return g + attrs = {} + g = None + + for element in toks: + if isinstance(element, ParseResults) or \ + isinstance(element, tuple) or \ + isinstance(element, list): + + element = element[0] + + if element == 'strict': + attrs['strict'] = True + elif element in ['graph', 'digraph']: + attrs['graph_type'] = element + elif type(element) == type(''): + attrs['graph_name'] = element + elif isinstance(element, pydot.Graph): + g = pydot.Graph(**attrs) + g.__dict__.update(element.__dict__) + for e in g.get_edge_list(): + e.parent_graph = g + for e in g.get_node_list(): + e.parent_graph = g + for e in g.get_subgraph_list(): + e.set_graph_parent(g) + + elif isinstance(element, P_AttrList): + attrs.update(element.attrs) + else: + raise ValueError, "Unknown element statement: %r " % element + + if g is not None: + g.__dict__.update(attrs) + return g def add_defaults(element, defaults): - d = element.__dict__ - for key, value in defaults.items(): - if not d.get(key): - d[key] = value + d = element.__dict__ + for key, value in defaults.items(): + if not d.get(key): + d[key] = value def add_elements(g, toks, defaults_graph=None, defaults_node=None, defaults_edge=None): - - if defaults_graph is None: - defaults_graph = {} - if defaults_node is None: - defaults_node = {} - if defaults_edge is None: - defaults_edge = {} - - for element in toks: - if isinstance(element, pydot.Graph): - add_defaults(element, defaults_graph) - g.add_subgraph(element) - elif isinstance(element, pydot.Node): - add_defaults(element, defaults_node) - g.add_node(element) - elif isinstance(element, pydot.Edge): - add_defaults(element, defaults_edge) - g.add_edge(element) - elif isinstance(element, ParseResults): - for e in element: - add_elements(g, [e], defaults_graph, defaults_node, defaults_edge) - elif isinstance(element, DefaultStatement): - if element.default_type == 'graph': - default_graph_attrs = pydot.Node('graph') - default_graph_attrs.__dict__.update(element.attrs) - g.add_node(default_graph_attrs) -# defaults_graph.update(element.attrs) -# g.__dict__.update(element.attrs) - elif element.default_type == 'node': - default_node_attrs = pydot.Node('node') - default_node_attrs.__dict__.update(element.attrs) - g.add_node(default_node_attrs) - #defaults_node.update(element.attrs) - elif element.default_type == 'edge': - default_edge_attrs = pydot.Node('edge') - default_edge_attrs.__dict__.update(element.attrs) - g.add_node(default_edge_attrs) - #defaults_edge.update(element.attrs) - else: - raise ValueError, "Unknown DefaultStatement: %s " % element.default_type - elif isinstance(element, P_AttrList): - g.__dict__.update(element.attrs) - else: - raise ValueError, "Unknown element statement: %r " % element + + if defaults_graph is None: + defaults_graph = {} + if defaults_node is None: + defaults_node = {} + if defaults_edge is None: + defaults_edge = {} + + for element in toks: + if isinstance(element, pydot.Graph): + add_defaults(element, defaults_graph) + g.add_subgraph(element) + elif isinstance(element, pydot.Node): + add_defaults(element, defaults_node) + g.add_node(element) + elif isinstance(element, pydot.Edge): + add_defaults(element, defaults_edge) + g.add_edge(element) + elif isinstance(element, ParseResults): + for e in element: + add_elements(g, [e], defaults_graph, defaults_node, defaults_edge) + elif isinstance(element, DefaultStatement): + if element.default_type == 'graph': + default_graph_attrs = pydot.Node('graph') + default_graph_attrs.__dict__.update(element.attrs) + g.add_node(default_graph_attrs) +# defaults_graph.update(element.attrs) +# g.__dict__.update(element.attrs) + elif element.default_type == 'node': + default_node_attrs = pydot.Node('node') + default_node_attrs.__dict__.update(element.attrs) + g.add_node(default_node_attrs) + #defaults_node.update(element.attrs) + elif element.default_type == 'edge': + default_edge_attrs = pydot.Node('edge') + default_edge_attrs.__dict__.update(element.attrs) + g.add_node(default_edge_attrs) + #defaults_edge.update(element.attrs) + else: + raise ValueError, "Unknown DefaultStatement: %s " % element.default_type + elif isinstance(element, P_AttrList): + g.__dict__.update(element.attrs) + else: + raise ValueError, "Unknown element statement: %r " % element def push_graph_stmt(str, loc, toks): - g = pydot.Subgraph() - add_elements(g, toks) - return g + g = pydot.Subgraph() + add_elements(g, toks) + return g -def push_subgraph_stmt(str, loc, toks): - for e in toks: - if len(e)==3: - g = e[2] - g.set_name(e[1]) +def push_subgraph_stmt(str, loc, toks): + for e in toks: + if len(e)==3: + g = e[2] + g.set_name(e[1]) - return g + return g def push_default_stmt(str, loc, toks): - # The pydot class instances should be marked as - # default statements to be inherited by actual - # graphs, nodes and edges. - # print "push_default_stmt", toks - default_type = toks[0][0] - if len(toks) > 1: - attrs = toks[1].attrs - else: - attrs = {} - - if default_type in ['graph', 'node', 'edge']: - return DefaultStatement(default_type, attrs) - else: - raise ValueError, "Unknown default statement: %r " % toks + # The pydot class instances should be marked as + # default statements to be inherited by actual + # graphs, nodes and edges. + # print "push_default_stmt", toks + default_type = toks[0][0] + if len(toks) > 1: + attrs = toks[1].attrs + else: + attrs = {} + + if default_type in ['graph', 'node', 'edge']: + return DefaultStatement(default_type, attrs) + else: + raise ValueError, "Unknown default statement: %r " % toks def push_attr_list(str, loc, toks): - p = P_AttrList(toks) - return p + p = P_AttrList(toks) + return p def get_port(node): - if len(node)>1: - if isinstance(node[1], ParseResults): - if len(node[1][0])==2: - if node[1][0][0]==':': - return node[1][0][1] - - return None + if len(node)>1: + if isinstance(node[1], ParseResults): + if len(node[1][0])==2: + if node[1][0][0]==':': + return node[1][0][1] + + return None def push_edge_stmt(str, loc, toks): - - tok_attrs = [a for a in toks if isinstance(a, P_AttrList)] - attrs = {} - for a in tok_attrs: - attrs.update(a.attrs) - - n_prev = toks[0] - e = [] - for n_next in tuple(toks)[2::2]: - port = get_port(n_prev) - if port is not None: - n_prev_port = ':'+port - else: - n_prev_port = '' - - port = get_port(n_next) - if port is not None: - n_next_port = ':'+port - else: - n_next_port = '' - - e.append(pydot.Edge(n_prev[0]+n_prev_port, n_next[0]+n_next_port, **attrs)) - n_prev = n_next - return e + + tok_attrs = [a for a in toks if isinstance(a, P_AttrList)] + attrs = {} + for a in tok_attrs: + attrs.update(a.attrs) + + n_prev = toks[0] + e = [] + for n_next in tuple(toks)[2::2]: + port = get_port(n_prev) + if port is not None: + n_prev_port = ':'+port + else: + n_prev_port = '' + + port = get_port(n_next) + if port is not None: + n_next_port = ':'+port + else: + n_next_port = '' + + e.append(pydot.Edge(n_prev[0]+n_prev_port, n_next[0]+n_next_port, **attrs)) + n_prev = n_next + return e def push_node_stmt(str, loc, toks): - if len(toks) == 2: - attrs = toks[1].attrs - else: - attrs = {} - - node_name = toks[0] - if isinstance(node_name, list) or isinstance(node_name, tuple): - if len(node_name)>0: - node_name = node_name[0] - - n = pydot.Node(node_name, **attrs) - return n + if len(toks) == 2: + attrs = toks[1].attrs + else: + attrs = {} + + node_name = toks[0] + if isinstance(node_name, list) or isinstance(node_name, tuple): + if len(node_name)>0: + node_name = node_name[0] + + n = pydot.Node(node_name, **attrs) + return n def strip_quotes( s, l, t ): - return [ t[0].strip('"') ] + return [ t[0].strip('"') ] graphparser = None def GRAPH_DEF(): - global graphparser - - if not graphparser: - # punctuation - colon = Literal(":") - lbrace = Literal("{") - rbrace = Literal("}") - lbrack = Literal("[") - rbrack = Literal("]") - lparen = Literal("(") - rparen = Literal(")") - equals = Literal("=") - comma = Literal(",") - dot = Literal(".") - slash = Literal("/") - bslash = Literal("\\") - star = Literal("*") - semi = Literal(";") - at = Literal("@") - minus = Literal("-") - - # keywords - strict_ = Literal("strict") - graph_ = Literal("graph") - digraph_ = Literal("digraph") - subgraph_ = Literal("subgraph") - node_ = Literal("node") - edge_ = Literal("edge") - - identifier = Word(alphanums + "_" ).setName("identifier") - - double_quote = Literal('"') - double_quoted_string = \ - Combine( double_quote + ZeroOrMore(CharsNotIn('"')) + double_quote ) - - alphastring_ = OneOrMore(CharsNotIn(_noncomma)) - - ID = (identifier | double_quoted_string.setParseAction(strip_quotes) |\ - alphastring_).setName("ID") - - html_text = Combine(Literal("<<") + OneOrMore(CharsNotIn(",]"))) - - float_number = Combine(Optional(minus) + \ - OneOrMore(Word(nums + "."))).setName("float_number") - - righthand_id = (float_number | ID | html_text).setName("righthand_id") - - port_angle = (at + ID).setName("port_angle") - - port_location = (Group(colon + ID) | \ - Group(colon + lparen + ID + comma + ID + rparen)).setName("port_location") - - port = (Group(port_location + Optional(port_angle)) | \ - Group(port_angle + Optional(port_location))).setName("port") - - node_id = (ID + Optional(port)) - a_list = OneOrMore(ID + Optional(equals.suppress() + righthand_id) + \ - Optional(comma.suppress())).setName("a_list") - - attr_list = OneOrMore(lbrack.suppress() + Optional(a_list) + \ - rbrack.suppress()).setName("attr_list") - - attr_stmt = (Group(graph_ | node_ | edge_) + attr_list).setName("attr_stmt") - - edgeop = (Literal("--") | Literal("->")).setName("edgeop") - - stmt_list = Forward() - graph_stmt = Group(lbrace.suppress() + Optional(stmt_list) + \ - rbrace.suppress()).setName("graph_stmt") - - subgraph = (Group(Optional(subgraph_ + Optional(ID)) + graph_stmt) | \ - Group(subgraph_ + ID)).setName("subgraph") - - edgeRHS = OneOrMore(edgeop + Group(node_id | subgraph)) - - edge_stmt = Group(node_id | subgraph) + edgeRHS + Optional(attr_list) - - node_stmt = (node_id + Optional(attr_list) + semi.suppress()).setName("node_stmt") - - assignment = (ID + equals.suppress() + righthand_id).setName("assignment") - stmt = (assignment | edge_stmt | attr_stmt | node_stmt | subgraph).setName("stmt") - stmt_list << OneOrMore(stmt + Optional(semi.suppress())) - - graphparser = (Optional(strict_) + Group((graph_ | digraph_)) + \ - Optional(ID) + graph_stmt).setResultsName("graph") - - singleLineComment = "//" + restOfLine - graphparser.ignore(singleLineComment) - graphparser.ignore(cStyleComment) - - assignment.setParseAction(push_attr_list) - a_list.setParseAction(push_attr_list) - edge_stmt.setParseAction(push_edge_stmt) - node_stmt.setParseAction(push_node_stmt) - attr_stmt.setParseAction(push_default_stmt) - - subgraph.setParseAction(push_subgraph_stmt) - graph_stmt.setParseAction(push_graph_stmt) - graphparser.setParseAction(push_top_graph_stmt) - - return graphparser + global graphparser + + if not graphparser: + # punctuation + colon = Literal(":") + lbrace = Literal("{") + rbrace = Literal("}") + lbrack = Literal("[") + rbrack = Literal("]") + lparen = Literal("(") + rparen = Literal(")") + equals = Literal("=") + comma = Literal(",") + dot = Literal(".") + slash = Literal("/") + bslash = Literal("\\") + star = Literal("*") + semi = Literal(";") + at = Literal("@") + minus = Literal("-") + + # keywords + strict_ = Literal("strict") + graph_ = Literal("graph") + digraph_ = Literal("digraph") + subgraph_ = Literal("subgraph") + node_ = Literal("node") + edge_ = Literal("edge") + + identifier = Word(alphanums + "_" ).setName("identifier") + + double_quote = Literal('"') + double_quoted_string = \ + Combine( double_quote + ZeroOrMore(CharsNotIn('"')) + double_quote ) + + alphastring_ = OneOrMore(CharsNotIn(_noncomma)) + + ID = (identifier | double_quoted_string.setParseAction(strip_quotes) |\ + alphastring_).setName("ID") + + html_text = Combine(Literal("<<") + OneOrMore(CharsNotIn(",]"))) + + float_number = Combine(Optional(minus) + \ + OneOrMore(Word(nums + "."))).setName("float_number") + + righthand_id = (float_number | ID | html_text).setName("righthand_id") + + port_angle = (at + ID).setName("port_angle") + + port_location = (Group(colon + ID) | \ + Group(colon + lparen + ID + comma + ID + rparen)).setName("port_location") + + port = (Group(port_location + Optional(port_angle)) | \ + Group(port_angle + Optional(port_location))).setName("port") + + node_id = (ID + Optional(port)) + a_list = OneOrMore(ID + Optional(equals.suppress() + righthand_id) + \ + Optional(comma.suppress())).setName("a_list") + + attr_list = OneOrMore(lbrack.suppress() + Optional(a_list) + \ + rbrack.suppress()).setName("attr_list") + + attr_stmt = (Group(graph_ | node_ | edge_) + attr_list).setName("attr_stmt") + + edgeop = (Literal("--") | Literal("->")).setName("edgeop") + + stmt_list = Forward() + graph_stmt = Group(lbrace.suppress() + Optional(stmt_list) + \ + rbrace.suppress()).setName("graph_stmt") + + subgraph = (Group(Optional(subgraph_ + Optional(ID)) + graph_stmt) | \ + Group(subgraph_ + ID)).setName("subgraph") + + edgeRHS = OneOrMore(edgeop + Group(node_id | subgraph)) + + edge_stmt = Group(node_id | subgraph) + edgeRHS + Optional(attr_list) + + node_stmt = (node_id + Optional(attr_list) + semi.suppress()).setName("node_stmt") + + assignment = (ID + equals.suppress() + righthand_id).setName("assignment") + stmt = (assignment | edge_stmt | attr_stmt | node_stmt | subgraph).setName("stmt") + stmt_list << OneOrMore(stmt + Optional(semi.suppress())) + + graphparser = (Optional(strict_) + Group((graph_ | digraph_)) + \ + Optional(ID) + graph_stmt).setResultsName("graph") + + singleLineComment = "//" + restOfLine + graphparser.ignore(singleLineComment) + graphparser.ignore(cStyleComment) + + assignment.setParseAction(push_attr_list) + a_list.setParseAction(push_attr_list) + edge_stmt.setParseAction(push_edge_stmt) + node_stmt.setParseAction(push_node_stmt) + attr_stmt.setParseAction(push_default_stmt) + + subgraph.setParseAction(push_subgraph_stmt) + graph_stmt.setParseAction(push_graph_stmt) + graphparser.setParseAction(push_top_graph_stmt) + + return graphparser def parse_dot_data(data): - try: - graphparser = GRAPH_DEF() - if pyparsing_version >= '1.2': - graphparser.parseWithTabs() - tokens = graphparser.parseString(data) - graph = tokens.graph - return graph - except ParseException, err: - print err.line - print " "*(err.column-1) + "^" - print err - return None + try: + graphparser = GRAPH_DEF() + if pyparsing_version >= '1.2': + graphparser.parseWithTabs() + tokens = graphparser.parseString(data) + graph = tokens.graph + return graph + except ParseException, err: + print err.line + print " "*(err.column-1) + "^" + print err + return None diff --git a/bin/addons/base/ir/workflow/pydot/pydot.py b/bin/addons/base/ir/workflow/pydot/pydot.py index 947099f3ca0..e793a0a593b 100755..100644 --- a/bin/addons/base/ir/workflow/pydot/pydot.py +++ b/bin/addons/base/ir/workflow/pydot/pydot.py @@ -6,12 +6,12 @@ and process graphs in Graphviz's dot language. References: -pydot Homepage: http://www.dkbza.org/pydot.html -Graphviz: http://www.research.att.com/sw/tools/graphviz/ -DOT Language: http://www.research.att.com/~erg/graphviz/info/lang.html +pydot Homepage: http://www.dkbza.org/pydot.html +Graphviz: http://www.research.att.com/sw/tools/graphviz/ +DOT Language: http://www.research.att.com/~erg/graphviz/info/lang.html Programmed and tested with Graphviz 1.12 and Python 2.3.3 on GNU/Linux -by Ero Carrera (c) 2004 [ero@dkbza.org] +by Ero Carrera (c) 2004 [ero@dkbza.org] Distributed under MIT license [http://opensource.org/licenses/mit-license.html]. """ @@ -27,1049 +27,1049 @@ import dot_parser def graph_from_dot_data(data): - """Load graph as defined by data in DOT format. - - The data is assumed to be in DOT format. It will - be parsed and a Dot class will be returned, - representing the graph. - """ - - graph = dot_parser.parse_dot_data(data) - if graph is not None: - dot = Dot() - dot.__dict__.update(graph.__dict__) - return dot - - return None + """Load graph as defined by data in DOT format. + + The data is assumed to be in DOT format. It will + be parsed and a Dot class will be returned, + representing the graph. + """ + + graph = dot_parser.parse_dot_data(data) + if graph is not None: + dot = Dot() + dot.__dict__.update(graph.__dict__) + return dot + + return None def graph_from_dot_file(path): - """Load graph as defined by a DOT file. - - The file is assumed to be in DOT format. It will - be loaded, parsed and a Dot class will be returned, - representing the graph. - """ - - fd = file(path, 'rb') - data = fd.read() - fd.close() - - return graph_from_dot_data(data) + """Load graph as defined by a DOT file. + + The file is assumed to be in DOT format. It will + be loaded, parsed and a Dot class will be returned, + representing the graph. + """ + + fd = file(path, 'rb') + data = fd.read() + fd.close() + + return graph_from_dot_data(data) def graph_from_edges(edge_list, node_prefix='', directed=False): - """Creates a basic graph out of an edge list. - - The edge list has to be a list of tuples representing - the nodes connected by the edge. - The values can be anything: bool, int, float, str. - - If the graph is undirected by default, it is only - calculated from one of the symmetric halves of the matrix. - """ - if directed: - graph = Dot(graph_type='digraph') - else: - graph = Dot(graph_type='graph') - for edge in edge_list: - e = Edge(node_prefix+str(edge[0]), node_prefix+str(edge[1])) - graph.add_edge(e) - return graph + """Creates a basic graph out of an edge list. + + The edge list has to be a list of tuples representing + the nodes connected by the edge. + The values can be anything: bool, int, float, str. + + If the graph is undirected by default, it is only + calculated from one of the symmetric halves of the matrix. + """ + if directed: + graph = Dot(graph_type='digraph') + else: + graph = Dot(graph_type='graph') + for edge in edge_list: + e = Edge(node_prefix+str(edge[0]), node_prefix+str(edge[1])) + graph.add_edge(e) + return graph def graph_from_adjacency_matrix(matrix, node_prefix='', directed=False): - """Creates a basic graph out of an adjacency matrix. - - The matrix has to be a list of rows of values - representing an adjacency matrix. - The values can be anything: bool, int, float, as long - as they can evaluate to True or False. - """ - node_orig = 1 - if directed: - graph = Dot(graph_type='digraph') - else: - graph = Dot(graph_type='graph') - for row in matrix: - if not directed: - skip = matrix.index(row) - r = row[skip:] - else: - skip = 0 - r = row - node_dest = skip+1 - for e in r: - if e: - graph.add_edge( \ - Edge( node_prefix+str(node_orig), \ - node_prefix+str(node_dest))) - node_dest += 1 - node_orig += 1 - return graph + """Creates a basic graph out of an adjacency matrix. + + The matrix has to be a list of rows of values + representing an adjacency matrix. + The values can be anything: bool, int, float, as long + as they can evaluate to True or False. + """ + node_orig = 1 + if directed: + graph = Dot(graph_type='digraph') + else: + graph = Dot(graph_type='graph') + for row in matrix: + if not directed: + skip = matrix.index(row) + r = row[skip:] + else: + skip = 0 + r = row + node_dest = skip+1 + for e in r: + if e: + graph.add_edge( \ + Edge( node_prefix+str(node_orig), \ + node_prefix+str(node_dest))) + node_dest += 1 + node_orig += 1 + return graph def graph_from_incidence_matrix(matrix, node_prefix='', directed=False): - """Creates a basic graph out of an incidence matrix. - - The matrix has to be a list of rows of values - representing an incidence matrix. - The values can be anything: bool, int, float, as long - as they can evaluate to True or False. - """ - node_orig = 1 - if directed: - graph = Dot(graph_type='digraph') - else: - graph = Dot(graph_type='graph') - for row in matrix: - nodes = [] - c = 1 - for node in row: - if node: - nodes.append(c*node) - c += 1 - nodes.sort() - if len(nodes) == 2: - graph.add_edge( \ - Edge(node_prefix+str(abs(nodes[0])), \ - node_prefix+str(nodes[1]) )) - if not directed: - graph.set_simplify(True) - return graph - - + """Creates a basic graph out of an incidence matrix. + + The matrix has to be a list of rows of values + representing an incidence matrix. + The values can be anything: bool, int, float, as long + as they can evaluate to True or False. + """ + node_orig = 1 + if directed: + graph = Dot(graph_type='digraph') + else: + graph = Dot(graph_type='graph') + for row in matrix: + nodes = [] + c = 1 + for node in row: + if node: + nodes.append(c*node) + c += 1 + nodes.sort() + if len(nodes) == 2: + graph.add_edge( \ + Edge(node_prefix+str(abs(nodes[0])), \ + node_prefix+str(nodes[1]) )) + if not directed: + graph.set_simplify(True) + return graph + + def find_graphviz(): - """Locate Graphviz's executables in the system. - - Attempts to locate graphviz's executables in a Unix system. - It will look for 'dot', 'twopi' and 'neato' in all the directories - specified in the PATH environment variable. - It will return a dictionary containing the program names as keys - and their paths as values. - """ - progs = {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': ''} - if not os.environ.has_key('PATH'): - return None - for path in os.environ['PATH'].split(os.pathsep): - for prg in progs.keys(): - if os.path.exists(path+os.path.sep+prg): - progs[prg] = path+os.path.sep+prg - elif os.path.exists(path+os.path.sep+prg + '.exe'): - progs[prg] = path+os.path.sep+prg + '.exe' - return progs + """Locate Graphviz's executables in the system. + + Attempts to locate graphviz's executables in a Unix system. + It will look for 'dot', 'twopi' and 'neato' in all the directories + specified in the PATH environment variable. + It will return a dictionary containing the program names as keys + and their paths as values. + """ + progs = {'dot': '', 'twopi': '', 'neato': '', 'circo': '', 'fdp': ''} + if not os.environ.has_key('PATH'): + return None + for path in os.environ['PATH'].split(os.pathsep): + for prg in progs.keys(): + if os.path.exists(path+os.path.sep+prg): + progs[prg] = path+os.path.sep+prg + elif os.path.exists(path+os.path.sep+prg + '.exe'): + progs[prg] = path+os.path.sep+prg + '.exe' + return progs class Common: - """Common information to several classes. - - Should not be directly used, several classes are derived from - this one. - """ - chars_ID = None - parent_graph = None - - def char_range(self, a,b): - """Generate a list containing a range of characters. - - Returns a list of characters starting from 'a' up to 'b' - both inclusive. - """ - return map(chr, range(ord(a), ord(b)+1)) - - def is_ID(self, s): - """Checks whether a string is an dot language ID. - - It will check whether the string is solely composed - by the characters allowed in an ID or not. - """ - if not self.chars_ID: - self.chars_ID = self.char_range('a','z')+self.char_range('A','Z')+ \ - self.char_range('0','9')+['_'] - for c in s: - if c not in self.chars_ID: - return False - return True + """Common information to several classes. + + Should not be directly used, several classes are derived from + this one. + """ + chars_ID = None + parent_graph = None + + def char_range(self, a,b): + """Generate a list containing a range of characters. + + Returns a list of characters starting from 'a' up to 'b' + both inclusive. + """ + return map(chr, range(ord(a), ord(b)+1)) + + def is_ID(self, s): + """Checks whether a string is an dot language ID. + + It will check whether the string is solely composed + by the characters allowed in an ID or not. + """ + if not self.chars_ID: + self.chars_ID = self.char_range('a','z')+self.char_range('A','Z')+ \ + self.char_range('0','9')+['_'] + for c in s: + if c not in self.chars_ID: + return False + return True class Error(Exception): - """General error handling class. - """ - def __init__(self, value): - self.value = value - def __str__(self): - return self.value + """General error handling class. + """ + def __init__(self, value): + self.value = value + def __str__(self): + return self.value class Node(object, Common): - """A graph node. - - This class represents a graph's node with all its attributes. - - node(name, attribute=value, ...) - - name: node's name - - All the attributes defined in the Graphviz dot language should - be supported. - """ - attributes = ['showboxes', 'URL', 'fontcolor', 'fontsize', 'label', 'fontname', \ - 'comment', 'root', 'toplabel', 'vertices', 'width', 'z', 'bottomlabel', \ - 'distortion', 'fixedsize', 'group', 'height', 'orientation', 'pin', \ - 'rects', 'regular', 'shape', 'shapefile', 'sides', 'skew', 'pos', \ - 'layer', 'tooltip', 'style', 'target', 'color', 'peripheries', - 'fillcolor'] - - def __init__(self, name, **attrs): - - if isinstance(name, str) and not name.startswith('"'): - idx = name.find(':') - if idx>0: - name = name[:idx] - - self.name = str(name) - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - - def set_name(self, node_name): - """Set the node's name.""" - - self.name = str(node_name) - - def get_name(self): - """Get the node's name.""" - - return self.name - - def set(self, name, value): - """Set an attribute value by name. - - Given an attribute 'name' it will set its value to 'value'. - There's always the possibility of using the methods: - set_'name'(value) - which are defined for all the existing attributes. - """ - if name in self.attributes: - self.__dict__[name] = value - return True - # Attribute is not known - return False - - def to_string(self): - """Returns a string representation of the node in dot language. - """ - - if not isinstance(self.name, str): - self.name = str(self.name) - - # RMF: special case defaults for node, edge and graph properties. - if self.name in ['node', 'edge', 'graph']: - node = self.name - else: - node = '"'+self.name+'"' - - node_attr = None - all_attrs = [a for a in self.attributes] - all_attrs += [a for a in Graph.attributes if a not in all_attrs] - all_attrs += [a for a in Edge.attributes if a not in all_attrs] - for attr in all_attrs: - if self.__dict__.has_key(attr) \ - and self.__getattribute__(attr) is not None: - if not node_attr: - node_attr = '' - else: - node_attr += ', ' - node_attr += attr+'=' - val = self.__dict__[attr] - - if val.startswith('<') and val.endswith('>'): - node_attr += val - elif (isinstance(val, str) or isinstance(val, unicode)) and not self.is_ID(val): - node_attr += '"'+val+'"' - else: - node_attr += str(val) - - if node_attr: - node += ' ['+node_attr+']' - node += ';' - - return node + """A graph node. + + This class represents a graph's node with all its attributes. + + node(name, attribute=value, ...) + + name: node's name + + All the attributes defined in the Graphviz dot language should + be supported. + """ + attributes = ['showboxes', 'URL', 'fontcolor', 'fontsize', 'label', 'fontname', \ + 'comment', 'root', 'toplabel', 'vertices', 'width', 'z', 'bottomlabel', \ + 'distortion', 'fixedsize', 'group', 'height', 'orientation', 'pin', \ + 'rects', 'regular', 'shape', 'shapefile', 'sides', 'skew', 'pos', \ + 'layer', 'tooltip', 'style', 'target', 'color', 'peripheries', + 'fillcolor'] + + def __init__(self, name, **attrs): + + if isinstance(name, str) and not name.startswith('"'): + idx = name.find(':') + if idx>0: + name = name[:idx] + + self.name = str(name) + for attr in self.attributes: + # Set all the attributes to None. + self.__setattr__(attr, None) + # Generate all the Setter methods. + self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) + # Generate all the Getter methods. + self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) + for attr, val in attrs.items(): + self.__setattr__(attr, val) + + def set_name(self, node_name): + """Set the node's name.""" + + self.name = str(node_name) + + def get_name(self): + """Get the node's name.""" + + return self.name + + def set(self, name, value): + """Set an attribute value by name. + + Given an attribute 'name' it will set its value to 'value'. + There's always the possibility of using the methods: + set_'name'(value) + which are defined for all the existing attributes. + """ + if name in self.attributes: + self.__dict__[name] = value + return True + # Attribute is not known + return False + + def to_string(self): + """Returns a string representation of the node in dot language. + """ + + if not isinstance(self.name, str): + self.name = str(self.name) + + # RMF: special case defaults for node, edge and graph properties. + if self.name in ['node', 'edge', 'graph']: + node = self.name + else: + node = '"'+self.name+'"' + + node_attr = None + all_attrs = [a for a in self.attributes] + all_attrs += [a for a in Graph.attributes if a not in all_attrs] + all_attrs += [a for a in Edge.attributes if a not in all_attrs] + for attr in all_attrs: + if self.__dict__.has_key(attr) \ + and self.__getattribute__(attr) is not None: + if not node_attr: + node_attr = '' + else: + node_attr += ', ' + node_attr += attr+'=' + val = self.__dict__[attr] + + if val.startswith('<') and val.endswith('>'): + node_attr += val + elif (isinstance(val, str) or isinstance(val, unicode)) and not self.is_ID(val): + node_attr += '"'+val+'"' + else: + node_attr += str(val) + + if node_attr: + node += ' ['+node_attr+']' + node += ';' + + return node class Edge(object, Common): - """A graph edge. - - This class represents a graph's edge with all its attributes. - - edge(src, dst, attribute=value, ...) - - src: source node's name - dst: destination node's name - - All the attributes defined in the Graphviz dot language should - be supported. - - Attributes can be set through the dynamically generated methods: - - set_[attribute name], i.e. set_label, set_fontname - - or using the instance's attributes: - - Edge.[attribute name], i.e. edge_instance.label, edge_instance.fontname - """ - attributes = ['style', 'target', 'pos', 'layer', 'tooltip', 'color', 'showboxes',\ - 'URL', 'fontcolor', 'fontsize', 'label', 'fontname', 'comment', 'lp', \ - 'arrowhead', 'arrowsize', 'arrowtail', 'constraint', 'decorate', 'dir', \ - 'headURL', 'headclip', 'headhref', 'headlabel', 'headport', \ - 'headtarget', 'headtooltip', 'href', 'labelangle', 'labeldistance', \ - 'labelfloat', 'labelfontcolor', 'labelfontname', 'labelfontsize', 'len',\ - 'lhead', 'ltail', 'minlen', 'samehead', 'sametail', 'weight', 'tailURL',\ - 'tailclip', 'tailhref', 'taillabel', 'tailport', 'tailtarget', \ - 'tailtooltip'] - - def __init__(self, src, dst, **attrs): - self.src = src - self.dst = dst - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - - def get_source(self): - """Get the edges source node name.""" - - return self.src - - def get_destination(self): - """Get the edge's destination node name.""" - - return self.dst - - def __eq__(self, edge): - """Compare two edges. - - If the parent graph is directed, arcs linking - node A to B are considered equal and A->B != B->A - - If the parent graph is undirected, any edge - connecting two nodes is equal to any other - edge connecting the same nodes, A->B == B->A - """ - - if not isinstance(edge, Edge): - raise Error, 'Can\'t compare and edge to a non-edge object.' - if self.parent_graph.graph_type=='graph': - # If the graph is undirected, the edge has neither - # source nor destination. - if (self.src==edge.src and self.dst==edge.dst) or \ - (self.src==edge.dst and self.dst==edge.src): - return True - else: - if self.src==edge.src and self.dst==edge.dst: - return True - return False - - - def set(self, name, value): - """Set an attribute value by name. - - Given an attribute 'name' it will set its value to 'value'. - There's always the possibility of using the methods: - set_'name'(value) - which are defined for all the existing attributes. - """ - if name in self.attributes: - self.__dict__[name] = value - return True - # Attribute is not known - return False - - - def parse_node_ref(self, node_str): - - if not isinstance(node_str, str): - node_str = str(node_str) - - if node_str[0]=='"' and node_str[-1]=='"' and node_str[0].count('"')%2!=0: - return node_str - - node_port_idx = node_str.rfind(':') - - if node_port_idx>0 and node_str[0]=='"' and node_str[node_port_idx-1]=='"': - return node_str - - node_str = node_str.replace('"', '') - - if node_port_idx>0: - a = node_str[:node_port_idx] - b = node_str[node_port_idx+1:] - if self.is_ID(a): - node = a - else: - node = '"'+a+'"' - if self.is_ID(b): - node += ':'+b - else: - node+=':"'+b+'"' - return node - - return '"'+node_str+'"' - - - def to_string(self): - """Returns a string representation of the edge in dot language. - """ - - src = self.parse_node_ref(self.src) - dst = self.parse_node_ref(self.dst) - - edge = src - if self.parent_graph and \ - self.parent_graph.graph_type and \ - self.parent_graph.graph_type=='digraph': - edge+=' -> ' - else: - edge+=' -- ' - edge+=dst - - edge_attr = None - for attr in self.attributes: - if self.__dict__.has_key(attr) \ - and self.__getattribute__(attr) is not None: - if not edge_attr: - edge_attr = '' - else: - edge_attr+=', ' - edge_attr+=attr+'=' - val = self.__dict__[attr] - if (isinstance(val, str) or isinstance(val, unicode)) and not self.is_ID(val): - edge_attr+='"'+val+'"' - else: - edge_attr+=str(val) - - if edge_attr: - edge+=' ['+edge_attr+']' - edge+=';' - - return edge - + """A graph edge. + + This class represents a graph's edge with all its attributes. + + edge(src, dst, attribute=value, ...) + + src: source node's name + dst: destination node's name + + All the attributes defined in the Graphviz dot language should + be supported. + + Attributes can be set through the dynamically generated methods: + + set_[attribute name], i.e. set_label, set_fontname + + or using the instance's attributes: + + Edge.[attribute name], i.e. edge_instance.label, edge_instance.fontname + """ + attributes = ['style', 'target', 'pos', 'layer', 'tooltip', 'color', 'showboxes',\ + 'URL', 'fontcolor', 'fontsize', 'label', 'fontname', 'comment', 'lp', \ + 'arrowhead', 'arrowsize', 'arrowtail', 'constraint', 'decorate', 'dir', \ + 'headURL', 'headclip', 'headhref', 'headlabel', 'headport', \ + 'headtarget', 'headtooltip', 'href', 'labelangle', 'labeldistance', \ + 'labelfloat', 'labelfontcolor', 'labelfontname', 'labelfontsize', 'len',\ + 'lhead', 'ltail', 'minlen', 'samehead', 'sametail', 'weight', 'tailURL',\ + 'tailclip', 'tailhref', 'taillabel', 'tailport', 'tailtarget', \ + 'tailtooltip'] + + def __init__(self, src, dst, **attrs): + self.src = src + self.dst = dst + for attr in self.attributes: + # Set all the attributes to None. + self.__setattr__(attr, None) + # Generate all the Setter methods. + self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) + # Generate all the Getter methods. + self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) + for attr, val in attrs.items(): + self.__setattr__(attr, val) + + def get_source(self): + """Get the edges source node name.""" + + return self.src + + def get_destination(self): + """Get the edge's destination node name.""" + + return self.dst + + def __eq__(self, edge): + """Compare two edges. + + If the parent graph is directed, arcs linking + node A to B are considered equal and A->B != B->A + + If the parent graph is undirected, any edge + connecting two nodes is equal to any other + edge connecting the same nodes, A->B == B->A + """ + + if not isinstance(edge, Edge): + raise Error, 'Can\'t compare and edge to a non-edge object.' + if self.parent_graph.graph_type=='graph': + # If the graph is undirected, the edge has neither + # source nor destination. + if (self.src==edge.src and self.dst==edge.dst) or \ + (self.src==edge.dst and self.dst==edge.src): + return True + else: + if self.src==edge.src and self.dst==edge.dst: + return True + return False + + + def set(self, name, value): + """Set an attribute value by name. + + Given an attribute 'name' it will set its value to 'value'. + There's always the possibility of using the methods: + set_'name'(value) + which are defined for all the existing attributes. + """ + if name in self.attributes: + self.__dict__[name] = value + return True + # Attribute is not known + return False + + + def parse_node_ref(self, node_str): + + if not isinstance(node_str, str): + node_str = str(node_str) + + if node_str[0]=='"' and node_str[-1]=='"' and node_str[0].count('"')%2!=0: + return node_str + + node_port_idx = node_str.rfind(':') + + if node_port_idx>0 and node_str[0]=='"' and node_str[node_port_idx-1]=='"': + return node_str + + node_str = node_str.replace('"', '') + + if node_port_idx>0: + a = node_str[:node_port_idx] + b = node_str[node_port_idx+1:] + if self.is_ID(a): + node = a + else: + node = '"'+a+'"' + if self.is_ID(b): + node += ':'+b + else: + node+=':"'+b+'"' + return node + + return '"'+node_str+'"' + + + def to_string(self): + """Returns a string representation of the edge in dot language. + """ + + src = self.parse_node_ref(self.src) + dst = self.parse_node_ref(self.dst) + + edge = src + if self.parent_graph and \ + self.parent_graph.graph_type and \ + self.parent_graph.graph_type=='digraph': + edge+=' -> ' + else: + edge+=' -- ' + edge+=dst + + edge_attr = None + for attr in self.attributes: + if self.__dict__.has_key(attr) \ + and self.__getattribute__(attr) is not None: + if not edge_attr: + edge_attr = '' + else: + edge_attr+=', ' + edge_attr+=attr+'=' + val = self.__dict__[attr] + if (isinstance(val, str) or isinstance(val, unicode)) and not self.is_ID(val): + edge_attr+='"'+val+'"' + else: + edge_attr+=str(val) + + if edge_attr: + edge+=' ['+edge_attr+']' + edge+=';' + + return edge + class Graph(object, Common): - """Class representing a graph in Graphviz's dot language. - - This class implements the methods to work on a representation - of a graph in Graphviz's dot language. - - graph(graph_name='G', type='digraph', strict=False, suppress_disconnected=False, attribute=value, ...) - - graph_name: - the graph's name - type: - can be 'graph' or 'digraph' - suppress_disconnected: - defaults to False, which will remove from the - graph any disconnected nodes. - simplify: - if True it will avoid displaying equal edges, i.e. - only one edge between two nodes. removing the - duplicated ones. - - All the attributes defined in the Graphviz dot language should - be supported. - - Attributes can be set through the dynamically generated methods: - - set_[attribute name], i.e. set_size, set_fontname - - or using the instance's attributes: - - Graph.[attribute name], i.e. graph_instance.label, graph_instance.fontname - """ - - attributes = ['Damping', 'bb', 'center', 'clusterrank', 'compound', 'concentrate',\ - 'defaultdist', 'dim', 'fontpath', 'epsilon', 'layers', 'layersep', \ - 'margin', 'maxiter', 'mclimit', 'mindist', 'pack', 'packmode', 'model', \ - 'page', 'pagedir', 'nodesep', 'normalize', 'nslimit1', 'ordering', \ - 'orientation', 'outputorder', 'overlap', 'remincross', 'resolution', \ - 'rankdir', 'ranksep', 'ratio', 'rotate', 'samplepoints', 'searchsize', \ - 'sep', 'size', 'splines', 'start', 'stylesheet', 'truecolor', \ - 'viewport', 'voro_margin', 'quantum', 'bgcolor', 'labeljust', \ - 'labelloc', 'root', 'showboxes', 'URL', 'fontcolor', 'fontsize', \ - 'label' ,'fontname', 'comment', 'lp', 'target', 'color', 'style', \ - 'concentrators'] - - def __init__(self, graph_name='G', graph_type='digraph', strict=False, \ - suppress_disconnected=False, simplify=False, **attrs): - - if graph_type not in ['graph', 'digraph']: - raise Error, 'Invalid type. Accepted graph types are: graph, digraph, subgraph' - self.graph_type = graph_type - self.graph_name = graph_name - self.strict = strict - self.suppress_disconnected = suppress_disconnected - self.simplify = simplify - self.node_list = [] - self.edge_list = [] - self.edge_src_list = [] - self.edge_dst_list = [] - self.subgraph_list = [] - self.parent_graph = self - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - - def set_simplify(self, simplify): - """Set whether to simplify or not. - - If True it will avoid displaying equal edges, i.e. - only one edge between two nodes. removing the - duplicated ones. - """ - - self.simplify = simplify - - def get_simplify(self): - """Get whether to simplify or not. - - Refer to set_simplify for more information. - """ - - return self.simplify - - - def set_type(self, graph_type): - """Set the graph's type, 'graph' or 'digraph'.""" - self.graph_type = graph_type - - def get_type(self): - """Get the graph's type, 'graph' or 'digraph'.""" - return self.graph_type - - def set_name(self, graph_name): - """Set the graph's name.""" - - self.graph_name = graph_name - - def get_name(self): - """Get the graph's name.""" - - return self.graph_name - - def set_strict(self, val): - """Set graph to 'strict' mode. - - This option is only valid for top level graphs. - """ - - self.strict = val - - def get_strict(self, val): - """Get graph's 'strict' mode (True, False). - - This option is only valid for top level graphs. - """ - - return self.strict - - def set_suppress_disconnected(self, val): - """Suppress disconnected nodes in the output graph. - - This option will skip nodes in the graph with no incoming or outgoing - edges. This option works also for subgraphs and has effect only in the - current graph/subgraph. - """ - - self.suppress_disconnected = val - - def get_suppress_disconnected(self, val): - """Get if suppress disconnected is set. - - Refer to set_suppress_disconnected for more information. - """ - - self.suppress_disconnected = val - - def set(self, name, value): - """Set an attribute value by name. - - Given an attribute 'name' it will set its value to 'value'. - There's always the possibility of using the methods: - - set_'name'(value) - - which are defined for all the existing attributes. - """ - if name in self.attributes: - self.__dict__[name] = value - return True - # Attribute is not known - return False - - def get(self, name): - """Get an attribute value by name. - - Given an attribute 'name' it will get its value. - There's always the possibility of using the methods: - - get_'name'() - - which are defined for all the existing attributes. - """ - return self.__dict__[name] - - def add_node(self, graph_node): - """Adds a node object to the graph. - - It takes a node object as its only argument and returns - None. - """ - - if not isinstance(graph_node, Node): - raise Error, 'add_node received a non node class object' - - if self.get_node(graph_node.get_name()) is None: - self.node_list.append(graph_node) - graph_node.parent_graph = self.parent_graph - - def get_node(self, name): - """Retrieved a node from the graph. - - Given a node's name the corresponding Node - instance will be returned. - - If multiple nodes exist with that name, a list of - Node instances is returned. - If only one node exists, the instance is returned. - None is returned otherwise. - """ - - match = [node for node in self.node_list if node.get_name() == str(name)] - - l = len(match) - if l==1: - return match[0] - elif l>1: - return match - else: - return None - - def get_node_list(self): - """Get the list of Node instances. - - This method returns the list of Node instances - composing the graph. - """ - - return self.node_list - - def add_edge(self, graph_edge): - """Adds an edge object to the graph. - - It takes a edge object as its only argument and returns - None. - """ - - if not isinstance(graph_edge, Edge): - raise Error, 'add_edge received a non edge class object' - - self.edge_list.append(graph_edge) - - src = self.get_node(graph_edge.get_source()) - if src is None: - self.add_node(Node(graph_edge.get_source())) - - dst = self.get_node(graph_edge.get_destination()) - if dst is None: - self.add_node(Node(graph_edge.get_destination())) - - graph_edge.parent_graph = self.parent_graph - - if graph_edge.src not in self.edge_src_list: - self.edge_src_list.append(graph_edge.src) - - if graph_edge.dst not in self.edge_dst_list: - self.edge_dst_list.append(graph_edge.dst) - - def get_edge(self, src, dst): - """Retrieved an edge from the graph. - - Given an edge's source and destination the corresponding - Edge instance will be returned. - - If multiple edges exist with that source and destination, - a list of Edge instances is returned. - If only one edge exists, the instance is returned. - None is returned otherwise. - """ - - match = [edge for edge in self.edge_list if edge.src == src and edge.dst == dst] - - l = len(match) - if l==1: - return match[0] - elif l>1: - return match - else: - return None - - def get_edge_list(self): - """Get the list of Edge instances. - - This method returns the list of Edge instances - composing the graph. - """ - - return self.edge_list - - def add_subgraph(self, sgraph): - """Adds an edge object to the graph. - - It takes a subgraph object as its only argument and returns - None. - """ - if not isinstance(sgraph, Subgraph) and not isinstance(sgraph, Cluster): - raise Error, 'add_subgraph received a non subgraph class object' - - self.subgraph_list.append(sgraph) - - sgraph.set_graph_parent(self.parent_graph) - return None - - def get_subgraph(self, name): - """Retrieved a subgraph from the graph. - - Given a subgraph's name the corresponding - Subgraph instance will be returned. - - If multiple subgraphs exist with the same name, a list of - Subgraph instances is returned. - If only one Subgraph exists, the instance is returned. - None is returned otherwise. - """ - - match = [sgraph for sgraph in self.subgraph_list if sgraph.graph_name == name] - - l = len(match) - if l==1: - return match[0] - elif l>1: - return match - else: - return None - - def get_subgraph_list(self): - """Get the list of Subgraph instances. - - This method returns the list of Subgraph instances - in the graph. - """ - - return self.subgraph_list - - def set_graph_parent(self, parent): - """Sets a graph and its elements to point the the parent. - - Any subgraph added to a parent graph receives a reference - to the parent to access some common data. - """ - self.parent_graph = parent - - for elm in self.edge_list: - elm.parent_graph = parent - - for elm in self.node_list: - elm.parent_graph = parent - - for elm in self.subgraph_list: - elm.parent_graph = parent - elm.set_graph_parent(parent) - - def to_string(self, indent=''): - """Returns a string representation of the graph in dot language. - - It will return the graph and all its subelements in string from. - """ - graph = indent+'' - if self.__dict__.has_key('strict'): - if self==self.parent_graph and self.strict: - graph+='strict ' - - graph+=self.graph_type+' '+self.graph_name+' {\n' - - for attr in self.attributes: - if self.__dict__.has_key(attr) \ - and self.__getattribute__(attr) is not None: - graph += indent+'\t'+attr+'=' - val = self.__dict__[attr] - if isinstance(val, str) and not self.is_ID(val): - graph += '"'+val+'"' - else: - graph += str(val) - graph+=';\n' - - # RMF: for graphviz to handle edge/node defaults correctly, - # it seems they must appear before the rest of the nodes/edges - # so if they exist, dump them out first. - for elm in self.node_list: - if elm.name in ['node', 'edge', 'graph']: - graph += indent+'\t'+elm.to_string()+'\n' - - for elm in self.subgraph_list: - graph += elm.to_string(indent+'\t')+'\n' - - for elm in self.node_list: - # RMF: don't repeat the node/edge defaults - if elm.name in ['node', 'edge', 'graph']: - continue - if self.suppress_disconnected: - if elm.name not in self.edge_src_list+self.edge_dst_list: - continue - graph+=indent+'\t'+elm.to_string()+'\n' - - edges_done = [] - for elm in self.edge_list: - if self.simplify and elm in edges_done: - continue - graph+=indent+'\t'+elm.to_string()+'\n' - edges_done.append(elm) - - graph+=indent+'}\n' - - return graph + """Class representing a graph in Graphviz's dot language. + + This class implements the methods to work on a representation + of a graph in Graphviz's dot language. + + graph(graph_name='G', type='digraph', strict=False, suppress_disconnected=False, attribute=value, ...) + + graph_name: + the graph's name + type: + can be 'graph' or 'digraph' + suppress_disconnected: + defaults to False, which will remove from the + graph any disconnected nodes. + simplify: + if True it will avoid displaying equal edges, i.e. + only one edge between two nodes. removing the + duplicated ones. + + All the attributes defined in the Graphviz dot language should + be supported. + + Attributes can be set through the dynamically generated methods: + + set_[attribute name], i.e. set_size, set_fontname + + or using the instance's attributes: + + Graph.[attribute name], i.e. graph_instance.label, graph_instance.fontname + """ + + attributes = ['Damping', 'bb', 'center', 'clusterrank', 'compound', 'concentrate',\ + 'defaultdist', 'dim', 'fontpath', 'epsilon', 'layers', 'layersep', \ + 'margin', 'maxiter', 'mclimit', 'mindist', 'pack', 'packmode', 'model', \ + 'page', 'pagedir', 'nodesep', 'normalize', 'nslimit1', 'ordering', \ + 'orientation', 'outputorder', 'overlap', 'remincross', 'resolution', \ + 'rankdir', 'ranksep', 'ratio', 'rotate', 'samplepoints', 'searchsize', \ + 'sep', 'size', 'splines', 'start', 'stylesheet', 'truecolor', \ + 'viewport', 'voro_margin', 'quantum', 'bgcolor', 'labeljust', \ + 'labelloc', 'root', 'showboxes', 'URL', 'fontcolor', 'fontsize', \ + 'label' ,'fontname', 'comment', 'lp', 'target', 'color', 'style', \ + 'concentrators'] + + def __init__(self, graph_name='G', graph_type='digraph', strict=False, \ + suppress_disconnected=False, simplify=False, **attrs): + + if graph_type not in ['graph', 'digraph']: + raise Error, 'Invalid type. Accepted graph types are: graph, digraph, subgraph' + self.graph_type = graph_type + self.graph_name = graph_name + self.strict = strict + self.suppress_disconnected = suppress_disconnected + self.simplify = simplify + self.node_list = [] + self.edge_list = [] + self.edge_src_list = [] + self.edge_dst_list = [] + self.subgraph_list = [] + self.parent_graph = self + for attr in self.attributes: + # Set all the attributes to None. + self.__setattr__(attr, None) + # Generate all the Setter methods. + self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) + # Generate all the Getter methods. + self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) + for attr, val in attrs.items(): + self.__setattr__(attr, val) + + def set_simplify(self, simplify): + """Set whether to simplify or not. + + If True it will avoid displaying equal edges, i.e. + only one edge between two nodes. removing the + duplicated ones. + """ + + self.simplify = simplify + + def get_simplify(self): + """Get whether to simplify or not. + + Refer to set_simplify for more information. + """ + + return self.simplify + + + def set_type(self, graph_type): + """Set the graph's type, 'graph' or 'digraph'.""" + self.graph_type = graph_type + + def get_type(self): + """Get the graph's type, 'graph' or 'digraph'.""" + return self.graph_type + + def set_name(self, graph_name): + """Set the graph's name.""" + + self.graph_name = graph_name + + def get_name(self): + """Get the graph's name.""" + + return self.graph_name + + def set_strict(self, val): + """Set graph to 'strict' mode. + + This option is only valid for top level graphs. + """ + + self.strict = val + + def get_strict(self, val): + """Get graph's 'strict' mode (True, False). + + This option is only valid for top level graphs. + """ + + return self.strict + + def set_suppress_disconnected(self, val): + """Suppress disconnected nodes in the output graph. + + This option will skip nodes in the graph with no incoming or outgoing + edges. This option works also for subgraphs and has effect only in the + current graph/subgraph. + """ + + self.suppress_disconnected = val + + def get_suppress_disconnected(self, val): + """Get if suppress disconnected is set. + + Refer to set_suppress_disconnected for more information. + """ + + self.suppress_disconnected = val + + def set(self, name, value): + """Set an attribute value by name. + + Given an attribute 'name' it will set its value to 'value'. + There's always the possibility of using the methods: + + set_'name'(value) + + which are defined for all the existing attributes. + """ + if name in self.attributes: + self.__dict__[name] = value + return True + # Attribute is not known + return False + + def get(self, name): + """Get an attribute value by name. + + Given an attribute 'name' it will get its value. + There's always the possibility of using the methods: + + get_'name'() + + which are defined for all the existing attributes. + """ + return self.__dict__[name] + + def add_node(self, graph_node): + """Adds a node object to the graph. + + It takes a node object as its only argument and returns + None. + """ + + if not isinstance(graph_node, Node): + raise Error, 'add_node received a non node class object' + + if self.get_node(graph_node.get_name()) is None: + self.node_list.append(graph_node) + graph_node.parent_graph = self.parent_graph + + def get_node(self, name): + """Retrieved a node from the graph. + + Given a node's name the corresponding Node + instance will be returned. + + If multiple nodes exist with that name, a list of + Node instances is returned. + If only one node exists, the instance is returned. + None is returned otherwise. + """ + + match = [node for node in self.node_list if node.get_name() == str(name)] + + l = len(match) + if l==1: + return match[0] + elif l>1: + return match + else: + return None + + def get_node_list(self): + """Get the list of Node instances. + + This method returns the list of Node instances + composing the graph. + """ + + return self.node_list + + def add_edge(self, graph_edge): + """Adds an edge object to the graph. + + It takes a edge object as its only argument and returns + None. + """ + + if not isinstance(graph_edge, Edge): + raise Error, 'add_edge received a non edge class object' + + self.edge_list.append(graph_edge) + + src = self.get_node(graph_edge.get_source()) + if src is None: + self.add_node(Node(graph_edge.get_source())) + + dst = self.get_node(graph_edge.get_destination()) + if dst is None: + self.add_node(Node(graph_edge.get_destination())) + + graph_edge.parent_graph = self.parent_graph + + if graph_edge.src not in self.edge_src_list: + self.edge_src_list.append(graph_edge.src) + + if graph_edge.dst not in self.edge_dst_list: + self.edge_dst_list.append(graph_edge.dst) + + def get_edge(self, src, dst): + """Retrieved an edge from the graph. + + Given an edge's source and destination the corresponding + Edge instance will be returned. + + If multiple edges exist with that source and destination, + a list of Edge instances is returned. + If only one edge exists, the instance is returned. + None is returned otherwise. + """ + + match = [edge for edge in self.edge_list if edge.src == src and edge.dst == dst] + + l = len(match) + if l==1: + return match[0] + elif l>1: + return match + else: + return None + + def get_edge_list(self): + """Get the list of Edge instances. + + This method returns the list of Edge instances + composing the graph. + """ + + return self.edge_list + + def add_subgraph(self, sgraph): + """Adds an edge object to the graph. + + It takes a subgraph object as its only argument and returns + None. + """ + if not isinstance(sgraph, Subgraph) and not isinstance(sgraph, Cluster): + raise Error, 'add_subgraph received a non subgraph class object' + + self.subgraph_list.append(sgraph) + + sgraph.set_graph_parent(self.parent_graph) + return None + + def get_subgraph(self, name): + """Retrieved a subgraph from the graph. + + Given a subgraph's name the corresponding + Subgraph instance will be returned. + + If multiple subgraphs exist with the same name, a list of + Subgraph instances is returned. + If only one Subgraph exists, the instance is returned. + None is returned otherwise. + """ + + match = [sgraph for sgraph in self.subgraph_list if sgraph.graph_name == name] + + l = len(match) + if l==1: + return match[0] + elif l>1: + return match + else: + return None + + def get_subgraph_list(self): + """Get the list of Subgraph instances. + + This method returns the list of Subgraph instances + in the graph. + """ + + return self.subgraph_list + + def set_graph_parent(self, parent): + """Sets a graph and its elements to point the the parent. + + Any subgraph added to a parent graph receives a reference + to the parent to access some common data. + """ + self.parent_graph = parent + + for elm in self.edge_list: + elm.parent_graph = parent + + for elm in self.node_list: + elm.parent_graph = parent + + for elm in self.subgraph_list: + elm.parent_graph = parent + elm.set_graph_parent(parent) + + def to_string(self, indent=''): + """Returns a string representation of the graph in dot language. + + It will return the graph and all its subelements in string from. + """ + graph = indent+'' + if self.__dict__.has_key('strict'): + if self==self.parent_graph and self.strict: + graph+='strict ' + + graph+=self.graph_type+' '+self.graph_name+' {\n' + + for attr in self.attributes: + if self.__dict__.has_key(attr) \ + and self.__getattribute__(attr) is not None: + graph += indent+'\t'+attr+'=' + val = self.__dict__[attr] + if isinstance(val, str) and not self.is_ID(val): + graph += '"'+val+'"' + else: + graph += str(val) + graph+=';\n' + + # RMF: for graphviz to handle edge/node defaults correctly, + # it seems they must appear before the rest of the nodes/edges + # so if they exist, dump them out first. + for elm in self.node_list: + if elm.name in ['node', 'edge', 'graph']: + graph += indent+'\t'+elm.to_string()+'\n' + + for elm in self.subgraph_list: + graph += elm.to_string(indent+'\t')+'\n' + + for elm in self.node_list: + # RMF: don't repeat the node/edge defaults + if elm.name in ['node', 'edge', 'graph']: + continue + if self.suppress_disconnected: + if elm.name not in self.edge_src_list+self.edge_dst_list: + continue + graph+=indent+'\t'+elm.to_string()+'\n' + + edges_done = [] + for elm in self.edge_list: + if self.simplify and elm in edges_done: + continue + graph+=indent+'\t'+elm.to_string()+'\n' + edges_done.append(elm) + + graph+=indent+'}\n' + + return graph class Subgraph(Graph): - """Class representing a subgraph in Graphviz's dot language. - - This class implements the methods to work on a representation - of a subgraph in Graphviz's dot language. - - subgraph(graph_name='subG', suppress_disconnected=False, attribute=value, ...) - - graph_name: - the subgraph's name - suppress_disconnected: - defaults to false, which will remove from the - subgraph any disconnected nodes. - All the attributes defined in the Graphviz dot language should - be supported. - - Attributes can be set through the dynamically generated methods: - - set_[attribute name], i.e. set_size, set_fontname - - or using the instance's attributes: - - Subgraph.[attribute name], i.e. - subgraph_instance.label, subgraph_instance.fontname - """ - - attributes = Graph.attributes+['rank'] - - # RMF: subgraph should have all the attributes of graph so it can be passed - # as a graph to all methods - def __init__(self, graph_name='subG', suppress_disconnected=False, \ - simplify=False, **attrs): - - self.graph_type = 'subgraph' - self.graph_name = graph_name - self.suppress_disconnected = suppress_disconnected - self.simplify = simplify - self.node_list = [] - self.edge_list = [] - self.edge_src_list = [] - self.edge_dst_list = [] - self.subgraph_list = [] - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) - + """Class representing a subgraph in Graphviz's dot language. + + This class implements the methods to work on a representation + of a subgraph in Graphviz's dot language. + + subgraph(graph_name='subG', suppress_disconnected=False, attribute=value, ...) + + graph_name: + the subgraph's name + suppress_disconnected: + defaults to false, which will remove from the + subgraph any disconnected nodes. + All the attributes defined in the Graphviz dot language should + be supported. + + Attributes can be set through the dynamically generated methods: + + set_[attribute name], i.e. set_size, set_fontname + + or using the instance's attributes: + + Subgraph.[attribute name], i.e. + subgraph_instance.label, subgraph_instance.fontname + """ + + attributes = Graph.attributes+['rank'] + + # RMF: subgraph should have all the attributes of graph so it can be passed + # as a graph to all methods + def __init__(self, graph_name='subG', suppress_disconnected=False, \ + simplify=False, **attrs): + + self.graph_type = 'subgraph' + self.graph_name = graph_name + self.suppress_disconnected = suppress_disconnected + self.simplify = simplify + self.node_list = [] + self.edge_list = [] + self.edge_src_list = [] + self.edge_dst_list = [] + self.subgraph_list = [] + for attr in self.attributes: + # Set all the attributes to None. + self.__setattr__(attr, None) + # Generate all the Setter methods. + self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) + # Generate all the Getter methods. + self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) + for attr, val in attrs.items(): + self.__setattr__(attr, val) + class Cluster(Graph): - """Class representing a cluster in Graphviz's dot language. - - This class implements the methods to work on a representation - of a cluster in Graphviz's dot language. - - cluster(graph_name='subG', suppress_disconnected=False, attribute=value, ...) - - graph_name: - the cluster's name (the string 'cluster' will be always prepended) - suppress_disconnected: - defaults to false, which will remove from the - cluster any disconnected nodes. - All the attributes defined in the Graphviz dot language should - be supported. - - Attributes can be set through the dynamically generated methods: - - set_[attribute name], i.e. set_color, set_fontname - - or using the instance's attributes: - - Cluster.[attribute name], i.e. - cluster_instance.color, cluster_instance.fontname - """ - - attributes = ['pencolor', 'bgcolor', 'labeljust', 'labelloc', 'URL', 'fontcolor', \ - 'fontsize', 'label', 'fontname', 'lp', 'style', 'target', 'color', \ - 'peripheries', 'fillcolor'] - - def __init__(self, graph_name='subG', suppress_disconnected=False, \ - simplify=False, **attrs): - - #if type not in ['subgraph']: - # raise Error, 'Invalid type. Accepted graph types are: subgraph' - self.graph_type = 'subgraph' - self.graph_name = 'cluster_'+graph_name - self.suppress_disconnected = suppress_disconnected - self.simplify = simplify - self.node_list = [] - self.edge_list = [] - self.edge_src_list = [] - self.edge_dst_list = [] - self.subgraph_list = [] - for attr in self.attributes: - # Set all the attributes to None. - self.__setattr__(attr, None) - # Generate all the Setter methods. - self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) - # Generate all the Getter methods. - self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) - for attr, val in attrs.items(): - self.__setattr__(attr, val) + """Class representing a cluster in Graphviz's dot language. + + This class implements the methods to work on a representation + of a cluster in Graphviz's dot language. + + cluster(graph_name='subG', suppress_disconnected=False, attribute=value, ...) + + graph_name: + the cluster's name (the string 'cluster' will be always prepended) + suppress_disconnected: + defaults to false, which will remove from the + cluster any disconnected nodes. + All the attributes defined in the Graphviz dot language should + be supported. + + Attributes can be set through the dynamically generated methods: + + set_[attribute name], i.e. set_color, set_fontname + + or using the instance's attributes: + + Cluster.[attribute name], i.e. + cluster_instance.color, cluster_instance.fontname + """ + + attributes = ['pencolor', 'bgcolor', 'labeljust', 'labelloc', 'URL', 'fontcolor', \ + 'fontsize', 'label', 'fontname', 'lp', 'style', 'target', 'color', \ + 'peripheries', 'fillcolor'] + + def __init__(self, graph_name='subG', suppress_disconnected=False, \ + simplify=False, **attrs): + + #if type not in ['subgraph']: + # raise Error, 'Invalid type. Accepted graph types are: subgraph' + self.graph_type = 'subgraph' + self.graph_name = 'cluster_'+graph_name + self.suppress_disconnected = suppress_disconnected + self.simplify = simplify + self.node_list = [] + self.edge_list = [] + self.edge_src_list = [] + self.edge_dst_list = [] + self.subgraph_list = [] + for attr in self.attributes: + # Set all the attributes to None. + self.__setattr__(attr, None) + # Generate all the Setter methods. + self.__setattr__('set_'+attr, lambda x, a=attr : self.__setattr__(a, x)) + # Generate all the Getter methods. + self.__setattr__('get_'+attr, lambda a=attr : self.__getattribute__(a)) + for attr, val in attrs.items(): + self.__setattr__(attr, val) class Dot(Graph): - """A container for handling a dot language file. - - This class implements methods to write and process - a dot language file. It is a derived class of - the base class 'Graph'. - """ - - progs = None - - formats = ['ps', 'ps2', 'hpgl', 'pcl', 'mif', 'pic', 'gd', 'gd2', 'gif', 'jpg', \ - 'jpeg', 'png', 'wbmp', 'ismap', 'imap', 'cmap', 'vrml', 'vtx', 'mp', \ - 'fig', 'svg', 'svgz', 'dia', 'dot', 'canon', 'plain', 'plain-ext', 'xdot'] - - def __init__(self, prog='dot', **args): - Graph.__init__(self, **args) - - self.prog = prog - - # Automatically creates all the methods enabling the creation - # of output in any of the supported formats. - for frmt in self.formats: - self.__setattr__('create_'+frmt, lambda f=frmt, prog=self.prog : self.create(format=f, prog=prog)) - f = self.__dict__['create_'+frmt] - f.__doc__ = '''Refer to docstring from 'create' for more information.''' - - for frmt in self.formats+['raw']: - self.__setattr__('write_'+frmt, lambda path, f=frmt, prog=self.prog : self.write(path, format=f, prog=prog)) - f = self.__dict__['write_'+frmt] - f.__doc__ = '''Refer to docstring from 'write' for more information.''' - - def set_prog(self, prog): - """Sets the default program. - - Sets the default program in charge of processing - the dot file into a graph. - """ - self.prog = prog - - def write(self, path, prog=None, format='raw'): - """Writes a graph to a file. - - Given a filename 'path' it will open/create and truncate - such file and write on it a representation of the graph - defined by the dot object and in the format specified by - 'format'. - The format 'raw' is used to dump the string representation - of the Dot object, without further processing. - The output can be processed by any of graphviz tools, defined - in 'prog', which defaults to 'dot' - Returns True or False according to the success of the write - operation. - - There's also the preferred possibility of using: - - write_'format'(path, prog='program') - - which are automatically defined for all the supported formats. - [write_ps(), write_gif(), write_dia(), ...] - """ - - if prog is None: - prog = self.prog - - dot_fd = file(path, "w+b") - if format == 'raw': - dot_fd.write(self.to_string()) - else: - dot_fd.write(self.create(prog, format)) - dot_fd.close() - - return True - - def create(self, prog=None, format='ps'): - """Creates and returns a Postscript representation of the graph. - - create will write the graph to a temporary dot file and process - it with the program given by 'prog' (which defaults to 'twopi'), - reading the Postscript output and returning it as a string is the - operation is successful. - On failure None is returned. - - There's also the preferred possibility of using: - - create_'format'(prog='program') - - which are automatically defined for all the supported formats. - [create_ps(), create_gif(), create_dia(), ...] - """ - if prog is None: - prog = self.prog - - if self.progs is None: - self.progs = find_graphviz() - if self.progs is None: - return None - if not self.progs.has_key(prog): - # Program not found ?!?! - return None - - tmp_fd, tmp_name = tempfile.mkstemp() - os.close(tmp_fd) - self.write(tmp_name) - stdin, stdout, stderr = os.popen3(self.progs[prog]+' -T'+format+' '+tmp_name, 'b') - stdin.close() - stderr.close() - data = stdout.read() - stdout.close() - os.unlink(tmp_name) - return data + """A container for handling a dot language file. + + This class implements methods to write and process + a dot language file. It is a derived class of + the base class 'Graph'. + """ + + progs = None + + formats = ['ps', 'ps2', 'hpgl', 'pcl', 'mif', 'pic', 'gd', 'gd2', 'gif', 'jpg', \ + 'jpeg', 'png', 'wbmp', 'ismap', 'imap', 'cmap', 'vrml', 'vtx', 'mp', \ + 'fig', 'svg', 'svgz', 'dia', 'dot', 'canon', 'plain', 'plain-ext', 'xdot'] + + def __init__(self, prog='dot', **args): + Graph.__init__(self, **args) + + self.prog = prog + + # Automatically creates all the methods enabling the creation + # of output in any of the supported formats. + for frmt in self.formats: + self.__setattr__('create_'+frmt, lambda f=frmt, prog=self.prog : self.create(format=f, prog=prog)) + f = self.__dict__['create_'+frmt] + f.__doc__ = '''Refer to docstring from 'create' for more information.''' + + for frmt in self.formats+['raw']: + self.__setattr__('write_'+frmt, lambda path, f=frmt, prog=self.prog : self.write(path, format=f, prog=prog)) + f = self.__dict__['write_'+frmt] + f.__doc__ = '''Refer to docstring from 'write' for more information.''' + + def set_prog(self, prog): + """Sets the default program. + + Sets the default program in charge of processing + the dot file into a graph. + """ + self.prog = prog + + def write(self, path, prog=None, format='raw'): + """Writes a graph to a file. + + Given a filename 'path' it will open/create and truncate + such file and write on it a representation of the graph + defined by the dot object and in the format specified by + 'format'. + The format 'raw' is used to dump the string representation + of the Dot object, without further processing. + The output can be processed by any of graphviz tools, defined + in 'prog', which defaults to 'dot' + Returns True or False according to the success of the write + operation. + + There's also the preferred possibility of using: + + write_'format'(path, prog='program') + + which are automatically defined for all the supported formats. + [write_ps(), write_gif(), write_dia(), ...] + """ + + if prog is None: + prog = self.prog + + dot_fd = file(path, "w+b") + if format == 'raw': + dot_fd.write(self.to_string()) + else: + dot_fd.write(self.create(prog, format)) + dot_fd.close() + + return True + + def create(self, prog=None, format='ps'): + """Creates and returns a Postscript representation of the graph. + + create will write the graph to a temporary dot file and process + it with the program given by 'prog' (which defaults to 'twopi'), + reading the Postscript output and returning it as a string is the + operation is successful. + On failure None is returned. + + There's also the preferred possibility of using: + + create_'format'(prog='program') + + which are automatically defined for all the supported formats. + [create_ps(), create_gif(), create_dia(), ...] + """ + if prog is None: + prog = self.prog + + if self.progs is None: + self.progs = find_graphviz() + if self.progs is None: + return None + if not self.progs.has_key(prog): + # Program not found ?!?! + return None + + tmp_fd, tmp_name = tempfile.mkstemp() + os.close(tmp_fd) + self.write(tmp_name) + stdin, stdout, stderr = os.popen3(self.progs[prog]+' -T'+format+' '+tmp_name, 'b') + stdin.close() + stderr.close() + data = stdout.read() + stdout.close() + os.unlink(tmp_name) + return data diff --git a/bin/addons/base/ir/workflow/pydot/setup.py b/bin/addons/base/ir/workflow/pydot/setup.py index cd8956bebbd..5209f242d81 100755..100644 --- a/bin/addons/base/ir/workflow/pydot/setup.py +++ b/bin/addons/base/ir/workflow/pydot/setup.py @@ -3,21 +3,21 @@ from distutils.core import setup import pydot -setup( name = 'pydot', - version = pydot.__version__, - description = 'Python interface to Graphiz\'s Dot', - author = 'Ero Carrera', - author_email = 'ero@dkbza.org', - url = 'http://dkbza.org/pydot.html', - license = 'MIT', - platforms = ["any"], - classifiers = ['Development Status :: 5 - Production/Stable', \ - 'Intended Audience :: Science/Research', \ - 'License :: OSI Approved :: MIT License',\ - 'Natural Language :: English', \ - 'Operating System :: OS Independent', \ - 'Programming Language :: Python', \ - 'Topic :: Scientific/Engineering :: Visualization',\ - 'Topic :: Software Development :: Libraries :: Python Modules'], - long_description = "\n".join(pydot.__doc__.split('\n')), - py_modules = ['pydot', 'dot_parser']) +setup( name = 'pydot', + version = pydot.__version__, + description = 'Python interface to Graphiz\'s Dot', + author = 'Ero Carrera', + author_email = 'ero@dkbza.org', + url = 'http://dkbza.org/pydot.html', + license = 'MIT', + platforms = ["any"], + classifiers = ['Development Status :: 5 - Production/Stable', \ + 'Intended Audience :: Science/Research', \ + 'License :: OSI Approved :: MIT License',\ + 'Natural Language :: English', \ + 'Operating System :: OS Independent', \ + 'Programming Language :: Python', \ + 'Topic :: Scientific/Engineering :: Visualization',\ + 'Topic :: Software Development :: Libraries :: Python Modules'], + long_description = "\n".join(pydot.__doc__.split('\n')), + py_modules = ['pydot', 'dot_parser']) diff --git a/bin/addons/base/ir/workflow/workflow.py b/bin/addons/base/ir/workflow/workflow.py index 6aebf7262ec..24a8e315570 100644 --- a/bin/addons/base/ir/workflow/workflow.py +++ b/bin/addons/base/ir/workflow/workflow.py @@ -32,157 +32,157 @@ from tools import graph import netsvc class workflow(osv.osv): - _name = "workflow" - _table = "wkf" - _log_access = False - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'osv': fields.char('Resource Model', size=64, required=True), - 'on_create': fields.boolean('On Create'), - 'activities': fields.one2many('workflow.activity', 'wkf_id', 'Activities'), - } - _defaults = { - 'on_create': lambda *a: True - } + _name = "workflow" + _table = "wkf" + _log_access = False + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'osv': fields.char('Resource Model', size=64, required=True), + 'on_create': fields.boolean('On Create'), + 'activities': fields.one2many('workflow.activity', 'wkf_id', 'Activities'), + } + _defaults = { + 'on_create': lambda *a: True + } - def write(self, cr, user, ids, vals, context=None): - if not context: - context={} - wf_service = netsvc.LocalService("workflow") - wf_service.clear_cache(cr, user) - return super(workflow, self).write(cr, user, ids, vals, context=context) + def write(self, cr, user, ids, vals, context=None): + if not context: + context={} + wf_service = netsvc.LocalService("workflow") + wf_service.clear_cache(cr, user) + return super(workflow, self).write(cr, user, ids, vals, context=context) - # - # scale = [stepx, stepy, posx, posy ] - # + # + # scale = [stepx, stepy, posx, posy ] + # - def graph_get(self, cr, uid, id, scale, context={}): + def graph_get(self, cr, uid, id, scale, context={}): - nodes= [] - transitions = [] - start = [] - tres = {} - workflow = self.browse(cr, uid, id, context) - for a in workflow.activities: - nodes.append((a.id,a.name)) - if a.flow_start: - start.append((a.id,a.name)) - for t in a.out_transitions: - transitions.append( ((a.id,a.name), (t.act_to.id,t.act_to.name)) ) - tres[t.id] = (a.id,t.act_to.id) - g = graph(nodes, transitions) - g.process(start) - g.scale(*scale) - result = g.result_get() - results = {} + nodes= [] + transitions = [] + start = [] + tres = {} + workflow = self.browse(cr, uid, id, context) + for a in workflow.activities: + nodes.append((a.id,a.name)) + if a.flow_start: + start.append((a.id,a.name)) + for t in a.out_transitions: + transitions.append( ((a.id,a.name), (t.act_to.id,t.act_to.name)) ) + tres[t.id] = (a.id,t.act_to.id) + g = graph(nodes, transitions) + g.process(start) + g.scale(*scale) + result = g.result_get() + results = {} - for r in result.items(): - r[1]['name'] = r[0][1] - results[str(r[0][0])] = r[1] - return {'node': results, 'transition': tres} + for r in result.items(): + r[1]['name'] = r[0][1] + results[str(r[0][0])] = r[1] + return {'node': results, 'transition': tres} - def create(self, cr, user, vals, context=None): - if not context: - context={} - wf_service = netsvc.LocalService("workflow") - wf_service.clear_cache(cr, user) - return super(workflow, self).create(cr, user, vals, context=context) + def create(self, cr, user, vals, context=None): + if not context: + context={} + wf_service = netsvc.LocalService("workflow") + wf_service.clear_cache(cr, user) + return super(workflow, self).create(cr, user, vals, context=context) workflow() class wkf_activity(osv.osv): - _name = "workflow.activity" - _table = "wkf_activity" - _log_access = False - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'wkf_id': fields.many2one('workflow', 'Workflow', required=True, select=True, ondelete='cascade'), - 'split_mode': fields.selection([('XOR', 'Xor'), ('OR','Or'), ('AND','And')], 'Split Mode', size=3, required=True), - 'join_mode': fields.selection([('XOR', 'Xor'), ('AND', 'And')], 'Join Mode', size=3, required=True), - 'kind': fields.selection([('dummy', 'Dummy'), ('function', 'Function'), ('subflow', 'Subflow'), ('stopall', 'Stop All')], 'Kind', size=64, required=True), - 'action': fields.text('Python Action'), - 'action_id': fields.many2one('ir.actions.server', 'Server Action', ondelete='set null'), - 'flow_start': fields.boolean('Flow Start'), - 'flow_stop': fields.boolean('Flow Stop'), - 'subflow_id': fields.many2one('workflow', 'Subflow'), - 'signal_send': fields.char('Signal (subflow.*)', size=32), - 'out_transitions': fields.one2many('workflow.transition', 'act_from', 'Outgoing transitions'), - 'in_transitions': fields.one2many('workflow.transition', 'act_to', 'Incoming transitions'), - } - _defaults = { - 'kind': lambda *a: 'dummy', - 'join_mode': lambda *a: 'XOR', - 'split_mode': lambda *a: 'XOR', - } + _name = "workflow.activity" + _table = "wkf_activity" + _log_access = False + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'wkf_id': fields.many2one('workflow', 'Workflow', required=True, select=True, ondelete='cascade'), + 'split_mode': fields.selection([('XOR', 'Xor'), ('OR','Or'), ('AND','And')], 'Split Mode', size=3, required=True), + 'join_mode': fields.selection([('XOR', 'Xor'), ('AND', 'And')], 'Join Mode', size=3, required=True), + 'kind': fields.selection([('dummy', 'Dummy'), ('function', 'Function'), ('subflow', 'Subflow'), ('stopall', 'Stop All')], 'Kind', size=64, required=True), + 'action': fields.text('Python Action'), + 'action_id': fields.many2one('ir.actions.server', 'Server Action', ondelete='set null'), + 'flow_start': fields.boolean('Flow Start'), + 'flow_stop': fields.boolean('Flow Stop'), + 'subflow_id': fields.many2one('workflow', 'Subflow'), + 'signal_send': fields.char('Signal (subflow.*)', size=32), + 'out_transitions': fields.one2many('workflow.transition', 'act_from', 'Outgoing transitions'), + 'in_transitions': fields.one2many('workflow.transition', 'act_to', 'Incoming transitions'), + } + _defaults = { + 'kind': lambda *a: 'dummy', + 'join_mode': lambda *a: 'XOR', + 'split_mode': lambda *a: 'XOR', + } wkf_activity() class wkf_transition(osv.osv): - _table = "wkf_transition" - _name = "workflow.transition" - _log_access = False - _rec_name = 'signal' #TODO: pas top mais bon... - _columns = { - 'trigger_model': fields.char('Trigger Type', size=128), - 'trigger_expr_id': fields.char('Trigger Expr ID', size=128), - 'signal': fields.char('Signal (button Name)', size=64), - 'role_id': fields.many2one('res.roles', 'Role Required'), - 'condition': fields.char('Condition', required=True, size=128), - 'act_from': fields.many2one('workflow.activity', 'Source Activity', required=True, select=True, ondelete='cascade'), - 'act_to': fields.many2one('workflow.activity', 'Destination Activity', required=True, select=True, ondelete='cascade'), - } - _defaults = { - 'condition': lambda *a: 'True', - } + _table = "wkf_transition" + _name = "workflow.transition" + _log_access = False + _rec_name = 'signal' #TODO: pas top mais bon... + _columns = { + 'trigger_model': fields.char('Trigger Type', size=128), + 'trigger_expr_id': fields.char('Trigger Expr ID', size=128), + 'signal': fields.char('Signal (button Name)', size=64), + 'role_id': fields.many2one('res.roles', 'Role Required'), + 'condition': fields.char('Condition', required=True, size=128), + 'act_from': fields.many2one('workflow.activity', 'Source Activity', required=True, select=True, ondelete='cascade'), + 'act_to': fields.many2one('workflow.activity', 'Destination Activity', required=True, select=True, ondelete='cascade'), + } + _defaults = { + 'condition': lambda *a: 'True', + } wkf_transition() class wkf_instance(osv.osv): - _table = "wkf_instance" - _name = "workflow.instance" - _rec_name = 'res_type' - _log_access = False - _columns = { - 'wkf_id': fields.many2one('workflow', 'Workflow', ondelete='restrict'), - 'uid': fields.integer('User ID'), - 'res_id': fields.integer('Resource ID'), - 'res_type': fields.char('Resource Model', size=64), - 'state': fields.char('State', size=32), - } - def _auto_init(self, cr, context={}): - 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\'') - 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.commit() + _table = "wkf_instance" + _name = "workflow.instance" + _rec_name = 'res_type' + _log_access = False + _columns = { + 'wkf_id': fields.many2one('workflow', 'Workflow', ondelete='restrict'), + 'uid': fields.integer('User ID'), + 'res_id': fields.integer('Resource ID'), + 'res_type': fields.char('Resource Model', size=64), + 'state': fields.char('State', size=32), + } + def _auto_init(self, cr, context={}): + 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\'') + 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.commit() wkf_instance() class wkf_workitem(osv.osv): - _table = "wkf_workitem" - _name = "workflow.workitem" - _log_access = False - _rec_name = 'state' - _columns = { - 'act_id': fields.many2one('workflow.activity', 'Activity', required=True, ondelete="cascade"), - 'subflow_id': fields.many2one('workflow.instance', 'Subflow', ondelete="cascade"), - 'inst_id': fields.many2one('workflow.instance', 'Instance', required=True, ondelete="cascade", select=1), - 'state': fields.char('State', size=64), - } + _table = "wkf_workitem" + _name = "workflow.workitem" + _log_access = False + _rec_name = 'state' + _columns = { + 'act_id': fields.many2one('workflow.activity', 'Activity', required=True, ondelete="cascade"), + 'subflow_id': fields.many2one('workflow.instance', 'Subflow', ondelete="cascade"), + 'inst_id': fields.many2one('workflow.instance', 'Instance', required=True, ondelete="cascade", select=1), + 'state': fields.char('State', size=64), + } wkf_workitem() class wkf_triggers(osv.osv): - _table = "wkf_triggers" - _name = "workflow.triggers" - _log_access = False - _columns = { - 'res_id': fields.integer('Resource ID', size=128), - 'model': fields.char('Model', size=128), - 'instance_id': fields.many2one('workflow.instance', 'Destination Instance', ondelete="cascade"), - 'workitem_id': fields.many2one('workflow.workitem', 'Workitem', required=True, ondelete="cascade"), - } - def _auto_init(self, cr, context={}): - super(wkf_triggers, self)._auto_init(cr, context) - cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_triggers_res_id_model_index\'') - if not cr.fetchone(): - cr.execute('CREATE INDEX wkf_triggers_res_id_model_index ON wkf_triggers (res_id, model)') - cr.commit() + _table = "wkf_triggers" + _name = "workflow.triggers" + _log_access = False + _columns = { + 'res_id': fields.integer('Resource ID', size=128), + 'model': fields.char('Model', size=128), + 'instance_id': fields.many2one('workflow.instance', 'Destination Instance', ondelete="cascade"), + 'workitem_id': fields.many2one('workflow.workitem', 'Workitem', required=True, ondelete="cascade"), + } + def _auto_init(self, cr, context={}): + super(wkf_triggers, self)._auto_init(cr, context) + cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'wkf_triggers_res_id_model_index\'') + if not cr.fetchone(): + cr.execute('CREATE INDEX wkf_triggers_res_id_model_index ON wkf_triggers (res_id, model)') + cr.commit() wkf_triggers() diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py index 1ad9a844ea0..76ead57fdbd 100644 --- a/bin/addons/base/module/module.py +++ b/bin/addons/base/module/module.py @@ -44,609 +44,609 @@ ver_regexp = re.compile("^(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d* suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$") def vercmp(ver1, ver2): - """ - Compare two versions - Take from portage_versions.py - @param ver1: version to compare with - @type ver1: string (example "1.2-r3") - @param ver2: version to compare again - @type ver2: string (example "2.1-r1") - @rtype: None or float - @return: - 1. position if ver1 is greater than ver2 - 2. negative if ver1 is less than ver2 - 3. 0 if ver1 equals ver2 - 4. None if ver1 or ver2 are invalid - """ - - match1 = ver_regexp.match(ver1) - match2 = ver_regexp.match(ver2) - - if not match1 or not match1.groups(): - return None - if not match2 or not match2.groups(): - return None - - list1 = [int(match1.group(1))] - list2 = [int(match2.group(1))] - - if len(match1.group(2)) or len(match2.group(2)): - vlist1 = match1.group(2)[1:].split(".") - vlist2 = match2.group(2)[1:].split(".") - for i in range(0, max(len(vlist1), len(vlist2))): - # Implicit .0 is given -1, so 1.0.0 > 1.0 - # would be ambiguous if two versions that aren't literally equal - # are given the same value (in sorting, for example). - if len(vlist1) <= i or len(vlist1[i]) == 0: - list1.append(-1) - list2.append(int(vlist2[i])) - elif len(vlist2) <= i or len(vlist2[i]) == 0: - list1.append(int(vlist1[i])) - list2.append(-1) - # Let's make life easy and use integers unless we're forced to use floats - elif (vlist1[i][0] != "0" and vlist2[i][0] != "0"): - list1.append(int(vlist1[i])) - list2.append(int(vlist2[i])) - # now we have to use floats so 1.02 compares correctly against 1.1 - else: - list1.append(float("0."+vlist1[i])) - list2.append(float("0."+vlist2[i])) - # and now the final letter - if len(match1.group(4)): - list1.append(ord(match1.group(4))) - if len(match2.group(4)): - list2.append(ord(match2.group(4))) - - for i in range(0, max(len(list1), len(list2))): - if len(list1) <= i: - return -1 - elif len(list2) <= i: - return 1 - elif list1[i] != list2[i]: - return list1[i] - list2[i] - - # main version is equal, so now compare the _suffix part - list1 = match1.group(5).split("_")[1:] - list2 = match2.group(5).split("_")[1:] - - for i in range(0, max(len(list1), len(list2))): - # Implicit _p0 is given a value of -1, so that 1 < 1_p0 - if len(list1) <= i: - s1 = ("p","-1") - else: - s1 = suffix_regexp.match(list1[i]).groups() - if len(list2) <= i: - s2 = ("p","-1") - else: - s2 = suffix_regexp.match(list2[i]).groups() - if s1[0] != s2[0]: - return suffix_value[s1[0]] - suffix_value[s2[0]] - if s1[1] != s2[1]: - # it's possible that the s(1|2)[1] == '' - # in such a case, fudge it. - try: - r1 = int(s1[1]) - except ValueError: - r1 = 0 - try: - r2 = int(s2[1]) - except ValueError: - r2 = 0 - if r1 - r2: - return r1 - r2 - - # the suffix part is equal to, so finally check the revision - if match1.group(9): - r1 = int(match1.group(9)) - else: - r1 = 0 - if match2.group(9): - r2 = int(match2.group(9)) - else: - r2 = 0 - return r1 - r2 + """ + Compare two versions + Take from portage_versions.py + @param ver1: version to compare with + @type ver1: string (example "1.2-r3") + @param ver2: version to compare again + @type ver2: string (example "2.1-r1") + @rtype: None or float + @return: + 1. position if ver1 is greater than ver2 + 2. negative if ver1 is less than ver2 + 3. 0 if ver1 equals ver2 + 4. None if ver1 or ver2 are invalid + """ + + match1 = ver_regexp.match(ver1) + match2 = ver_regexp.match(ver2) + + if not match1 or not match1.groups(): + return None + if not match2 or not match2.groups(): + return None + + list1 = [int(match1.group(1))] + list2 = [int(match2.group(1))] + + if len(match1.group(2)) or len(match2.group(2)): + vlist1 = match1.group(2)[1:].split(".") + vlist2 = match2.group(2)[1:].split(".") + for i in range(0, max(len(vlist1), len(vlist2))): + # Implicit .0 is given -1, so 1.0.0 > 1.0 + # would be ambiguous if two versions that aren't literally equal + # are given the same value (in sorting, for example). + if len(vlist1) <= i or len(vlist1[i]) == 0: + list1.append(-1) + list2.append(int(vlist2[i])) + elif len(vlist2) <= i or len(vlist2[i]) == 0: + list1.append(int(vlist1[i])) + list2.append(-1) + # Let's make life easy and use integers unless we're forced to use floats + elif (vlist1[i][0] != "0" and vlist2[i][0] != "0"): + list1.append(int(vlist1[i])) + list2.append(int(vlist2[i])) + # now we have to use floats so 1.02 compares correctly against 1.1 + else: + list1.append(float("0."+vlist1[i])) + list2.append(float("0."+vlist2[i])) + # and now the final letter + if len(match1.group(4)): + list1.append(ord(match1.group(4))) + if len(match2.group(4)): + list2.append(ord(match2.group(4))) + + for i in range(0, max(len(list1), len(list2))): + if len(list1) <= i: + return -1 + elif len(list2) <= i: + return 1 + elif list1[i] != list2[i]: + return list1[i] - list2[i] + + # main version is equal, so now compare the _suffix part + list1 = match1.group(5).split("_")[1:] + list2 = match2.group(5).split("_")[1:] + + for i in range(0, max(len(list1), len(list2))): + # Implicit _p0 is given a value of -1, so that 1 < 1_p0 + if len(list1) <= i: + s1 = ("p","-1") + else: + s1 = suffix_regexp.match(list1[i]).groups() + if len(list2) <= i: + s2 = ("p","-1") + else: + s2 = suffix_regexp.match(list2[i]).groups() + if s1[0] != s2[0]: + return suffix_value[s1[0]] - suffix_value[s2[0]] + if s1[1] != s2[1]: + # it's possible that the s(1|2)[1] == '' + # in such a case, fudge it. + try: + r1 = int(s1[1]) + except ValueError: + r1 = 0 + try: + r2 = int(s2[1]) + except ValueError: + r2 = 0 + if r1 - r2: + return r1 - r2 + + # the suffix part is equal to, so finally check the revision + if match1.group(9): + r1 = int(match1.group(9)) + else: + r1 = 0 + if match2.group(9): + r2 = int(match2.group(9)) + else: + r2 = 0 + return r1 - r2 class module_repository(osv.osv): - _name = "ir.module.repository" - _description = "Module Repository" - _columns = { - 'name': fields.char('Name', size=128), - 'url': fields.char('Url', size=256, required=True), - 'sequence': fields.integer('Sequence', required=True), - 'filter': fields.char('Filter', size=128, required=True, - help='Regexp to search module on the repository webpage:\n' - '- The first parenthesis must match the name of the module.\n' - '- The second parenthesis must match all the version number.\n' - '- The last parenthesis must match the extension of the module.'), - 'active': fields.boolean('Active'), - } - _defaults = { - 'sequence': lambda *a: 5, - 'filter': lambda *a: 'href="([a-zA-Z0-9_]+)-('+release.version.rsplit('.', 1)[0]+'.(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?)(\.zip)"', - 'active': lambda *a: 1, - } - _order = "sequence" + _name = "ir.module.repository" + _description = "Module Repository" + _columns = { + 'name': fields.char('Name', size=128), + 'url': fields.char('Url', size=256, required=True), + 'sequence': fields.integer('Sequence', required=True), + 'filter': fields.char('Filter', size=128, required=True, + help='Regexp to search module on the repository webpage:\n' + '- The first parenthesis must match the name of the module.\n' + '- The second parenthesis must match all the version number.\n' + '- The last parenthesis must match the extension of the module.'), + 'active': fields.boolean('Active'), + } + _defaults = { + 'sequence': lambda *a: 5, + 'filter': lambda *a: 'href="([a-zA-Z0-9_]+)-('+release.version.rsplit('.', 1)[0]+'.(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?)(\.zip)"', + 'active': lambda *a: 1, + } + _order = "sequence" module_repository() class module_category(osv.osv): - _name = "ir.module.category" - _description = "Module Category" - - def _module_nbr(self,cr,uid, ids, prop, unknow_none,context): - cr.execute('select category_id,count(*) from ir_module_module where category_id in ('+','.join(map(str,ids))+') or category_id in (select id from ir_module_category where parent_id in ('+','.join(map(str,ids))+')) group by category_id') - result = dict(cr.fetchall()) - for id in ids: - cr.execute('select id from ir_module_category where parent_id=%d', (id,)) - childs = [c for c, in cr.fetchall()] - result[id] = reduce(lambda x,y:x+y, [result.get(c, 0) for c in childs], result.get(id, 0)) - return result - - _columns = { - 'name': fields.char("Name", size=128, required=True), - 'parent_id': fields.many2one('ir.module.category', 'Parent Category', select=True), - 'child_ids': fields.one2many('ir.module.category', 'parent_id', 'Parent Category'), - 'module_nr': fields.function(_module_nbr, method=True, string='# of Modules', type='integer') - } - _order = 'name' + _name = "ir.module.category" + _description = "Module Category" + + def _module_nbr(self,cr,uid, ids, prop, unknow_none,context): + cr.execute('select category_id,count(*) from ir_module_module where category_id in ('+','.join(map(str,ids))+') or category_id in (select id from ir_module_category where parent_id in ('+','.join(map(str,ids))+')) group by category_id') + result = dict(cr.fetchall()) + for id in ids: + cr.execute('select id from ir_module_category where parent_id=%d', (id,)) + childs = [c for c, in cr.fetchall()] + result[id] = reduce(lambda x,y:x+y, [result.get(c, 0) for c in childs], result.get(id, 0)) + return result + + _columns = { + 'name': fields.char("Name", size=128, required=True), + 'parent_id': fields.many2one('ir.module.category', 'Parent Category', select=True), + 'child_ids': fields.one2many('ir.module.category', 'parent_id', 'Parent Category'), + 'module_nr': fields.function(_module_nbr, method=True, string='# of Modules', type='integer') + } + _order = 'name' module_category() class module(osv.osv): - _name = "ir.module.module" - _description = "Module" - - def get_module_info(self, name): - try: - f = tools.file_open(os.path.join(name, '__terp__.py')) - data = f.read() - info = eval(data) - if 'version' in info: - info['version'] = release.version.rsplit('.', 1)[0] + '.' + info['version'] - f.close() - except: - return {} - return info - - def _get_installed_version(self, cr, uid, ids, field_name=None, arg=None, context={}): - res = {} - for m in self.browse(cr, uid, ids): - if m.state in ('installed', 'to upgrade', 'to remove'): - res[m.id] = self.get_module_info(m.name).get('version', '') - else: - res[m.id] = '' - return res - - _columns = { - 'name': fields.char("Name", size=128, readonly=True, required=True), - 'category_id': fields.many2one('ir.module.category', 'Category', readonly=True), - 'shortdesc': fields.char('Short description', size=256, readonly=True), - 'description': fields.text("Description", readonly=True), - 'author': fields.char("Author", size=128, readonly=True), - 'website': fields.char("Website", size=256, readonly=True), - 'installed_version': fields.function(_get_installed_version, method=True, - string='Installed version', type='char'), - 'latest_version': fields.char('Latest version', size=64, readonly=True), - 'published_version': fields.char('Published Version', size=64, readonly=True), - 'url': fields.char('URL', size=128), - 'dependencies_id': fields.one2many('ir.module.module.dependency', - 'module_id', 'Dependencies', readonly=True), - 'state': fields.selection([ - ('uninstallable','Not Installable'), - ('uninstalled','Not Installed'), - ('installed','Installed'), - ('to upgrade','To be upgraded'), - ('to remove','To be removed'), - ('to install','To be installed') - ], string='State', readonly=True), - 'demo': fields.boolean('Demo data'), - 'license': fields.selection([('GPL-2', 'GPL-2'), - ('Other proprietary', 'Other proprietary')], string='License', - readonly=True), - } - - _defaults = { - 'state': lambda *a: 'uninstalled', - 'demo': lambda *a: False, - 'license': lambda *a: 'GPL-2', - } - _order = 'name' - - _sql_constraints = [ - ('name_uniq', 'unique (name)', 'The name of the module must be unique !') - ] - - def unlink(self, cr, uid, ids, context=None): - if not ids: - return True - if isinstance(ids, (int, long)): - ids = [ids] - for mod in self.read(cr, uid, ids, ['state'], context): - if mod['state'] in ('installed', 'to upgrade', 'to remove', 'to install'): - raise orm.except_orm(_('Error'), - _('You try to remove a module that is installed or will be installed')) - return super(module, self).unlink(cr, uid, ids, context=context) - - def state_change(self, cr, uid, ids, newstate, context={}, level=50): - if level<1: - raise Exception, _('Recursion error in modules dependencies !') - demo = True - for module in self.browse(cr, uid, ids): - mdemo = True - for dep in module.dependencies_id: - ids2 = self.search(cr, uid, [('name','=',dep.name)]) - mdemo = self.state_change(cr, uid, ids2, newstate, context, level-1,)\ - and mdemo - if not module.dependencies_id: - mdemo = module.demo - if module.state == 'uninstalled': - self.write(cr, uid, [module.id], {'state': newstate, 'demo':mdemo}) - demo = demo and mdemo - return demo - - def state_upgrade(self, cr, uid, ids, newstate, context=None, level=50): - dep_obj = self.pool.get('ir.module.module.dependency') - if level<1: - raise Exception, _('Recursion error in modules dependencies !') - for module in self.browse(cr, uid, ids): - dep_ids = dep_obj.search(cr, uid, [('name', '=', module.name)]) - if dep_ids: - ids2 = [] - for dep in dep_obj.browse(cr, uid, dep_ids): - if dep.module_id.state != 'to upgrade': - ids2.append(dep.module_id.id) - self.state_upgrade(cr, uid, ids2, newstate, context, level) - if module.state == 'installed': - self.write(cr, uid, module.id, {'state': newstate}) - return True - - def button_install(self, cr, uid, ids, context={}): - return self.state_change(cr, uid, ids, 'to install', context) - - def button_install_cancel(self, cr, uid, ids, context={}): - self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False}) - return True - - def button_uninstall(self, cr, uid, ids, context={}): - for module in self.browse(cr, uid, ids): - cr.execute('''select m.state,m.name - from - ir_module_module_dependency d - join - ir_module_module m on (d.module_id=m.id) - where - d.name=%s and - m.state not in ('uninstalled','uninstallable','to remove')''', (module.name,)) - res = cr.fetchall() - if res: - raise orm.except_orm(_('Error'), _('The module you are trying to remove depends on installed modules :\n %s') % '\n'.join(map(lambda x: '\t%s: %s' % (x[0], x[1]), res))) - self.write(cr, uid, ids, {'state': 'to remove'}) - return True - - def button_uninstall_cancel(self, cr, uid, ids, context={}): - self.write(cr, uid, ids, {'state': 'installed'}) - return True - def button_upgrade(self, cr, uid, ids, context=None): - return self.state_upgrade(cr, uid, ids, 'to upgrade', context) - def button_upgrade_cancel(self, cr, uid, ids, context={}): - self.write(cr, uid, ids, {'state': 'installed'}) - return True - def button_update_translations(self, cr, uid, ids, context={}): - cr.execute('select code from res_lang where translatable=TRUE') - langs = [l[0] for l in cr.fetchall()] - modules = self.read(cr, uid, ids, ['name']) - for module in modules: - files = self.get_module_info(module['name']).get('translations', {}) - for lang in langs: - if files.has_key(lang): - filepath = files[lang] - # if filepath does not contain :// we prepend the path of the module - if filepath.find('://') == -1: - filepath = addons.get_module_resource(module['name'], filepath) - tools.trans_load(filepath, lang) - return True - - # update the list of available packages - def update_list(self, cr, uid, context={}): - robj = self.pool.get('ir.module.repository') - res = [0, 0] # [update, add] - - # iterate through installed modules and mark them as being so - for name in addons.get_modules(): - mod_name = name - if name[-4:]=='.zip': - mod_name=name[:-4] - ids = self.search(cr, uid, [('name','=',mod_name)]) - if ids: - id = ids[0] - mod = self.browse(cr, uid, id) - terp = self.get_module_info(mod_name) - if terp.get('installable', True) and mod.state == 'uninstallable': - self.write(cr, uid, id, {'state': 'uninstalled'}) - if vercmp(terp.get('version', ''), mod.latest_version or '0') > 0: - self.write(cr, uid, id, { - 'latest_version': terp.get('version'), - 'url': ''}) - res[0] += 1 - self.write(cr, uid, id, { - 'description': terp.get('description', ''), - 'shortdesc': terp.get('name', ''), - 'author': terp.get('author', 'Unknown'), - 'website': terp.get('website', ''), - 'license': terp.get('license', 'GPL-2'), - }) - cr.execute('DELETE FROM ir_module_module_dependency\ - WHERE module_id = %d', (id,)) - self._update_dependencies(cr, uid, ids[0], terp.get('depends', - [])) - self._update_category(cr, uid, ids[0], terp.get('category', - 'Uncategorized')) - continue - terp_file = addons.get_module_resource(name, '__terp__.py') - mod_path = addons.get_module_path(name) - if os.path.isdir(mod_path) or os.path.islink(mod_path) or zipfile.is_zipfile(mod_path): - terp = self.get_module_info(mod_name) - if not terp or not terp.get('installable', True): - continue - if not os.path.isfile(mod_path+'.zip'): - import imp - # XXX must restrict to only addons paths - imp.load_module(name, *imp.find_module(mod_name)) - else: - import zipimport - zimp = zipimport.zipimporter(mod_path+'.zip') - zimp.load_module(mod_name) - id = self.create(cr, uid, { - 'name': mod_name, - 'state': 'uninstalled', - 'description': terp.get('description', ''), - 'shortdesc': terp.get('name', ''), - 'author': terp.get('author', 'Unknown'), - 'website': terp.get('website', ''), - 'latest_version': terp.get('version', ''), - 'license': terp.get('license', 'GPL-2'), - }) - res[1] += 1 - self._update_dependencies(cr, uid, id, terp.get('depends', [])) - self._update_category(cr, uid, id, terp.get('category', 'Uncategorized')) - - import socket - socket.setdefaulttimeout(10) - for repository in robj.browse(cr, uid, robj.search(cr, uid, [])): - try: - index_page = urllib.urlopen(repository.url).read() - except IOError, e: - if e.errno == 21: - raise orm.except_orm(_('Error'), - _("This url '%s' must provide an html file with links to zip modules") % (repository.url)) - else: - raise - modules = re.findall(repository.filter, index_page, re.I+re.M) - mod_sort = {} - for m in modules: - name = m[0] - version = m[1] - extension = m[-1] - if version == 'x': # 'x' version was a mistake - version = '0' - if name in mod_sort: - if vercmp(version, mod_sort[name][0]) <= 0: - continue - mod_sort[name] = [version, extension] - for name in mod_sort.keys(): - version, extension = mod_sort[name] - url = repository.url+'/'+name+'-'+version+extension - ids = self.search(cr, uid, [('name','=',name)]) - if not ids: - self.create(cr, uid, { - 'name': name, - 'latest_version': version, - 'published_version': version, - 'url': url, - 'state': 'uninstalled', - }) - res[1] += 1 - else: - id = ids[0] - latest_version = self.read(cr, uid, id, ['latest_version'])\ - ['latest_version'] - if latest_version == 'x': # 'x' version was a mistake - latest_version = '0' - c = vercmp(version, latest_version) - if c > 0: - self.write(cr, uid, id, - {'latest_version': version, 'url': url}) - res[0] += 1 - published_version = self.read(cr, uid, id, ['published_version'])\ - ['published_version'] - if published_version == 'x' or not published_version: - published_version = '0' - c = vercmp(version, published_version) - if c > 0: - self.write(cr, uid, id, - {'published_version': version}) - return res - - def download(self, cr, uid, ids, download=True, context=None): - res = [] - for mod in self.browse(cr, uid, ids, context=context): - if not mod.url: - continue - match = re.search('-([a-zA-Z0-9\._-]+)(\.zip)', mod.url, re.I) - version = '0' - if match: - version = match.group(1) - if vercmp(mod.installed_version or '0', version) >= 0: - continue - res.append(mod.url) - if not download: - continue - zipfile = urllib.urlopen(mod.url).read() - fname = addons.get_module_path(mod.name+'.zip') - try: - fp = file(fname, 'wb') - fp.write(zipfile) - fp.close() - except IOError, e: - raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,)) - terp = self.get_module_info(mod.name) - self.write(cr, uid, mod.id, { - 'description': terp.get('description', ''), - 'shortdesc': terp.get('name', ''), - 'author': terp.get('author', 'Unknown'), - 'website': terp.get('website', ''), - 'license': terp.get('license', 'GPL-2'), - }) - cr.execute('DELETE FROM ir_module_module_dependency ' \ - 'WHERE module_id = %d', (mod.id,)) - self._update_dependencies(cr, uid, mod.id, terp.get('depends', - [])) - self._update_category(cr, uid, mod.id, terp.get('category', - 'Uncategorized')) - # Import module - zimp = zipimport.zipimporter(fname) - zimp.load_module(mod.name) - return res - - def _update_dependencies(self, cr, uid, id, depends=[]): - for d in depends: - cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%d, %s)', (id, d)) - - def _update_category(self, cr, uid, id, category='Uncategorized'): - categs = category.split('/') - p_id = None - while categs: - if p_id is not None: - cr.execute('select id from ir_module_category where name=%s and parent_id=%d', (categs[0], p_id)) - else: - cr.execute('select id from ir_module_category where name=%s and parent_id is NULL', (categs[0],)) - c_id = cr.fetchone() - if not c_id: - cr.execute('select nextval(\'ir_module_category_id_seq\')') - c_id = cr.fetchone()[0] - cr.execute('insert into ir_module_category (id, name, parent_id) values (%d, %s, %d)', (c_id, categs[0], p_id)) - else: - c_id = c_id[0] - p_id = c_id - categs = categs[1:] - self.write(cr, uid, [id], {'category_id': p_id}) + _name = "ir.module.module" + _description = "Module" + + def get_module_info(self, name): + try: + f = tools.file_open(os.path.join(name, '__terp__.py')) + data = f.read() + info = eval(data) + if 'version' in info: + info['version'] = release.version.rsplit('.', 1)[0] + '.' + info['version'] + f.close() + except: + return {} + return info + + def _get_installed_version(self, cr, uid, ids, field_name=None, arg=None, context={}): + res = {} + for m in self.browse(cr, uid, ids): + if m.state in ('installed', 'to upgrade', 'to remove'): + res[m.id] = self.get_module_info(m.name).get('version', '') + else: + res[m.id] = '' + return res + + _columns = { + 'name': fields.char("Name", size=128, readonly=True, required=True), + 'category_id': fields.many2one('ir.module.category', 'Category', readonly=True), + 'shortdesc': fields.char('Short description', size=256, readonly=True), + 'description': fields.text("Description", readonly=True), + 'author': fields.char("Author", size=128, readonly=True), + 'website': fields.char("Website", size=256, readonly=True), + 'installed_version': fields.function(_get_installed_version, method=True, + string='Installed version', type='char'), + 'latest_version': fields.char('Latest version', size=64, readonly=True), + 'published_version': fields.char('Published Version', size=64, readonly=True), + 'url': fields.char('URL', size=128), + 'dependencies_id': fields.one2many('ir.module.module.dependency', + 'module_id', 'Dependencies', readonly=True), + 'state': fields.selection([ + ('uninstallable','Not Installable'), + ('uninstalled','Not Installed'), + ('installed','Installed'), + ('to upgrade','To be upgraded'), + ('to remove','To be removed'), + ('to install','To be installed') + ], string='State', readonly=True), + 'demo': fields.boolean('Demo data'), + 'license': fields.selection([('GPL-2', 'GPL-2'), + ('Other proprietary', 'Other proprietary')], string='License', + readonly=True), + } + + _defaults = { + 'state': lambda *a: 'uninstalled', + 'demo': lambda *a: False, + 'license': lambda *a: 'GPL-2', + } + _order = 'name' + + _sql_constraints = [ + ('name_uniq', 'unique (name)', 'The name of the module must be unique !') + ] + + def unlink(self, cr, uid, ids, context=None): + if not ids: + return True + if isinstance(ids, (int, long)): + ids = [ids] + for mod in self.read(cr, uid, ids, ['state'], context): + if mod['state'] in ('installed', 'to upgrade', 'to remove', 'to install'): + raise orm.except_orm(_('Error'), + _('You try to remove a module that is installed or will be installed')) + return super(module, self).unlink(cr, uid, ids, context=context) + + def state_change(self, cr, uid, ids, newstate, context={}, level=50): + if level<1: + raise Exception, _('Recursion error in modules dependencies !') + demo = True + for module in self.browse(cr, uid, ids): + mdemo = True + for dep in module.dependencies_id: + ids2 = self.search(cr, uid, [('name','=',dep.name)]) + mdemo = self.state_change(cr, uid, ids2, newstate, context, level-1,)\ + and mdemo + if not module.dependencies_id: + mdemo = module.demo + if module.state == 'uninstalled': + self.write(cr, uid, [module.id], {'state': newstate, 'demo':mdemo}) + demo = demo and mdemo + return demo + + def state_upgrade(self, cr, uid, ids, newstate, context=None, level=50): + dep_obj = self.pool.get('ir.module.module.dependency') + if level<1: + raise Exception, _('Recursion error in modules dependencies !') + for module in self.browse(cr, uid, ids): + dep_ids = dep_obj.search(cr, uid, [('name', '=', module.name)]) + if dep_ids: + ids2 = [] + for dep in dep_obj.browse(cr, uid, dep_ids): + if dep.module_id.state != 'to upgrade': + ids2.append(dep.module_id.id) + self.state_upgrade(cr, uid, ids2, newstate, context, level) + if module.state == 'installed': + self.write(cr, uid, module.id, {'state': newstate}) + return True + + def button_install(self, cr, uid, ids, context={}): + return self.state_change(cr, uid, ids, 'to install', context) + + def button_install_cancel(self, cr, uid, ids, context={}): + self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False}) + return True + + def button_uninstall(self, cr, uid, ids, context={}): + for module in self.browse(cr, uid, ids): + cr.execute('''select m.state,m.name + from + ir_module_module_dependency d + join + ir_module_module m on (d.module_id=m.id) + where + d.name=%s and + m.state not in ('uninstalled','uninstallable','to remove')''', (module.name,)) + res = cr.fetchall() + if res: + raise orm.except_orm(_('Error'), _('The module you are trying to remove depends on installed modules :\n %s') % '\n'.join(map(lambda x: '\t%s: %s' % (x[0], x[1]), res))) + self.write(cr, uid, ids, {'state': 'to remove'}) + return True + + def button_uninstall_cancel(self, cr, uid, ids, context={}): + self.write(cr, uid, ids, {'state': 'installed'}) + return True + def button_upgrade(self, cr, uid, ids, context=None): + return self.state_upgrade(cr, uid, ids, 'to upgrade', context) + def button_upgrade_cancel(self, cr, uid, ids, context={}): + self.write(cr, uid, ids, {'state': 'installed'}) + return True + def button_update_translations(self, cr, uid, ids, context={}): + cr.execute('select code from res_lang where translatable=TRUE') + langs = [l[0] for l in cr.fetchall()] + modules = self.read(cr, uid, ids, ['name']) + for module in modules: + files = self.get_module_info(module['name']).get('translations', {}) + for lang in langs: + if files.has_key(lang): + filepath = files[lang] + # if filepath does not contain :// we prepend the path of the module + if filepath.find('://') == -1: + filepath = addons.get_module_resource(module['name'], filepath) + tools.trans_load(filepath, lang) + return True + + # update the list of available packages + def update_list(self, cr, uid, context={}): + robj = self.pool.get('ir.module.repository') + res = [0, 0] # [update, add] + + # iterate through installed modules and mark them as being so + for name in addons.get_modules(): + mod_name = name + if name[-4:]=='.zip': + mod_name=name[:-4] + ids = self.search(cr, uid, [('name','=',mod_name)]) + if ids: + id = ids[0] + mod = self.browse(cr, uid, id) + terp = self.get_module_info(mod_name) + if terp.get('installable', True) and mod.state == 'uninstallable': + self.write(cr, uid, id, {'state': 'uninstalled'}) + if vercmp(terp.get('version', ''), mod.latest_version or '0') > 0: + self.write(cr, uid, id, { + 'latest_version': terp.get('version'), + 'url': ''}) + res[0] += 1 + self.write(cr, uid, id, { + 'description': terp.get('description', ''), + 'shortdesc': terp.get('name', ''), + 'author': terp.get('author', 'Unknown'), + 'website': terp.get('website', ''), + 'license': terp.get('license', 'GPL-2'), + }) + cr.execute('DELETE FROM ir_module_module_dependency\ + WHERE module_id = %d', (id,)) + self._update_dependencies(cr, uid, ids[0], terp.get('depends', + [])) + self._update_category(cr, uid, ids[0], terp.get('category', + 'Uncategorized')) + continue + terp_file = addons.get_module_resource(name, '__terp__.py') + mod_path = addons.get_module_path(name) + if os.path.isdir(mod_path) or os.path.islink(mod_path) or zipfile.is_zipfile(mod_path): + terp = self.get_module_info(mod_name) + if not terp or not terp.get('installable', True): + continue + if not os.path.isfile(mod_path+'.zip'): + import imp + # XXX must restrict to only addons paths + imp.load_module(name, *imp.find_module(mod_name)) + else: + import zipimport + zimp = zipimport.zipimporter(mod_path+'.zip') + zimp.load_module(mod_name) + id = self.create(cr, uid, { + 'name': mod_name, + 'state': 'uninstalled', + 'description': terp.get('description', ''), + 'shortdesc': terp.get('name', ''), + 'author': terp.get('author', 'Unknown'), + 'website': terp.get('website', ''), + 'latest_version': terp.get('version', ''), + 'license': terp.get('license', 'GPL-2'), + }) + res[1] += 1 + self._update_dependencies(cr, uid, id, terp.get('depends', [])) + self._update_category(cr, uid, id, terp.get('category', 'Uncategorized')) + + import socket + socket.setdefaulttimeout(10) + for repository in robj.browse(cr, uid, robj.search(cr, uid, [])): + try: + index_page = urllib.urlopen(repository.url).read() + except IOError, e: + if e.errno == 21: + raise orm.except_orm(_('Error'), + _("This url '%s' must provide an html file with links to zip modules") % (repository.url)) + else: + raise + modules = re.findall(repository.filter, index_page, re.I+re.M) + mod_sort = {} + for m in modules: + name = m[0] + version = m[1] + extension = m[-1] + if version == 'x': # 'x' version was a mistake + version = '0' + if name in mod_sort: + if vercmp(version, mod_sort[name][0]) <= 0: + continue + mod_sort[name] = [version, extension] + for name in mod_sort.keys(): + version, extension = mod_sort[name] + url = repository.url+'/'+name+'-'+version+extension + ids = self.search(cr, uid, [('name','=',name)]) + if not ids: + self.create(cr, uid, { + 'name': name, + 'latest_version': version, + 'published_version': version, + 'url': url, + 'state': 'uninstalled', + }) + res[1] += 1 + else: + id = ids[0] + latest_version = self.read(cr, uid, id, ['latest_version'])\ + ['latest_version'] + if latest_version == 'x': # 'x' version was a mistake + latest_version = '0' + c = vercmp(version, latest_version) + if c > 0: + self.write(cr, uid, id, + {'latest_version': version, 'url': url}) + res[0] += 1 + published_version = self.read(cr, uid, id, ['published_version'])\ + ['published_version'] + if published_version == 'x' or not published_version: + published_version = '0' + c = vercmp(version, published_version) + if c > 0: + self.write(cr, uid, id, + {'published_version': version}) + return res + + def download(self, cr, uid, ids, download=True, context=None): + res = [] + for mod in self.browse(cr, uid, ids, context=context): + if not mod.url: + continue + match = re.search('-([a-zA-Z0-9\._-]+)(\.zip)', mod.url, re.I) + version = '0' + if match: + version = match.group(1) + if vercmp(mod.installed_version or '0', version) >= 0: + continue + res.append(mod.url) + if not download: + continue + zipfile = urllib.urlopen(mod.url).read() + fname = addons.get_module_path(mod.name+'.zip') + try: + fp = file(fname, 'wb') + fp.write(zipfile) + fp.close() + except IOError, e: + raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,)) + terp = self.get_module_info(mod.name) + self.write(cr, uid, mod.id, { + 'description': terp.get('description', ''), + 'shortdesc': terp.get('name', ''), + 'author': terp.get('author', 'Unknown'), + 'website': terp.get('website', ''), + 'license': terp.get('license', 'GPL-2'), + }) + cr.execute('DELETE FROM ir_module_module_dependency ' \ + 'WHERE module_id = %d', (mod.id,)) + self._update_dependencies(cr, uid, mod.id, terp.get('depends', + [])) + self._update_category(cr, uid, mod.id, terp.get('category', + 'Uncategorized')) + # Import module + zimp = zipimport.zipimporter(fname) + zimp.load_module(mod.name) + return res + + def _update_dependencies(self, cr, uid, id, depends=[]): + for d in depends: + cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%d, %s)', (id, d)) + + def _update_category(self, cr, uid, id, category='Uncategorized'): + categs = category.split('/') + p_id = None + while categs: + if p_id is not None: + cr.execute('select id from ir_module_category where name=%s and parent_id=%d', (categs[0], p_id)) + else: + cr.execute('select id from ir_module_category where name=%s and parent_id is NULL', (categs[0],)) + c_id = cr.fetchone() + if not c_id: + cr.execute('select nextval(\'ir_module_category_id_seq\')') + c_id = cr.fetchone()[0] + cr.execute('insert into ir_module_category (id, name, parent_id) values (%d, %s, %d)', (c_id, categs[0], p_id)) + else: + c_id = c_id[0] + p_id = c_id + categs = categs[1:] + self.write(cr, uid, [id], {'category_id': p_id}) module() class module_dependency(osv.osv): - _name = "ir.module.module.dependency" - _description = "Module dependency" - - def _state(self, cr, uid, ids, name, args, context={}): - result = {} - mod_obj = self.pool.get('ir.module.module') - for md in self.browse(cr, uid, ids): - ids = mod_obj.search(cr, uid, [('name', '=', md.name)]) - if ids: - result[md.id] = mod_obj.read(cr, uid, [ids[0]], ['state'])[0]['state'] - else: - result[md.id] = 'unknown' - return result - - _columns = { - 'name': fields.char('Name', size=128), - 'module_id': fields.many2one('ir.module.module', 'Module', select=True, ondelete='cascade'), - 'state': fields.function(_state, method=True, type='selection', selection=[ - ('uninstallable','Uninstallable'), - ('uninstalled','Not Installed'), - ('installed','Installed'), - ('to upgrade','To be upgraded'), - ('to remove','To be removed'), - ('to install','To be installed'), - ('unknown', 'Unknown'), - ], string='State', readonly=True), - } + _name = "ir.module.module.dependency" + _description = "Module dependency" + + def _state(self, cr, uid, ids, name, args, context={}): + result = {} + mod_obj = self.pool.get('ir.module.module') + for md in self.browse(cr, uid, ids): + ids = mod_obj.search(cr, uid, [('name', '=', md.name)]) + if ids: + result[md.id] = mod_obj.read(cr, uid, [ids[0]], ['state'])[0]['state'] + else: + result[md.id] = 'unknown' + return result + + _columns = { + 'name': fields.char('Name', size=128), + 'module_id': fields.many2one('ir.module.module', 'Module', select=True, ondelete='cascade'), + 'state': fields.function(_state, method=True, type='selection', selection=[ + ('uninstallable','Uninstallable'), + ('uninstalled','Not Installed'), + ('installed','Installed'), + ('to upgrade','To be upgraded'), + ('to remove','To be removed'), + ('to install','To be installed'), + ('unknown', 'Unknown'), + ], string='State', readonly=True), + } module_dependency() class module_config_wizard_step(osv.osv): - _name = 'ir.module.module.configuration.step' - _columns={ - 'name':fields.char('Name',size=64,required=True, select=True), - 'note':fields.text('Text'), - 'action_id':fields.many2one('ir.actions.act_window', 'Action', select=True,required=True, ondelete='cascade'), - 'sequence':fields.integer('Sequence'), - 'state':fields.selection([('open', 'Open'),('done', 'Done'),('skip','Skip')], string='State', required=True) - } - _defaults={ - 'state': lambda *a: 'open', - 'sequence': lambda *a: 10, - } - _order="sequence" + _name = 'ir.module.module.configuration.step' + _columns={ + 'name':fields.char('Name',size=64,required=True, select=True), + 'note':fields.text('Text'), + 'action_id':fields.many2one('ir.actions.act_window', 'Action', select=True,required=True, ondelete='cascade'), + 'sequence':fields.integer('Sequence'), + 'state':fields.selection([('open', 'Open'),('done', 'Done'),('skip','Skip')], string='State', required=True) + } + _defaults={ + 'state': lambda *a: 'open', + 'sequence': lambda *a: 10, + } + _order="sequence" module_config_wizard_step() class module_configuration(osv.osv_memory): - _name='ir.module.module.configuration.wizard' - def _get_action_name(self, cr, uid, context={}): - item_obj = self.pool.get('ir.module.module.configuration.step') - item_ids = item_obj.search(cr, uid, [ - ('state', '=', 'open'), - ], limit=1, context=context) - if item_ids and len(item_ids): - item = item_obj.browse(cr, uid, item_ids[0], context=context) - return item.note - else: - return "Your database is now fully configured.\n\nClick 'Continue' and enyoy your OpenERP experience..." - return False - - def _get_action(self, cr, uid, context={}): - item_obj = self.pool.get('ir.module.module.configuration.step') - item_ids = item_obj.search(cr, uid, [ - ('state', '=', 'open'), - ], limit=1, context=context) - if item_ids: - item = item_obj.browse(cr, uid, item_ids[0], context=context) - return item.id - return False - - _columns = { - 'name': fields.text('Next Wizard',readonly=True), - 'item_id':fields.many2one('ir.module.module.configuration.step', 'Next Configuration Wizard',invisible=True, readonly=True), - - } - _defaults={ - 'item_id':_get_action, - 'name':_get_action_name, - - } - def button_skip(self,cr,uid,ids,context=None): - item_obj = self.pool.get('ir.module.module.configuration.step') - item_id=self.read(cr,uid,ids)[0]['item_id'] - if item_id: - item = item_obj.browse(cr, uid, item_id, context=context) - item_obj.write(cr, uid, item.id, { - 'state': 'skip', - }, context=context) - return{ - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', - } - return {'type':'ir.actions.act_window_close'} - - def button_continue(self, cr, uid, ids, context=None): - item_obj = self.pool.get('ir.module.module.configuration.step') - item_id=self.read(cr,uid,ids)[0]['item_id'] - if item_id: - item = item_obj.browse(cr, uid, item_id, context=context) - item_obj.write(cr, uid, item.id, { - 'state': 'done', - }, context=context) - return{ - 'view_type': item.action_id.view_type, - 'view_id':item.action_id.view_id and [item.action_id.view_id.id] or False, - 'res_model': item.action_id.res_model, - 'type': item.action_id.type, - 'target':item.action_id.target, - } - return {'type':'ir.actions.act_window_close' } + _name='ir.module.module.configuration.wizard' + def _get_action_name(self, cr, uid, context={}): + item_obj = self.pool.get('ir.module.module.configuration.step') + item_ids = item_obj.search(cr, uid, [ + ('state', '=', 'open'), + ], limit=1, context=context) + if item_ids and len(item_ids): + item = item_obj.browse(cr, uid, item_ids[0], context=context) + return item.note + else: + return "Your database is now fully configured.\n\nClick 'Continue' and enyoy your OpenERP experience..." + return False + + def _get_action(self, cr, uid, context={}): + item_obj = self.pool.get('ir.module.module.configuration.step') + item_ids = item_obj.search(cr, uid, [ + ('state', '=', 'open'), + ], limit=1, context=context) + if item_ids: + item = item_obj.browse(cr, uid, item_ids[0], context=context) + return item.id + return False + + _columns = { + 'name': fields.text('Next Wizard',readonly=True), + 'item_id':fields.many2one('ir.module.module.configuration.step', 'Next Configuration Wizard',invisible=True, readonly=True), + + } + _defaults={ + 'item_id':_get_action, + 'name':_get_action_name, + + } + def button_skip(self,cr,uid,ids,context=None): + item_obj = self.pool.get('ir.module.module.configuration.step') + item_id=self.read(cr,uid,ids)[0]['item_id'] + if item_id: + item = item_obj.browse(cr, uid, item_id, context=context) + item_obj.write(cr, uid, item.id, { + 'state': 'skip', + }, context=context) + return{ + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', + } + return {'type':'ir.actions.act_window_close'} + + def button_continue(self, cr, uid, ids, context=None): + item_obj = self.pool.get('ir.module.module.configuration.step') + item_id=self.read(cr,uid,ids)[0]['item_id'] + if item_id: + item = item_obj.browse(cr, uid, item_id, context=context) + item_obj.write(cr, uid, item.id, { + 'state': 'done', + }, context=context) + return{ + 'view_type': item.action_id.view_type, + 'view_id':item.action_id.view_id and [item.action_id.view_id.id] or False, + 'res_model': item.action_id.res_model, + 'type': item.action_id.type, + 'target':item.action_id.target, + } + return {'type':'ir.actions.act_window_close' } module_configuration() diff --git a/bin/addons/base/module/report/ir_module_reference_print.py b/bin/addons/base/module/report/ir_module_reference_print.py index f934803e862..57ffc71bdc4 100644 --- a/bin/addons/base/module/report/ir_module_reference_print.py +++ b/bin/addons/base/module/report/ir_module_reference_print.py @@ -31,33 +31,33 @@ import time from report import report_sxw class ir_module_reference_print(report_sxw.rml_parse): - def __init__(self, cr, uid, name, context): - super(ir_module_reference_print, self).__init__(cr, uid, name, context) - self.localcontext.update({ - 'time': time, - 'findobj': self._object_find, - 'objdoc': self._object_doc, - 'findflds': self._fields_find, - }) - def _object_doc(self, obj): - modobj = self.pool.get(obj) - return modobj.__doc__ + def __init__(self, cr, uid, name, context): + super(ir_module_reference_print, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'findobj': self._object_find, + 'objdoc': self._object_doc, + 'findflds': self._fields_find, + }) + def _object_doc(self, obj): + modobj = self.pool.get(obj) + return modobj.__doc__ - def _object_find(self, module): - modobj = self.pool.get('ir.model') - if module=='base': - ids = modobj.search(self.cr, self.uid, [('model','=like','res%')]) - ids += modobj.search(self.cr, self.uid, [('model','=like','ir%')]) - else: - ids = modobj.search(self.cr, self.uid, [('model','=like',module+'%')]) - return modobj.browse(self.cr, self.uid, ids) + def _object_find(self, module): + modobj = self.pool.get('ir.model') + if module=='base': + ids = modobj.search(self.cr, self.uid, [('model','=like','res%')]) + ids += modobj.search(self.cr, self.uid, [('model','=like','ir%')]) + else: + ids = modobj.search(self.cr, self.uid, [('model','=like',module+'%')]) + return modobj.browse(self.cr, self.uid, ids) - def _fields_find(self, obj): - modobj = self.pool.get(obj) - res = modobj.fields_get(self.cr, self.uid).items() - return res + def _fields_find(self, obj): + modobj = self.pool.get(obj) + res = modobj.fields_get(self.cr, self.uid).items() + return res report_sxw.report_sxw('report.ir.module.reference', 'ir.module.module', - 'addons/base/module/report/ir_module_reference.rml', - parser=ir_module_reference_print, header=False) + 'addons/base/module/report/ir_module_reference.rml', + parser=ir_module_reference_print, header=False) diff --git a/bin/addons/base/module/wizard/add_new.py b/bin/addons/base/module/wizard/add_new.py index 0b5ec6a2f2f..39465dff970 100644 --- a/bin/addons/base/module/wizard/add_new.py +++ b/bin/addons/base/module/wizard/add_new.py @@ -49,48 +49,48 @@ _info_arch = '''<?xml version="1.0"?> _info_fields = {} class wizard_install_module(wizard.interface): - def watch_dir(self, cr, uid, data, context): - mod_obj = pooler.get_pool(cr.dbname).get('ir.module.module') - all_mods = mod_obj.read(cr, uid, mod_obj.search(cr, uid, []), ['name', 'state']) - known_modules = [x['name'] for x in all_mods] - ls_ad = glob.glob(os.path.join(tools.config['addons_path'], '*', '__terp__.py')) - modules = [module_name_re.match(name).group(1) for name in ls_ad] - for fname in os.listdir(tools.config['addons_path']): - if zipfile.is_zipfile(fname): - modules.append( fname.split('.')[0]) - for module in modules: - if module in known_modules: - continue - terp = mod_obj.get_module_info(module) - if not terp.get('installable', True): - continue - imp.load_module(module, *imp.find_module(module)) - mod_id = mod_obj.create(cr, uid, { - 'name': module, - 'state': 'uninstalled', - 'description': terp.get('description', ''), - 'shortdesc': terp.get('name', ''), - 'author': terp.get('author', 'Unknown')}) - dependencies = terp.get('depends', []) - for d in dependencies: - cr.execute('insert into ir_module_module_dependency (module_id,name) values (%s, %s)', (mod_id, d)) - for module in known_modules: - terp = mod_obj.get_module_info(module) - if terp.get('installable', True): - for mod in all_mods: - if mod['name'] == module and mod['state'] == 'uninstallable': - mod_obj.write(cr, uid, [mod['id']], {'state': 'uninstalled'}) - return {} + def watch_dir(self, cr, uid, data, context): + mod_obj = pooler.get_pool(cr.dbname).get('ir.module.module') + all_mods = mod_obj.read(cr, uid, mod_obj.search(cr, uid, []), ['name', 'state']) + known_modules = [x['name'] for x in all_mods] + ls_ad = glob.glob(os.path.join(tools.config['addons_path'], '*', '__terp__.py')) + modules = [module_name_re.match(name).group(1) for name in ls_ad] + for fname in os.listdir(tools.config['addons_path']): + if zipfile.is_zipfile(fname): + modules.append( fname.split('.')[0]) + for module in modules: + if module in known_modules: + continue + terp = mod_obj.get_module_info(module) + if not terp.get('installable', True): + continue + imp.load_module(module, *imp.find_module(module)) + mod_id = mod_obj.create(cr, uid, { + 'name': module, + 'state': 'uninstalled', + 'description': terp.get('description', ''), + 'shortdesc': terp.get('name', ''), + 'author': terp.get('author', 'Unknown')}) + dependencies = terp.get('depends', []) + for d in dependencies: + cr.execute('insert into ir_module_module_dependency (module_id,name) values (%s, %s)', (mod_id, d)) + for module in known_modules: + terp = mod_obj.get_module_info(module) + if terp.get('installable', True): + for mod in all_mods: + if mod['name'] == module and mod['state'] == 'uninstallable': + mod_obj.write(cr, uid, [mod['id']], {'state': 'uninstalled'}) + return {} - states = { - 'init': { - 'actions': [], - 'result': {'type':'form', 'arch': _info_arch, 'fields': _info_fields, 'state':[('end','Cancel','gtk-cancel'),('addmod','Check new modules','gtk-ok')]} - }, - 'addmod': { - 'actions': [watch_dir], - 'result': {'type':'state', 'state':'end'} - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type':'form', 'arch': _info_arch, 'fields': _info_fields, 'state':[('end','Cancel','gtk-cancel'),('addmod','Check new modules','gtk-ok')]} + }, + 'addmod': { + 'actions': [watch_dir], + 'result': {'type':'state', 'state':'end'} + }, + } wizard_install_module('module.module.scan') diff --git a/bin/addons/base/module/wizard/wizard_export_lang.py b/bin/addons/base/module/wizard/wizard_export_lang.py index 3f7fa8f99df..3a4a4e82ae9 100644 --- a/bin/addons/base/module/wizard/wizard_export_lang.py +++ b/bin/addons/base/module/wizard/wizard_export_lang.py @@ -40,114 +40,114 @@ from osv import fields,osv ''' view_form_init="""<?xml version="1.0"?> <form string="Export language"> - <image name="gtk-dialog-info" colspan="2"/> - <group colspan="2" col="4"> - <separator string="Export translation file" colspan="4"/> - <label align="0.0" string="Choose a language to export:" colspan="4"/> - <field name="lang" colspan="4"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group colspan="2" col="4"> + <separator string="Export translation file" colspan="4"/> + <label align="0.0" string="Choose a language to export:" colspan="4"/> + <field name="lang" colspan="4"/> + </group> </form>""" view_form_finish="""<?xml version="1.0"?> <form string="Export language"> - <image name="gtk-dialog-info" colspan="2"/> - <group colspan="2" col="4"> - <separator string="Export done" colspan="4"/> - <field name="data" readonly="1" colspan="3"/> - <label align="0.0" string="Save this document to a .CSV file and open it with\n your favourite spreadsheet software. The file\n encoding is UTF-8. You have to translate the latest\n column before reimporting it." colspan="4"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group colspan="2" col="4"> + <separator string="Export done" colspan="4"/> + <field name="data" readonly="1" colspan="3"/> + <label align="0.0" string="Save this document to a .CSV file and open it with\n your favourite spreadsheet software. The file\n encoding is UTF-8. You have to translate the latest\n column before reimporting it." colspan="4"/> + </group> </form>""" class wizard_export_lang(wizard.interface): - def _get_language(self, cr, uid, context): - lang_obj=pooler.get_pool(cr.dbname).get('res.lang') - ids=lang_obj.search(cr, uid, [('active', '=', True),]) - langs=lang_obj.browse(cr, uid, ids) - return [(lang.code, lang.translatable and lang.name or _('New language')) for lang in langs] + def _get_language(self, cr, uid, context): + lang_obj=pooler.get_pool(cr.dbname).get('res.lang') + ids=lang_obj.search(cr, uid, [('active', '=', True),]) + langs=lang_obj.browse(cr, uid, ids) + return [(lang.code, lang.translatable and lang.name or _('New language')) for lang in langs] - def _get_file(self, cr, uid, data, context): - file=tools.trans_generate(data['form']['lang'], 'all', dbname=cr.dbname) - buf=StringIO.StringIO() - writer=csv.writer(buf, 'UNIX') - for row in file: - writer.writerow(row) - del file - out=base64.encodestring(buf.getvalue()) - buf.close() - return {'data': out} + def _get_file(self, cr, uid, data, context): + file=tools.trans_generate(data['form']['lang'], 'all', dbname=cr.dbname) + buf=StringIO.StringIO() + writer=csv.writer(buf, 'UNIX') + for row in file: + writer.writerow(row) + del file + out=base64.encodestring(buf.getvalue()) + buf.close() + return {'data': out} - fields_form={ - 'lang': {'string':'Language', 'type':'selection', 'selection':_get_language,}, - } - fields_form_finish={ - 'data': {'string':'File', 'type':'binary', 'readonly': True,}, - } - states={ - 'init':{ - 'actions': [], - 'result': {'type': 'form', 'arch': view_form_init, 'fields': fields_form, - 'state': [ - ('end', 'Cancel', 'gtk-cancel'), - ('finish', 'Ok', 'gtk-ok', True) - ] - } - }, - 'finish':{ - 'actions': [_get_file], - 'result': {'type': 'form', 'arch': view_form_finish, - 'fields': fields_form_finish, - 'state': [ - ('end', 'Close', 'gtk-cancel', True) - ] - } - }, - } + fields_form={ + 'lang': {'string':'Language', 'type':'selection', 'selection':_get_language,}, + } + fields_form_finish={ + 'data': {'string':'File', 'type':'binary', 'readonly': True,}, + } + states={ + 'init':{ + 'actions': [], + 'result': {'type': 'form', 'arch': view_form_init, 'fields': fields_form, + 'state': [ + ('end', 'Cancel', 'gtk-cancel'), + ('finish', 'Ok', 'gtk-ok', True) + ] + } + }, + 'finish':{ + 'actions': [_get_file], + 'result': {'type': 'form', 'arch': view_form_finish, + 'fields': fields_form_finish, + 'state': [ + ('end', 'Close', 'gtk-cancel', True) + ] + } + }, + } wizard_export_lang('module.lang.export') ''' class wizard_export_lang(osv.osv_memory): - def _get_languages(self, cr, uid, context): - lang_obj=pooler.get_pool(cr.dbname).get('res.lang') - ids=lang_obj.search(cr, uid, [('active', '=', True),]) - langs=lang_obj.browse(cr, uid, ids) - return [(lang.code, lang.translatable and lang.name or _('New language')) for lang in langs] - + def _get_languages(self, cr, uid, context): + lang_obj=pooler.get_pool(cr.dbname).get('res.lang') + ids=lang_obj.search(cr, uid, [('active', '=', True),]) + langs=lang_obj.browse(cr, uid, ids) + return [(lang.code, lang.translatable and lang.name or _('New language')) for lang in langs] + - def act_cancel(self, cr, uid, ids, context=None): - #self.unlink(cr, uid, ids, context) - return {'type':'ir.actions.act_window_close' } + def act_cancel(self, cr, uid, ids, context=None): + #self.unlink(cr, uid, ids, context) + return {'type':'ir.actions.act_window_close' } - def act_destroy(self, *args): - return {'type':'ir.actions.act_window_close' } + def act_destroy(self, *args): + return {'type':'ir.actions.act_window_close' } - def act_getfile(self, cr, uid, ids, context=None): - this = self.browse(cr, uid, ids)[0] - mods = map(lambda m: m.name, this.modules) - mods.sort() - buf=StringIO.StringIO() - - tools.trans_export(this.lang, mods, buf, this.format, dbname=cr.dbname) + def act_getfile(self, cr, uid, ids, context=None): + this = self.browse(cr, uid, ids)[0] + mods = map(lambda m: m.name, this.modules) + mods.sort() + buf=StringIO.StringIO() + + tools.trans_export(this.lang, mods, buf, this.format, dbname=cr.dbname) - if this.format == 'csv': - this.advice = _("Save this document to a .CSV file and open it with your favourite spreadsheet software. The file encoding is UTF-8. You have to translate the latest column before reimporting it.") - elif this.format == 'po': - this.advice = _("Save this document to a .po file and edit it with a specific software or a text editor. The file encoding is UTF-8.") - - out=base64.encodestring(buf.getvalue()) - buf.close() - return self.write(cr, uid, ids, {'state':'get', 'data':out, 'advice':this.advice}, context=context) + if this.format == 'csv': + this.advice = _("Save this document to a .CSV file and open it with your favourite spreadsheet software. The file encoding is UTF-8. You have to translate the latest column before reimporting it.") + elif this.format == 'po': + this.advice = _("Save this document to a .po file and edit it with a specific software or a text editor. The file encoding is UTF-8.") + + out=base64.encodestring(buf.getvalue()) + buf.close() + return self.write(cr, uid, ids, {'state':'get', 'data':out, 'advice':this.advice}, context=context) - _name = "wizard.module.lang.export" - _columns = { - 'lang': fields.selection(_get_languages, 'Language',required=True), - 'format': fields.selection( ( ('csv','CSV File'), ('po','PO File') ), 'File Format', required=True), - 'modules': fields.many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id', 'Modules', domain=[('state','=','installed')]), - 'data': fields.binary('File', readonly=True), - 'advice': fields.text('', readonly=True), - 'state': fields.selection( ( ('choose','choose'), # choose language - ('get','get'), # get the file - ) ), - } - _defaults = { 'state': lambda *a: 'choose', } + _name = "wizard.module.lang.export" + _columns = { + 'lang': fields.selection(_get_languages, 'Language',required=True), + 'format': fields.selection( ( ('csv','CSV File'), ('po','PO File') ), 'File Format', required=True), + 'modules': fields.many2many('ir.module.module', 'rel_modules_langexport', 'wiz_id', 'module_id', 'Modules', domain=[('state','=','installed')]), + 'data': fields.binary('File', readonly=True), + 'advice': fields.text('', readonly=True), + 'state': fields.selection( ( ('choose','choose'), # choose language + ('get','get'), # get the file + ) ), + } + _defaults = { 'state': lambda *a: 'choose', } wizard_export_lang() diff --git a/bin/addons/base/module/wizard/wizard_import_lang.py b/bin/addons/base/module/wizard/wizard_import_lang.py index 9134e64433a..df0bee1310c 100644 --- a/bin/addons/base/module/wizard/wizard_import_lang.py +++ b/bin/addons/base/module/wizard/wizard_import_lang.py @@ -36,53 +36,53 @@ from tempfile import TemporaryFile view_form="""<?xml version="1.0"?> <form string="Import language"> - <image name="gtk-dialog-info" colspan="2"/> - <group colspan="2" col="4"> - <separator string="Import new language" colspan="4"/> - <field name="name"/> - <field name="code"/> - <field name="data" colspan="3"/> - <label string="You have to import a .CSV file wich is encoded in UTF-8.\nPlease check that the first line of your file is:" colspan="4" align="0.0"/> - <label string="type,name,res_id,src,value" colspan="4"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group colspan="2" col="4"> + <separator string="Import new language" colspan="4"/> + <field name="name"/> + <field name="code"/> + <field name="data" colspan="3"/> + <label string="You have to import a .CSV file wich is encoded in UTF-8.\nPlease check that the first line of your file is:" colspan="4" align="0.0"/> + <label string="type,name,res_id,src,value" colspan="4"/> + </group> </form>""" fields_form={ - 'name':{'string':'Language name', 'type':'char', 'size':64, 'required':True}, - 'code':{'string':'Code (eg:en__US)', 'type':'char', 'size':5, 'required':True}, - 'data':{'string':'File', 'type':'binary', 'required':True}, + 'name':{'string':'Language name', 'type':'char', 'size':64, 'required':True}, + 'code':{'string':'Code (eg:en__US)', 'type':'char', 'size':5, 'required':True}, + 'data':{'string':'File', 'type':'binary', 'required':True}, } class wizard_import_lang(wizard.interface): - def _import_lang(self, cr, uid, data, context): - form=data['form'] - fileobj = TemporaryFile('w+') - fileobj.write( base64.decodestring(form['data']) ) + def _import_lang(self, cr, uid, data, context): + form=data['form'] + fileobj = TemporaryFile('w+') + fileobj.write( base64.decodestring(form['data']) ) - # now we determine the file format - fileobj.seek(0) - first_line = fileobj.readline().strip() - fileformat = first_line.endswith("type,name,res_id,src,value") and 'csv' or 'po' - fileobj.seek(0) + # now we determine the file format + fileobj.seek(0) + first_line = fileobj.readline().strip() + fileformat = first_line.endswith("type,name,res_id,src,value") and 'csv' or 'po' + fileobj.seek(0) - tools.trans_load_data(cr.dbname, fileobj, fileformat, form['code'], lang_name=form['name']) - fileobj.close() - return {} + tools.trans_load_data(cr.dbname, fileobj, fileformat, form['code'], lang_name=form['name']) + fileobj.close() + return {} - states={ - 'init':{ - 'actions': [], - 'result': {'type': 'form', 'arch': view_form, 'fields': fields_form, - 'state':[ - ('end', 'Cancel', 'gtk-cancel'), - ('finish', 'Ok', 'gtk-ok', True) - ] - } - }, - 'finish':{ - 'actions':[], - 'result':{'type':'action', 'action':_import_lang, 'state':'end'} - }, - } + states={ + 'init':{ + 'actions': [], + 'result': {'type': 'form', 'arch': view_form, 'fields': fields_form, + 'state':[ + ('end', 'Cancel', 'gtk-cancel'), + ('finish', 'Ok', 'gtk-ok', True) + ] + } + }, + 'finish':{ + 'actions':[], + 'result':{'type':'action', 'action':_import_lang, 'state':'end'} + }, + } wizard_import_lang('module.lang.import') diff --git a/bin/addons/base/module/wizard/wizard_module_import.py b/bin/addons/base/module/wizard/wizard_module_import.py index d59c635cd30..394389dd68f 100644 --- a/bin/addons/base/module/wizard/wizard_module_import.py +++ b/bin/addons/base/module/wizard/wizard_module_import.py @@ -40,82 +40,82 @@ import base64 finish_form ='''<?xml version="1.0"?> <form string="Module import"> - <label string="Module successfully imported !" colspan="4"/> + <label string="Module successfully imported !" colspan="4"/> </form> ''' ask_form ='''<?xml version="1.0"?> <form string="Module import"> - <separator string="Module import" colspan="4"/> - <label string="Please give your module .ZIP file to import." colspan="4"/> - <field name="module_file"/> + <separator string="Module import" colspan="4"/> + <label string="Please give your module .ZIP file to import." colspan="4"/> + <field name="module_file"/> </form> ''' ask_fields = { - 'module_file': {'string': 'Module .ZIP file', 'type': 'binary', 'required': True}, + 'module_file': {'string': 'Module .ZIP file', 'type': 'binary', 'required': True}, } class move_module_wizard(wizard.interface): - def importzip(self, cr, uid, data, context): - module_obj=pooler.get_pool(cr.dbname).get('ir.module.module') - module_data = data['form']['module_file'] + def importzip(self, cr, uid, data, context): + module_obj=pooler.get_pool(cr.dbname).get('ir.module.module') + module_data = data['form']['module_file'] - val =base64.decodestring(module_data) - fp = StringIO.StringIO(val) - fdata = zipfile.ZipFile(fp, 'r') - fname = fdata.namelist()[0] - module_name = os.path.split(fname)[0] + val =base64.decodestring(module_data) + fp = StringIO.StringIO(val) + fdata = zipfile.ZipFile(fp, 'r') + fname = fdata.namelist()[0] + module_name = os.path.split(fname)[0] - ad = tools.config['addons_path'] + ad = tools.config['addons_path'] - fname = os.path.join(ad,module_name+'.zip') - try: - fp = file(fname, 'wb') - fp.write(val) - fp.close() - except IOError, e: - raise wizard.except_wizard(_('Error !'), _('Can not create the module file: %s !') % (fname,) ) + fname = os.path.join(ad,module_name+'.zip') + try: + fp = file(fname, 'wb') + fp.write(val) + fp.close() + except IOError, e: + raise wizard.except_wizard(_('Error !'), _('Can not create the module file: %s !') % (fname,) ) - pooler.get_pool(cr.dbname).get('ir.module.module').update_list(cr, uid) - return {'module_name': module_name} + pooler.get_pool(cr.dbname).get('ir.module.module').update_list(cr, uid) + return {'module_name': module_name} - def _action_module_open(self, cr, uid, data, context): - return { - 'domain': str([('name', '=', data['form']['module_name'])]), - 'name': 'Module List', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'ir.module.module', - 'view_id': False, - 'type': 'ir.actions.act_window' - } + def _action_module_open(self, cr, uid, data, context): + return { + 'domain': str([('name', '=', data['form']['module_name'])]), + 'name': 'Module List', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'ir.module.module', + 'view_id': False, + 'type': 'ir.actions.act_window' + } - states = { - 'init': { - 'actions': [], - 'result': { - 'type': 'form', - 'arch': ask_form, - 'fields': ask_fields, - 'state': [ - ('end', 'Cancel', 'gtk-cancel'), - ('import', 'Import module', 'gtk-ok', True) - ] - } - }, - 'import': { - 'actions': [importzip], - 'result': { - 'type':'form', - 'arch':finish_form, - 'fields':{}, - 'state':[('open_window','Close')] - } - }, - 'open_window': { - 'actions': [], - 'result': {'type': 'action', 'action': _action_module_open, 'state':'end'} - }, - } + states = { + 'init': { + 'actions': [], + 'result': { + 'type': 'form', + 'arch': ask_form, + 'fields': ask_fields, + 'state': [ + ('end', 'Cancel', 'gtk-cancel'), + ('import', 'Import module', 'gtk-ok', True) + ] + } + }, + 'import': { + 'actions': [importzip], + 'result': { + 'type':'form', + 'arch':finish_form, + 'fields':{}, + 'state':[('open_window','Close')] + } + }, + 'open_window': { + 'actions': [], + 'result': {'type': 'action', 'action': _action_module_open, 'state':'end'} + }, + } move_module_wizard('base.module.import') diff --git a/bin/addons/base/module/wizard/wizard_module_lang_install.py b/bin/addons/base/module/wizard/wizard_module_lang_install.py index a53da5b0463..95064b0d043 100644 --- a/bin/addons/base/module/wizard/wizard_module_lang_install.py +++ b/bin/addons/base/module/wizard/wizard_module_lang_install.py @@ -33,59 +33,59 @@ import tools view_form_end = """<?xml version="1.0"?> <form string="Language file loaded."> - <image name="gtk-dialog-info" colspan="2"/> - <group colspan="2" col="4"> - <separator string="Installation done" colspan="4"/> - <label align="0.0" string="The selected language has been successfully installed.\nYou must change the preferences of the user and open a new menu to view changes." colspan="4"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group colspan="2" col="4"> + <separator string="Installation done" colspan="4"/> + <label align="0.0" string="The selected language has been successfully installed.\nYou must change the preferences of the user and open a new menu to view changes." colspan="4"/> + </group> </form>""" view_form = """<?xml version="1.0"?> <form string="System Upgrade"> - <image name="gtk-dialog-info" colspan="2"/> - <group colspan="2" col="4"> - <separator string="System Upgrade" colspan="4"/> - <label align="0.0" string="Choose a language to install:" colspan="4"/> - <field name="lang" colspan="4"/> - <label align="0.0" string="Note that this operation may take a few minutes." colspan="4"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group colspan="2" col="4"> + <separator string="System Upgrade" colspan="4"/> + <label align="0.0" string="Choose a language to install:" colspan="4"/> + <field name="lang" colspan="4"/> + <label align="0.0" string="Note that this operation may take a few minutes." colspan="4"/> + </group> </form>""" class wizard_lang_install(wizard.interface): - def _lang_install(self, cr, uid, data, context): - lang = data['form']['lang'] - if lang and lang != 'en_US': - filename = tools.config["root_path"] + "/i18n/" + lang + ".csv" - tools.trans_load(cr.dbname, filename, lang) - return {} + def _lang_install(self, cr, uid, data, context): + lang = data['form']['lang'] + if lang and lang != 'en_US': + filename = tools.config["root_path"] + "/i18n/" + lang + ".csv" + tools.trans_load(cr.dbname, filename, lang) + return {} - def _get_language(sel, cr, uid, context): - return tools.scan_languages() + def _get_language(sel, cr, uid, context): + return tools.scan_languages() - fields_form = { - 'lang': {'string':'Language', 'type':'selection', 'selection':_get_language, - }, - } + fields_form = { + 'lang': {'string':'Language', 'type':'selection', 'selection':_get_language, + }, + } - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch': view_form, 'fields': fields_form, - 'state': [ - ('end', 'Cancel', 'gtk-cancel'), - ('start', 'Start installation', 'gtk-ok', True) - ] - } - }, - 'start': { - 'actions': [_lang_install], - 'result': {'type': 'form', 'arch': view_form_end, 'fields': {}, - 'state': [ - ('end', 'Ok', 'gtk-ok', True) - ] - } - }, - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch': view_form, 'fields': fields_form, + 'state': [ + ('end', 'Cancel', 'gtk-cancel'), + ('start', 'Start installation', 'gtk-ok', True) + ] + } + }, + 'start': { + 'actions': [_lang_install], + 'result': {'type': 'form', 'arch': view_form_end, 'fields': {}, + 'state': [ + ('end', 'Ok', 'gtk-ok', True) + ] + } + }, + } wizard_lang_install('module.lang.install') diff --git a/bin/addons/base/module/wizard/wizard_module_upgrade.py b/bin/addons/base/module/wizard/wizard_module_upgrade.py index 752c40a5358..482f2bb048e 100644 --- a/bin/addons/base/module/wizard/wizard_module_upgrade.py +++ b/bin/addons/base/module/wizard/wizard_module_upgrade.py @@ -35,121 +35,121 @@ import tools view_form_end = """<?xml version="1.0"?> <form string="System upgrade done"> - <separator string="System upgrade done"/> - <label align="0.0" string="The modules have been upgraded / installed !" colspan="4"/> - <label align="0.0" string="You may have to reinstall some language pack." colspan="4"/> - <label align="0.0" string="We suggest you to reload the menu tab (Ctrl+t Ctrl+r)." colspan="4"/> + <separator string="System upgrade done"/> + <label align="0.0" string="The modules have been upgraded / installed !" colspan="4"/> + <label align="0.0" string="You may have to reinstall some language pack." colspan="4"/> + <label align="0.0" string="We suggest you to reload the menu tab (Ctrl+t Ctrl+r)." colspan="4"/> </form>""" view_form = """<?xml version="1.0"?> <form string="System Upgrade"> - <image name="gtk-dialog-info" colspan="2"/> - <group colspan="2" col="4"> - <label align="0.0" string="Your system will be upgraded." colspan="4"/> - <label align="0.0" string="Note that this operation my take a few minutes." colspan="4"/> - <separator string="Modules to update"/> - <field name="module_info" nolabel="1" colspan="4"/> - <separator string="Modules to download"/> - <field name="module_download" nolabel="1" colspan="4"/> - </group> + <image name="gtk-dialog-info" colspan="2"/> + <group colspan="2" col="4"> + <label align="0.0" string="Your system will be upgraded." colspan="4"/> + <label align="0.0" string="Note that this operation my take a few minutes." colspan="4"/> + <separator string="Modules to update"/> + <field name="module_info" nolabel="1" colspan="4"/> + <separator string="Modules to download"/> + <field name="module_download" nolabel="1" colspan="4"/> + </group> </form>""" view_field = { - "module_info": {'type': 'text', 'string': 'Modules to update', - 'readonly': True}, - "module_download": {'type': 'text', 'string': 'Modules to download', - 'readonly': True}, + "module_info": {'type': 'text', 'string': 'Modules to update', + 'readonly': True}, + "module_download": {'type': 'text', 'string': 'Modules to download', + 'readonly': True}, } class wizard_info_get(wizard.interface): - def _get_install(self, cr, uid, data, context): - pool=pooler.get_pool(cr.dbname) - mod_obj = pool.get('ir.module.module') - ids = mod_obj.search(cr, uid, [ - ('state', 'in', ['to upgrade', 'to remove', 'to install'])]) - res = mod_obj.read(cr, uid, ids, ['name','state'], context) - url = mod_obj.download(cr, uid, ids, download=False, context=context) - return {'module_info': '\n'.join(map(lambda x: x['name']+' : '+x['state'], res)), - 'module_download': '\n'.join(url)} + def _get_install(self, cr, uid, data, context): + pool=pooler.get_pool(cr.dbname) + mod_obj = pool.get('ir.module.module') + ids = mod_obj.search(cr, uid, [ + ('state', 'in', ['to upgrade', 'to remove', 'to install'])]) + res = mod_obj.read(cr, uid, ids, ['name','state'], context) + url = mod_obj.download(cr, uid, ids, download=False, context=context) + return {'module_info': '\n'.join(map(lambda x: x['name']+' : '+x['state'], res)), + 'module_download': '\n'.join(url)} - def _check_upgrade_module(self,cr,uid,data,context): - db, pool = pooler.get_db_and_pool(cr.dbname) - cr = db.cursor() - mod_obj = pool.get('ir.module.module') - ids = mod_obj.search(cr, uid, [ - ('state', 'in', ['to upgrade', 'to remove', 'to install'])]) - if ids and len(ids): - return 'next' - else: - return 'end' + def _check_upgrade_module(self,cr,uid,data,context): + db, pool = pooler.get_db_and_pool(cr.dbname) + cr = db.cursor() + mod_obj = pool.get('ir.module.module') + ids = mod_obj.search(cr, uid, [ + ('state', 'in', ['to upgrade', 'to remove', 'to install'])]) + if ids and len(ids): + return 'next' + else: + return 'end' - def _upgrade_module(self, cr, uid, data, context): - db, pool = pooler.get_db_and_pool(cr.dbname) - cr = db.cursor() - mod_obj = pool.get('ir.module.module') - ids = mod_obj.search(cr, uid, [ - ('state', 'in', ['to upgrade', 'to remove', 'to install'])]) - mod_obj.download(cr, uid, ids, context=context) - cr.commit() - db, pool = pooler.restart_pool(cr.dbname, update_module=True) + def _upgrade_module(self, cr, uid, data, context): + db, pool = pooler.get_db_and_pool(cr.dbname) + cr = db.cursor() + mod_obj = pool.get('ir.module.module') + ids = mod_obj.search(cr, uid, [ + ('state', 'in', ['to upgrade', 'to remove', 'to install'])]) + mod_obj.download(cr, uid, ids, context=context) + cr.commit() + db, pool = pooler.restart_pool(cr.dbname, update_module=True) - lang_obj=pool.get('res.lang') - lang_ids=lang_obj.search(cr, uid, []) - langs=lang_obj.browse(cr, uid, lang_ids) - for lang in langs: - if lang.code and lang.code != 'en_US': - filename=os.path.join(tools.config["root_path"], "i18n", lang.code + ".csv") - tools.trans_load(cr.dbname, filename, lang.code) - return {} + lang_obj=pool.get('res.lang') + lang_ids=lang_obj.search(cr, uid, []) + langs=lang_obj.browse(cr, uid, lang_ids) + for lang in langs: + if lang.code and lang.code != 'en_US': + filename=os.path.join(tools.config["root_path"], "i18n", lang.code + ".csv") + tools.trans_load(cr.dbname, filename, lang.code) + return {} - def _config(self, cr, uid, data, context=None): - return { + def _config(self, cr, uid, data, context=None): + return { 'view_type': 'form', "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', } - states = { - 'init': { - 'actions': [], - 'result' : {'type': 'choice', 'next_state': _check_upgrade_module } - }, - 'next': { - 'actions': [_get_install], - 'result': {'type':'form', 'arch':view_form, 'fields': view_field, - 'state':[ - ('end', 'Cancel', 'gtk-cancel'), - ('start', 'Start Upgrade', 'gtk-ok', True) - ] - } - }, - 'start': { - 'actions': [_upgrade_module], - 'result': {'type':'form', 'arch':view_form_end, 'fields': {}, - 'state':[ - ('end', 'Close', 'gtk-close', True), - ('config', 'Start configuration', 'gtk-ok', True) - ] - } - }, - 'end': { - 'actions': [], - 'result': {'type':'form', 'arch':view_form_end, 'fields': {}, - 'state':[ - ('end', 'Close', 'gtk-close', True), - ('config', 'Start configuration', 'gtk-ok', True) - ] - } - }, - 'config':{ + states = { + 'init': { + 'actions': [], + 'result' : {'type': 'choice', 'next_state': _check_upgrade_module } + }, + 'next': { + 'actions': [_get_install], + 'result': {'type':'form', 'arch':view_form, 'fields': view_field, + 'state':[ + ('end', 'Cancel', 'gtk-cancel'), + ('start', 'Start Upgrade', 'gtk-ok', True) + ] + } + }, + 'start': { + 'actions': [_upgrade_module], + 'result': {'type':'form', 'arch':view_form_end, 'fields': {}, + 'state':[ + ('end', 'Close', 'gtk-close', True), + ('config', 'Start configuration', 'gtk-ok', True) + ] + } + }, + 'end': { + 'actions': [], + 'result': {'type':'form', 'arch':view_form_end, 'fields': {}, + 'state':[ + ('end', 'Close', 'gtk-close', True), + ('config', 'Start configuration', 'gtk-ok', True) + ] + } + }, + 'config':{ 'result': { 'type': 'action', 'action': _config, 'state': 'end', }, - } - } + } + } wizard_info_get('module.upgrade') diff --git a/bin/addons/base/module/wizard/wizard_update_module.py b/bin/addons/base/module/wizard/wizard_update_module.py index 94e12d28bad..924fec3eb46 100644 --- a/bin/addons/base/module/wizard/wizard_update_module.py +++ b/bin/addons/base/module/wizard/wizard_update_module.py @@ -34,70 +34,70 @@ import pooler class wizard_update_module(wizard.interface): - arch = '''<?xml version="1.0"?> - <form string="Scan for new modules"> - <label string="This function will check for new modules in the 'addons' path and on module repositories:" colspan="4" align="0.0"/> - <field name="repositories" colspan="4" nolabel="1"/> - </form>''' - fields = { - 'repositories': {'type': 'text', 'string': 'Repositories', 'readonly': True}, - } + arch = '''<?xml version="1.0"?> + <form string="Scan for new modules"> + <label string="This function will check for new modules in the 'addons' path and on module repositories:" colspan="4" align="0.0"/> + <field name="repositories" colspan="4" nolabel="1"/> + </form>''' + fields = { + 'repositories': {'type': 'text', 'string': 'Repositories', 'readonly': True}, + } - arch_module = '''<?xml version="1.0"?> - <form string="New modules"> - <field name="update" colspan="4"/> - <field name="add" colspan="4"/> - </form>''' + arch_module = '''<?xml version="1.0"?> + <form string="New modules"> + <field name="update" colspan="4"/> + <field name="add" colspan="4"/> + </form>''' - fields_module = { - 'update': {'type': 'integer', 'string': 'Number of modules updated', 'readonly': True}, - 'add': {'type': 'integer', 'string': 'Number of modules added', 'readonly': True}, - } + fields_module = { + 'update': {'type': 'integer', 'string': 'Number of modules updated', 'readonly': True}, + 'add': {'type': 'integer', 'string': 'Number of modules added', 'readonly': True}, + } - def _update_module(self, cr, uid, data, context): - update, add = pooler.get_pool(cr.dbname).get('ir.module.module').update_list(cr, uid) - return {'update': update, 'add': add} + def _update_module(self, cr, uid, data, context): + update, add = pooler.get_pool(cr.dbname).get('ir.module.module').update_list(cr, uid) + return {'update': update, 'add': add} - def _action_module_open(self, cr, uid, data, context): - return { - 'domain': str([]), - 'name': 'Module List', - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'ir.module.module', - 'view_id': False, - 'type': 'ir.actions.act_window' - } + def _action_module_open(self, cr, uid, data, context): + return { + 'domain': str([]), + 'name': 'Module List', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'ir.module.module', + 'view_id': False, + 'type': 'ir.actions.act_window' + } - def _get_repositories(self, cr, uid, data, context): - pool = pooler.get_pool(cr.dbname) - repository_obj = pool.get('ir.module.repository') - ids = repository_obj.search(cr, uid, []) - res = repository_obj.read(cr, uid, ids, ['name', 'url'], context) - return {'repositories': '\n'.join(map(lambda x: x['name']+': '+x['url'], res))} + def _get_repositories(self, cr, uid, data, context): + pool = pooler.get_pool(cr.dbname) + repository_obj = pool.get('ir.module.repository') + ids = repository_obj.search(cr, uid, []) + res = repository_obj.read(cr, uid, ids, ['name', 'url'], context) + return {'repositories': '\n'.join(map(lambda x: x['name']+': '+x['url'], res))} - states = { - 'init': { - 'actions': [_get_repositories], - 'result': {'type': 'form', 'arch': arch, 'fields': fields, - 'state': [ - ('end', 'Cancel', 'gtk-cancel'), - ('update', 'Check new modules', 'gtk-ok', True) - ] - } - }, - 'update': { - 'actions': [_update_module], - 'result': {'type': 'form', 'arch': arch_module, 'fields': fields_module, - 'state': [ - ('open_window', 'Ok', 'gtk-ok', True) - ] - } - }, - 'open_window': { - 'actions': [], - 'result': {'type': 'action', 'action': _action_module_open, 'state':'end'} - } - } + states = { + 'init': { + 'actions': [_get_repositories], + 'result': {'type': 'form', 'arch': arch, 'fields': fields, + 'state': [ + ('end', 'Cancel', 'gtk-cancel'), + ('update', 'Check new modules', 'gtk-ok', True) + ] + } + }, + 'update': { + 'actions': [_update_module], + 'result': {'type': 'form', 'arch': arch_module, 'fields': fields_module, + 'state': [ + ('open_window', 'Ok', 'gtk-ok', True) + ] + } + }, + 'open_window': { + 'actions': [], + 'result': {'type': 'action', 'action': _action_module_open, 'state':'end'} + } + } wizard_update_module('module.module.update') diff --git a/bin/addons/base/res/bank.py b/bin/addons/base/res/bank.py index 0917abc398a..dcae2f90b57 100644 --- a/bin/addons/base/res/bank.py +++ b/bin/addons/base/res/bank.py @@ -32,27 +32,27 @@ from osv import fields, osv class Bank(osv.osv): - _description='Bank' - _name = 'res.bank' - _columns = { - 'name': fields.char('Name', size=128, required=True), - 'code': fields.char('Code', size=64), - 'street': fields.char('Street', size=128), - 'street2': fields.char('Street2', size=128), - 'zip': fields.char('Zip', change_default=True, size=24), - 'city': fields.char('City', size=128), - 'state': fields.many2one("res.country.state", 'State', - domain="[('country_id', '=', country)]"), - 'country': fields.many2one('res.country', 'Country'), - 'email': fields.char('E-Mail', size=64), - 'phone': fields.char('Phone', size=64), - 'fax': fields.char('Fax', size=64), - 'active': fields.boolean('Active'), - 'bic': fields.char('BIC/Swift code', size=11, - help="Bank Identifier Code"), - } - _defaults = { - 'active': lambda *a: 1, - } + _description='Bank' + _name = 'res.bank' + _columns = { + 'name': fields.char('Name', size=128, required=True), + 'code': fields.char('Code', size=64), + 'street': fields.char('Street', size=128), + 'street2': fields.char('Street2', size=128), + 'zip': fields.char('Zip', change_default=True, size=24), + 'city': fields.char('City', size=128), + 'state': fields.many2one("res.country.state", 'State', + domain="[('country_id', '=', country)]"), + 'country': fields.many2one('res.country', 'Country'), + 'email': fields.char('E-Mail', size=64), + 'phone': fields.char('Phone', size=64), + 'fax': fields.char('Fax', size=64), + 'active': fields.boolean('Active'), + 'bic': fields.char('BIC/Swift code', size=11, + help="Bank Identifier Code"), + } + _defaults = { + 'active': lambda *a: 1, + } Bank() diff --git a/bin/addons/base/res/country.py b/bin/addons/base/res/country.py index 4a9d946bd04..603ee6fb5f4 100644 --- a/bin/addons/base/res/country.py +++ b/bin/addons/base/res/country.py @@ -32,76 +32,76 @@ from osv import fields, osv class Country(osv.osv): - _name = 'res.country' - _description = 'Country' - _columns = { - 'name': fields.char('Country Name', size=64, - help='The full name of the country.', required=True), - 'code': fields.char('Country Code', size=2, - help='The ISO country code in two chars.\n' - 'You can use this field for quick search.', required=True), - } - _sql_constraints = [ - ('name_uniq', 'unique (name)', - 'The name of the country must be unique !'), - ('code_uniq', 'unique (code)', - 'The code of the country must be unique !') - ] + _name = 'res.country' + _description = 'Country' + _columns = { + 'name': fields.char('Country Name', size=64, + help='The full name of the country.', required=True), + 'code': fields.char('Country Code', size=2, + help='The ISO country code in two chars.\n' + 'You can use this field for quick search.', required=True), + } + _sql_constraints = [ + ('name_uniq', 'unique (name)', + 'The name of the country must be unique !'), + ('code_uniq', 'unique (code)', + 'The code of the country must be unique !') + ] - def name_search(self, cr, user, name='', args=None, operator='ilike', - context=None, limit=80): - if not args: - args=[] - if not context: - context={} - ids = False - if len(name) == 2: - ids = self.search(cr, user, [('code', '=', name.upper())] + args, - limit=limit, context=context) - if not ids: - ids = self.search(cr, user, [('name', operator, name)] + args, - limit=limit, context=context) - return self.name_get(cr, user, ids, context) - _order='name' + def name_search(self, cr, user, name='', args=None, operator='ilike', + context=None, limit=80): + if not args: + args=[] + if not context: + context={} + ids = False + if len(name) == 2: + ids = self.search(cr, user, [('code', '=', name.upper())] + args, + limit=limit, context=context) + if not ids: + ids = self.search(cr, user, [('name', operator, name)] + args, + limit=limit, context=context) + return self.name_get(cr, user, ids, context) + _order='name' - def create(self, cursor, user, vals, context=None): - if 'code' in vals: - vals['code'] = vals['code'].upper() - return super(Country, self).create(cursor, user, vals, - context=context) + def create(self, cursor, user, vals, context=None): + if 'code' in vals: + vals['code'] = vals['code'].upper() + return super(Country, self).create(cursor, user, vals, + context=context) - def write(self, cursor, user, ids, vals, context=None): - if 'code' in vals: - vals['code'] = vals['code'].upper() - return super(Country, self).write(cursor, user, ids, vals, - context=context) + def write(self, cursor, user, ids, vals, context=None): + if 'code' in vals: + vals['code'] = vals['code'].upper() + return super(Country, self).write(cursor, user, ids, vals, + context=context) Country() class CountryState(osv.osv): - _description="Country state" - _name = 'res.country.state' - _columns = { - 'country_id': fields.many2one('res.country', 'Country', - required=True), - 'name': fields.char('State Name', size=64, required=True), - 'code': fields.char('State Code', size=3, required=True), - } - def name_search(self, cr, user, name='', args=None, operator='ilike', - context=None, limit=80): - if not args: - args = [] - if not context: - context = {} - ids = self.search(cr, user, [('code', '=', name)] + args, limit=limit, - context=context) - if not ids: - ids = self.search(cr, user, [('name', operator, name)] + args, - limit=limit, context=context) - return self.name_get(cr, user, ids, context) + _description="Country state" + _name = 'res.country.state' + _columns = { + 'country_id': fields.many2one('res.country', 'Country', + required=True), + 'name': fields.char('State Name', size=64, required=True), + 'code': fields.char('State Code', size=3, required=True), + } + def name_search(self, cr, user, name='', args=None, operator='ilike', + context=None, limit=80): + if not args: + args = [] + if not context: + context = {} + ids = self.search(cr, user, [('code', '=', name)] + args, limit=limit, + context=context) + if not ids: + ids = self.search(cr, user, [('name', operator, name)] + args, + limit=limit, context=context) + return self.name_get(cr, user, ids, context) - _order = 'code' + _order = 'code' CountryState() diff --git a/bin/addons/base/res/ir_property.py b/bin/addons/base/res/ir_property.py index 6ab36df6cf1..57f5c54db83 100644 --- a/bin/addons/base/res/ir_property.py +++ b/bin/addons/base/res/ir_property.py @@ -34,52 +34,52 @@ from osv import osv,fields # ------------------------------------------------------------------------- def _models_get2(self, cr, uid, context={}): - obj = self.pool.get('ir.model.fields') - ids = obj.search(cr, uid, [('view_load','=',1)]) - res = [] - done = {} - for o in obj.browse(cr, uid, ids, context=context): - if o.relation not in done: - res.append( [o.relation, o.relation]) - done[o.relation] = True - return res + obj = self.pool.get('ir.model.fields') + ids = obj.search(cr, uid, [('view_load','=',1)]) + res = [] + done = {} + for o in obj.browse(cr, uid, ids, context=context): + if o.relation not in done: + res.append( [o.relation, o.relation]) + done[o.relation] = True + return res def _models_get(self, cr, uid, context={}): - obj = self.pool.get('ir.model.fields') - ids = obj.search(cr, uid, [('view_load','=',1)]) - res = [] - done = {} - for o in obj.browse(cr, uid, ids, context=context): - if o.model_id.id not in done: - res.append( [o.model_id.model, o.model_id.name]) - done[o.model_id.id] = True - return res + obj = self.pool.get('ir.model.fields') + ids = obj.search(cr, uid, [('view_load','=',1)]) + res = [] + done = {} + for o in obj.browse(cr, uid, ids, context=context): + if o.model_id.id not in done: + res.append( [o.model_id.model, o.model_id.name]) + done[o.model_id.id] = True + return res class ir_property(osv.osv): - _name = 'ir.property' - _columns = { - 'name': fields.char('Name', size=128), - 'value': fields.reference('Value', selection=_models_get2, size=128), - 'res_id': fields.reference('Resource', selection=_models_get, size=128), - 'company_id': fields.many2one('res.company', 'Company'), - 'fields_id': fields.many2one('ir.model.fields', 'Fields', ondelete='cascade', required=True) - } - def unlink(self, cr, uid, ids, context={}): - if ids: - cr.execute('delete from ir_model_fields where id in (select fields_id from ir_property where (fields_id is not null) and (id in ('+','.join(map(str,ids))+')))') - res = super(ir_property, self).unlink(cr, uid, ids, context) - return res + _name = 'ir.property' + _columns = { + 'name': fields.char('Name', size=128), + 'value': fields.reference('Value', selection=_models_get2, size=128), + 'res_id': fields.reference('Resource', selection=_models_get, size=128), + 'company_id': fields.many2one('res.company', 'Company'), + 'fields_id': fields.many2one('ir.model.fields', 'Fields', ondelete='cascade', required=True) + } + def unlink(self, cr, uid, ids, context={}): + if ids: + cr.execute('delete from ir_model_fields where id in (select fields_id from ir_property where (fields_id is not null) and (id in ('+','.join(map(str,ids))+')))') + res = super(ir_property, self).unlink(cr, uid, ids, context) + return res - def get(self, cr, uid, name, model, res_id=False, context={}): - cr.execute('select id from ir_model_fields where name=%s and model=%s', (name, model)) - res = cr.fetchone() - if res: - ucid = self.pool.get('res.users').browse(cr, uid, uid).company_id.id - nid = self.search(cr, uid, [('fields_id','=',res[0]),('res_id','=',res_id),('company_id','=',ucid)]) - if nid: - d = self.browse(cr, uid, nid[0], context).value - return (d and int(d.split(',')[1])) or False - return False + def get(self, cr, uid, name, model, res_id=False, context={}): + cr.execute('select id from ir_model_fields where name=%s and model=%s', (name, model)) + res = cr.fetchone() + if res: + ucid = self.pool.get('res.users').browse(cr, uid, uid).company_id.id + nid = self.search(cr, uid, [('fields_id','=',res[0]),('res_id','=',res_id),('company_id','=',ucid)]) + if nid: + d = self.browse(cr, uid, nid[0], context).value + return (d and int(d.split(',')[1])) or False + return False ir_property() diff --git a/bin/addons/base/res/partner/crm.py b/bin/addons/base/res/partner/crm.py index 3bac895fed4..3fecb9a3388 100644 --- a/bin/addons/base/res/partner/crm.py +++ b/bin/addons/base/res/partner/crm.py @@ -35,72 +35,72 @@ from osv import fields,osv # Sale/Purchase Canal, Media # class res_partner_canal(osv.osv): - _name = "res.partner.canal" - _description = "Channels" - _columns = { - 'name': fields.char('Channel Name',size=64, required=True), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': lambda *a: 1, - } + _name = "res.partner.canal" + _description = "Channels" + _columns = { + 'name': fields.char('Channel Name',size=64, required=True), + 'active': fields.boolean('Active'), + } + _defaults = { + 'active': lambda *a: 1, + } res_partner_canal() # # Partner: State of Mind # class res_partner_som(osv.osv): - _name = "res.partner.som" - _columns = { - 'name': fields.char('State of Mind',size=64, required=True), - 'factor': fields.float('Factor', required=True) - } + _name = "res.partner.som" + _columns = { + 'name': fields.char('State of Mind',size=64, required=True), + 'factor': fields.float('Factor', required=True) + } res_partner_som() def _links_get(self, cr, uid, context={}): - obj = self.pool.get('res.request.link') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['object', 'name'], context) - return [(r['object'], r['name']) for r in res] + obj = self.pool.get('res.request.link') + ids = obj.search(cr, uid, []) + res = obj.read(cr, uid, ids, ['object', 'name'], context) + return [(r['object'], r['name']) for r in res] class res_partner_event(osv.osv): - _name = "res.partner.event" - _columns = { - 'name': fields.char('Events',size=64, required=True), - 'som': fields.many2one('res.partner.som', 'State of Mind'), - 'description': fields.text('Description'), - 'planned_cost': fields.float('Planned Cost'), - 'planned_revenue': fields.float('Planned Revenue'), - 'probability': fields.float('Probability (0.50)'), - 'document': fields.reference('Document', selection=_links_get, size=128), - 'partner_id': fields.many2one('res.partner', 'Partner', select=True), - 'date': fields.datetime('Date', size=16), - 'user_id': fields.many2one('res.users', 'User'), - 'canal_id': fields.many2one('res.partner.canal', 'Channel'), - 'partner_type': fields.selection([('customer','Customer'),('retailer','Retailer'),('prospect','Commercial Prospect')], 'Partner Relation'), - 'type': fields.selection([('sale','Sale Opportunity'),('purchase','Purchase Offer'),('prospect','Prospect Contact')], 'Type of Event'), - 'event_ical_id': fields.char('iCal id', size=64), - } - _order = 'date desc' - _defaults = { - 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } + _name = "res.partner.event" + _columns = { + 'name': fields.char('Events',size=64, required=True), + 'som': fields.many2one('res.partner.som', 'State of Mind'), + 'description': fields.text('Description'), + 'planned_cost': fields.float('Planned Cost'), + 'planned_revenue': fields.float('Planned Revenue'), + 'probability': fields.float('Probability (0.50)'), + 'document': fields.reference('Document', selection=_links_get, size=128), + 'partner_id': fields.many2one('res.partner', 'Partner', select=True), + 'date': fields.datetime('Date', size=16), + 'user_id': fields.many2one('res.users', 'User'), + 'canal_id': fields.many2one('res.partner.canal', 'Channel'), + 'partner_type': fields.selection([('customer','Customer'),('retailer','Retailer'),('prospect','Commercial Prospect')], 'Partner Relation'), + 'type': fields.selection([('sale','Sale Opportunity'),('purchase','Purchase Offer'),('prospect','Prospect Contact')], 'Type of Event'), + 'event_ical_id': fields.char('iCal id', size=64), + } + _order = 'date desc' + _defaults = { + 'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } res_partner_event() class res_partner_event_type(osv.osv): - _name = "res.partner.event.type" - _description = "Partner Events" - _columns = { - 'name': fields.char('Event Type',size=64, required=True), - 'key': fields.char('Key', size=64, required=True), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': lambda *a: 1 - } - def check(self, cr, uid, key, context={}): - return self.search(cr, uid, [('key','=',key)]) + _name = "res.partner.event.type" + _description = "Partner Events" + _columns = { + 'name': fields.char('Event Type',size=64, required=True), + 'key': fields.char('Key', size=64, required=True), + 'active': fields.boolean('Active'), + } + _defaults = { + 'active': lambda *a: 1 + } + def check(self, cr, uid, key, context={}): + return self.search(cr, uid, [('key','=',key)]) res_partner_event_type() diff --git a/bin/addons/base/res/partner/partner.py b/bin/addons/base/res/partner/partner.py index 833237b883f..c0edc509da2 100644 --- a/bin/addons/base/res/partner/partner.py +++ b/bin/addons/base/res/partner/partner.py @@ -35,399 +35,399 @@ import ir import pooler class res_partner_function(osv.osv): - _name = 'res.partner.function' - _description = 'Function of the contact' - _columns = { - 'name': fields.char('Function name', size=64, required=True), - 'code': fields.char('Code', size=8), - } - _order = 'name' + _name = 'res.partner.function' + _description = 'Function of the contact' + _columns = { + 'name': fields.char('Function name', size=64, required=True), + 'code': fields.char('Code', size=8), + } + _order = 'name' res_partner_function() class res_payterm(osv.osv): - _description = 'Payment term' - _name = 'res.payterm' - _columns = { - 'name': fields.char('Payment term (short name)', size=64), - } + _description = 'Payment term' + _name = 'res.payterm' + _columns = { + 'name': fields.char('Payment term (short name)', size=64), + } res_payterm() class res_partner_category(osv.osv): - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - reads = self.read(cr, uid, ids, ['name','parent_id'], context) - res = [] - for record in reads: - name = record['name'] - if record['parent_id']: - name = record['parent_id'][1]+' / '+name - res.append((record['id'], name)) - return res - - def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, unknow_dict): - res = self.name_get(cr, uid, ids) - return dict(res) - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from res_partner_category where id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - - _description='Partner Categories' - _name = 'res.partner.category' - _columns = { - 'name': fields.char('Category Name', required=True, size=64), - 'parent_id': fields.many2one('res.partner.category', 'Parent Category', select=True), - 'complete_name': fields.function(_name_get_fnc, method=True, type="char", string='Name'), - 'child_ids': fields.one2many('res.partner.category', 'parent_id', 'Childs Category'), - 'active' : fields.boolean('Active', help="The active field allows you to hide the category, without removing it."), - } - _constraints = [ - (_check_recursion, 'Error ! You can not create recursive categories.', ['parent_id']) - ] - _defaults = { - 'active' : lambda *a: 1, - } - _order = 'parent_id,name' + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + reads = self.read(cr, uid, ids, ['name','parent_id'], context) + res = [] + for record in reads: + name = record['name'] + if record['parent_id']: + name = record['parent_id'][1]+' / '+name + res.append((record['id'], name)) + return res + + def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, unknow_dict): + res = self.name_get(cr, uid, ids) + return dict(res) + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from res_partner_category where id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + + _description='Partner Categories' + _name = 'res.partner.category' + _columns = { + 'name': fields.char('Category Name', required=True, size=64), + 'parent_id': fields.many2one('res.partner.category', 'Parent Category', select=True), + 'complete_name': fields.function(_name_get_fnc, method=True, type="char", string='Name'), + 'child_ids': fields.one2many('res.partner.category', 'parent_id', 'Childs Category'), + 'active' : fields.boolean('Active', help="The active field allows you to hide the category, without removing it."), + } + _constraints = [ + (_check_recursion, 'Error ! You can not create recursive categories.', ['parent_id']) + ] + _defaults = { + 'active' : lambda *a: 1, + } + _order = 'parent_id,name' res_partner_category() class res_partner_title(osv.osv): - _name = 'res.partner.title' - _columns = { - 'name': fields.char('Title', required=True, size=46, translate=True), - 'shortcut': fields.char('Shortcut', required=True, size=16), - 'domain': fields.selection([('partner','Partner'),('contact','Contact')], 'Domain', required=True, size=24) - } - _order = 'name' + _name = 'res.partner.title' + _columns = { + 'name': fields.char('Title', required=True, size=46, translate=True), + 'shortcut': fields.char('Shortcut', required=True, size=16), + 'domain': fields.selection([('partner','Partner'),('contact','Contact')], 'Domain', required=True, size=24) + } + _order = 'name' res_partner_title() def _contact_title_get(self, cr, uid, context={}): - obj = self.pool.get('res.partner.title') - ids = obj.search(cr, uid, [('domain', '=', 'contact')]) - res = obj.read(cr, uid, ids, ['shortcut','name'], context) - return [(r['shortcut'], r['name']) for r in res] + obj = self.pool.get('res.partner.title') + ids = obj.search(cr, uid, [('domain', '=', 'contact')]) + res = obj.read(cr, uid, ids, ['shortcut','name'], context) + return [(r['shortcut'], r['name']) for r in res] def _partner_title_get(self, cr, uid, context={}): - obj = self.pool.get('res.partner.title') - ids = obj.search(cr, uid, [('domain', '=', 'partner')]) - res = obj.read(cr, uid, ids, ['shortcut','name'], context) - return [(r['shortcut'], r['name']) for r in res] + obj = self.pool.get('res.partner.title') + ids = obj.search(cr, uid, [('domain', '=', 'partner')]) + res = obj.read(cr, uid, ids, ['shortcut','name'], context) + return [(r['shortcut'], r['name']) for r in res] def _lang_get(self, cr, uid, context={}): - obj = self.pool.get('res.lang') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['code', 'name'], context) - res = [(r['code'], r['name']) for r in res] - return res + [(False, '')] + obj = self.pool.get('res.lang') + ids = obj.search(cr, uid, []) + res = obj.read(cr, uid, ids, ['code', 'name'], context) + res = [(r['code'], r['name']) for r in res] + return res + [(False, '')] class res_partner(osv.osv): - _description='Partner' - _name = "res.partner" - _order = "name" - _columns = { - 'name': fields.char('Name', size=128, required=True, select=True), - 'date': fields.date('Date', select=1), - 'title': fields.selection(_partner_title_get, 'Title', size=32), - 'parent_id': fields.many2one('res.partner','Main Company', select=2), - 'child_ids': fields.one2many('res.partner', 'parent_id', 'Partner Ref.'), - 'ref': fields.char('Code', size=64), - 'lang': fields.selection(_lang_get, 'Language', size=5), - 'user_id': fields.many2one('res.users', 'Dedicated Salesman', help='The internal user that is in charge of communicating with this partner if any.'), - 'responsible': fields.many2one('res.users', 'Users'), - 'vat': fields.char('VAT',size=32 ,help="Value Added Tax number"), - 'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'), - 'website': fields.char('Website',size=64), - 'comment': fields.text('Notes'), - 'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'), - 'category_id': fields.many2many('res.partner.category', 'res_partner_category_rel', 'partner_id', 'category_id', 'Categories'), - 'events': fields.one2many('res.partner.event', 'partner_id', 'Events'), - 'credit_limit': fields.float(string='Credit Limit'), - 'ean13': fields.char('EAN13', size=13), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': lambda *a: 1, - } - _sql_constraints = [ - ('name_uniq', 'unique (name)', 'The name of the partner must be unique !') - ] - - def copy(self, cr, uid, id, default=None, context={}): - name = self.read(cr, uid, [id], ['name'])[0]['name'] - default.update({'name': name+' (copy)'}) - return super(res_partner, self).copy(cr, uid, id, default, context) - - def _check_ean_key(self, cr, uid, ids): - for partner_o in pooler.get_pool(cr.dbname).get('res.partner').read(cr, uid, ids, ['ean13',]): - thisean=partner_o['ean13'] - if thisean and thisean!='': - if len(thisean)!=13: - return False - sum=0 - for i in range(12): - if not (i % 2): - sum+=int(thisean[i]) - else: - sum+=3*int(thisean[i]) - if math.ceil(sum/10.0)*10-sum!=int(thisean[12]): - return False - return True - -# _constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])] - - def name_get(self, cr, uid, ids, context={}): - if not len(ids): - return [] - if context.get('show_ref', False): - rec_name = 'ref' - else: - rec_name = 'name' - - res = [(r['id'], r[rec_name]) for r in self.read(cr, uid, ids, [rec_name], context)] - return res - - def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - if name: - ids = self.search(cr, uid, [('ref', '=', name)] + args, limit=limit, context=context) - if not ids: - ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context) - else: - ids = self.search(cr, uid, args, limit=limit, context=context) - return self.name_get(cr, uid, ids, context) - - def _email_send(self, cr, uid, ids, email_from, subject, body, on_error=None): - partners = self.browse(cr, uid, ids) - for partner in partners: - if len(partner.address): - if partner.address[0].email: - tools.email_send(email_from, [partner.address[0].email], subject, body, on_error) - return True - - def email_send(self, cr, uid, ids, email_from, subject, body, on_error=''): - while len(ids): - self.pool.get('ir.cron').create(cr, uid, { - 'name': 'Send Partner Emails', - 'user_id': uid, -# 'nextcall': False, - 'model': 'res.partner', - 'function': '_email_send', - 'args': repr([ids[:16], email_from, subject, body, on_error]) - }) - ids = ids[16:] - return True - - def address_get(self, cr, uid, ids, adr_pref=['default']): - cr.execute('select type,id from res_partner_address where partner_id in ('+','.join(map(str,ids))+')') - res = cr.fetchall() - adr = dict(res) - # get the id of the (first) default address if there is one, - # otherwise get the id of the first address in the list - if res: - default_address = adr.get('default', res[0][1]) - else: - default_address = False - result = {} - for a in adr_pref: - result[a] = adr.get(a, default_address) - return result - - def gen_next_ref(self, cr, uid, ids): - if len(ids) != 1: - return True - - # compute the next number ref - cr.execute("select ref from res_partner where ref is not null order by char_length(ref) desc, ref desc limit 1") - res = cr.dictfetchall() - ref = res and res[0]['ref'] or '0' - try: - nextref = int(ref)+1 - except e: - raise osv.except_osv(_('Warning'), _("Couldn't generate the next id because some partners have an alphabetic id !")) - - # update the current partner - cr.execute("update res_partner set ref=%d where id=%d", (nextref, ids[0])) - return True + _description='Partner' + _name = "res.partner" + _order = "name" + _columns = { + 'name': fields.char('Name', size=128, required=True, select=True), + 'date': fields.date('Date', select=1), + 'title': fields.selection(_partner_title_get, 'Title', size=32), + 'parent_id': fields.many2one('res.partner','Main Company', select=2), + 'child_ids': fields.one2many('res.partner', 'parent_id', 'Partner Ref.'), + 'ref': fields.char('Code', size=64), + 'lang': fields.selection(_lang_get, 'Language', size=5), + 'user_id': fields.many2one('res.users', 'Dedicated Salesman', help='The internal user that is in charge of communicating with this partner if any.'), + 'responsible': fields.many2one('res.users', 'Users'), + 'vat': fields.char('VAT',size=32 ,help="Value Added Tax number"), + 'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'), + 'website': fields.char('Website',size=64), + 'comment': fields.text('Notes'), + 'address': fields.one2many('res.partner.address', 'partner_id', 'Contacts'), + 'category_id': fields.many2many('res.partner.category', 'res_partner_category_rel', 'partner_id', 'category_id', 'Categories'), + 'events': fields.one2many('res.partner.event', 'partner_id', 'Events'), + 'credit_limit': fields.float(string='Credit Limit'), + 'ean13': fields.char('EAN13', size=13), + 'active': fields.boolean('Active'), + } + _defaults = { + 'active': lambda *a: 1, + } + _sql_constraints = [ + ('name_uniq', 'unique (name)', 'The name of the partner must be unique !') + ] + + def copy(self, cr, uid, id, default=None, context={}): + name = self.read(cr, uid, [id], ['name'])[0]['name'] + default.update({'name': name+' (copy)'}) + return super(res_partner, self).copy(cr, uid, id, default, context) + + def _check_ean_key(self, cr, uid, ids): + for partner_o in pooler.get_pool(cr.dbname).get('res.partner').read(cr, uid, ids, ['ean13',]): + thisean=partner_o['ean13'] + if thisean and thisean!='': + if len(thisean)!=13: + return False + sum=0 + for i in range(12): + if not (i % 2): + sum+=int(thisean[i]) + else: + sum+=3*int(thisean[i]) + if math.ceil(sum/10.0)*10-sum!=int(thisean[12]): + return False + return True + +# _constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean13'])] + + def name_get(self, cr, uid, ids, context={}): + if not len(ids): + return [] + if context.get('show_ref', False): + rec_name = 'ref' + else: + rec_name = 'name' + + res = [(r['id'], r[rec_name]) for r in self.read(cr, uid, ids, [rec_name], context)] + return res + + def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + if name: + ids = self.search(cr, uid, [('ref', '=', name)] + args, limit=limit, context=context) + if not ids: + ids = self.search(cr, uid, [('name', operator, name)] + args, limit=limit, context=context) + else: + ids = self.search(cr, uid, args, limit=limit, context=context) + return self.name_get(cr, uid, ids, context) + + def _email_send(self, cr, uid, ids, email_from, subject, body, on_error=None): + partners = self.browse(cr, uid, ids) + for partner in partners: + if len(partner.address): + if partner.address[0].email: + tools.email_send(email_from, [partner.address[0].email], subject, body, on_error) + return True + + def email_send(self, cr, uid, ids, email_from, subject, body, on_error=''): + while len(ids): + self.pool.get('ir.cron').create(cr, uid, { + 'name': 'Send Partner Emails', + 'user_id': uid, +# 'nextcall': False, + 'model': 'res.partner', + 'function': '_email_send', + 'args': repr([ids[:16], email_from, subject, body, on_error]) + }) + ids = ids[16:] + return True + + def address_get(self, cr, uid, ids, adr_pref=['default']): + cr.execute('select type,id from res_partner_address where partner_id in ('+','.join(map(str,ids))+')') + res = cr.fetchall() + adr = dict(res) + # get the id of the (first) default address if there is one, + # otherwise get the id of the first address in the list + if res: + default_address = adr.get('default', res[0][1]) + else: + default_address = False + result = {} + for a in adr_pref: + result[a] = adr.get(a, default_address) + return result + + def gen_next_ref(self, cr, uid, ids): + if len(ids) != 1: + return True + + # compute the next number ref + cr.execute("select ref from res_partner where ref is not null order by char_length(ref) desc, ref desc limit 1") + res = cr.dictfetchall() + ref = res and res[0]['ref'] or '0' + try: + nextref = int(ref)+1 + except e: + raise osv.except_osv(_('Warning'), _("Couldn't generate the next id because some partners have an alphabetic id !")) + + # update the current partner + cr.execute("update res_partner set ref=%d where id=%d", (nextref, ids[0])) + return True res_partner() class res_partner_address(osv.osv): - _description ='Partner Contact' - _name = 'res.partner.address' - _order = 'id' - _columns = { - 'partner_id': fields.many2one('res.partner', 'Partner', required=True, ondelete='cascade', select=True), - 'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other') ],'Address Type'), - 'function': fields.many2one('res.partner.function', 'Function'), - 'title': fields.selection(_contact_title_get, 'Title', size=32), - 'name': fields.char('Contact Name', size=64), - 'street': fields.char('Street', size=128), - 'street2': fields.char('Street2', size=128), - 'zip': fields.char('Zip', change_default=True, size=24), - 'city': fields.char('City', size=128), - 'state_id': fields.many2one("res.country.state", 'State', change_default=True, domain="[('country_id','=',country_id)]"), - 'country_id': fields.many2one('res.country', 'Country', change_default=True), - 'email': fields.char('E-Mail', size=240), - 'phone': fields.char('Phone', size=64), - 'fax': fields.char('Fax', size=64), - 'mobile': fields.char('Mobile', size=64), - 'birthdate': fields.char('Birthdate', size=64), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': lambda *a: 1, - } - - def name_get(self, cr, user, ids, context={}): - if not len(ids): - return [] - res = [] - for r in self.read(cr, user, ids, ['name','zip','city','partner_id', 'street']): - if context.get('contact_display', 'contact')=='partner': - res.append((r['id'], r['partner_id'][1])) - else: - addr = str(r['name'] or '') - if r['name'] and (r['zip'] or r['city']): - addr += ', ' - addr += str(r['street'] or '') + ' ' + str(r['zip'] or '') + ' ' + str(r['city'] or '') - res.append((r['id'], addr.strip() or '/')) - return res - - def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - if context.get('contact_display', 'contact')=='partner': - ids = self.search(cr, user, [('partner_id',operator,name)], limit=limit, context=context) - else: - ids = self.search(cr, user, [('zip','=',name)] + args, limit=limit, context=context) - if not ids: - ids = self.search(cr, user, [('city',operator,name)] + args, limit=limit, context=context) - if name: - ids += self.search(cr, user, [('name',operator,name)] + args, limit=limit, context=context) - ids += self.search(cr, user, [('partner_id',operator,name)] + args, limit=limit, context=context) - return self.name_get(cr, user, ids, context=context) + _description ='Partner Contact' + _name = 'res.partner.address' + _order = 'id' + _columns = { + 'partner_id': fields.many2one('res.partner', 'Partner', required=True, ondelete='cascade', select=True), + 'type': fields.selection( [ ('default','Default'),('invoice','Invoice'), ('delivery','Delivery'), ('contact','Contact'), ('other','Other') ],'Address Type'), + 'function': fields.many2one('res.partner.function', 'Function'), + 'title': fields.selection(_contact_title_get, 'Title', size=32), + 'name': fields.char('Contact Name', size=64), + 'street': fields.char('Street', size=128), + 'street2': fields.char('Street2', size=128), + 'zip': fields.char('Zip', change_default=True, size=24), + 'city': fields.char('City', size=128), + 'state_id': fields.many2one("res.country.state", 'State', change_default=True, domain="[('country_id','=',country_id)]"), + 'country_id': fields.many2one('res.country', 'Country', change_default=True), + 'email': fields.char('E-Mail', size=240), + 'phone': fields.char('Phone', size=64), + 'fax': fields.char('Fax', size=64), + 'mobile': fields.char('Mobile', size=64), + 'birthdate': fields.char('Birthdate', size=64), + 'active': fields.boolean('Active'), + } + _defaults = { + 'active': lambda *a: 1, + } + + def name_get(self, cr, user, ids, context={}): + if not len(ids): + return [] + res = [] + for r in self.read(cr, user, ids, ['name','zip','city','partner_id', 'street']): + if context.get('contact_display', 'contact')=='partner': + res.append((r['id'], r['partner_id'][1])) + else: + addr = str(r['name'] or '') + if r['name'] and (r['zip'] or r['city']): + addr += ', ' + addr += str(r['street'] or '') + ' ' + str(r['zip'] or '') + ' ' + str(r['city'] or '') + res.append((r['id'], addr.strip() or '/')) + return res + + def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + if context.get('contact_display', 'contact')=='partner': + ids = self.search(cr, user, [('partner_id',operator,name)], limit=limit, context=context) + else: + ids = self.search(cr, user, [('zip','=',name)] + args, limit=limit, context=context) + if not ids: + ids = self.search(cr, user, [('city',operator,name)] + args, limit=limit, context=context) + if name: + ids += self.search(cr, user, [('name',operator,name)] + args, limit=limit, context=context) + ids += self.search(cr, user, [('partner_id',operator,name)] + args, limit=limit, context=context) + return self.name_get(cr, user, ids, context=context) res_partner_address() class res_partner_bank_type(osv.osv): - _description='Bank Account Type' - _name = 'res.partner.bank.type' - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'code': fields.char('Code', size=64, required=True), - 'field_ids': fields.one2many('res.partner.bank.type.field', 'bank_type_id', 'Type fields'), - } + _description='Bank Account Type' + _name = 'res.partner.bank.type' + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'code': fields.char('Code', size=64, required=True), + 'field_ids': fields.one2many('res.partner.bank.type.field', 'bank_type_id', 'Type fields'), + } res_partner_bank_type() class res_partner_bank_type_fields(osv.osv): - _description='Bank type fields' - _name = 'res.partner.bank.type.field' - _columns = { - 'name': fields.char('Field name', size=64, required=True), - 'bank_type_id': fields.many2one('res.partner.bank.type', 'Bank type', required=True, ondelete='cascade'), - 'required': fields.boolean('Required'), - 'readonly': fields.boolean('Readonly'), - 'size': fields.integer('Max. Size'), - } + _description='Bank type fields' + _name = 'res.partner.bank.type.field' + _columns = { + 'name': fields.char('Field name', size=64, required=True), + 'bank_type_id': fields.many2one('res.partner.bank.type', 'Bank type', required=True, ondelete='cascade'), + 'required': fields.boolean('Required'), + 'readonly': fields.boolean('Readonly'), + 'size': fields.integer('Max. Size'), + } res_partner_bank_type_fields() class res_partner_bank(osv.osv): - '''Bank Accounts''' - _name = "res.partner.bank" - _rec_name = "state" - _description = __doc__ - _order = 'sequence' - - def _bank_type_get(self, cr, uid, context=None): - bank_type_obj = self.pool.get('res.partner.bank.type') - - result = [] - type_ids = bank_type_obj.search(cr, uid, []) - bank_types = bank_type_obj.browse(cr, uid, type_ids) - for bank_type in bank_types: - result.append((bank_type.code, bank_type.name)) - return result - - def _default_value(self, cursor, user, field, context=None): - if field in ('country_id', 'state_id'): - value = False - else: - value = '' - if not context.get('address', False): - return value - for ham, spam, address in context['address']: - if address.get('type', False) == 'default': - return address.get(field, value) - elif not address.get('type', False): - value = address.get(field, value) - return value - - _columns = { - 'name': fields.char('Description', size=128), - 'acc_number': fields.char('Account number', size=64, required=False), - 'bank': fields.many2one('res.bank', 'Bank'), - 'owner_name': fields.char('Account owner', size=64), - 'street': fields.char('Street', size=128), - 'zip': fields.char('Zip', change_default=True, size=24), - 'city': fields.char('City', size=128), - 'country_id': fields.many2one('res.country', 'Country', - change_default=True), - 'state_id': fields.many2one("res.country.state", 'State', - change_default=True, domain="[('country_id','=',country_id)]"), - 'partner_id': fields.many2one('res.partner', 'Partner', required=True, - ondelete='cascade', select=True), - 'state': fields.selection(_bank_type_get, 'Bank type', required=True, - change_default=True), - 'sequence': fields.integer('Sequence'), - 'state_id': fields.many2one('res.country.state', 'State', - domain="[('country_id', '=', country_id)]"), - } - _defaults = { - 'owner_name': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'name', context=context), - 'street': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'street', context=context), - 'city': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'city', context=context), - 'zip': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'zip', context=context), - 'country_id': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'country_id', context=context), - 'state_id': lambda obj, cursor, user, context: obj._default_value( - cursor, user, 'state_id', context=context), - } - - def fields_get(self, cr, uid, fields=None, context=None): - res = super(res_partner_bank, self).fields_get(cr, uid, fields, context) - bank_type_obj = self.pool.get('res.partner.bank.type') - type_ids = bank_type_obj.search(cr, uid, []) - types = bank_type_obj.browse(cr, uid, type_ids) - for type in types: - for field in type.field_ids: - if field.name in res: - res[field.name].setdefault('states', {}) - res[field.name]['states'][type.code] = [ - ('readonly', field.readonly), - ('required', field.required)] - return res - - def name_get(self, cr, uid, ids, context=None): - if not len(ids): - return [] - res = [] - for id in self.browse(cr, uid, ids): - res.append((id.id,id.acc_number)) - return res + '''Bank Accounts''' + _name = "res.partner.bank" + _rec_name = "state" + _description = __doc__ + _order = 'sequence' + + def _bank_type_get(self, cr, uid, context=None): + bank_type_obj = self.pool.get('res.partner.bank.type') + + result = [] + type_ids = bank_type_obj.search(cr, uid, []) + bank_types = bank_type_obj.browse(cr, uid, type_ids) + for bank_type in bank_types: + result.append((bank_type.code, bank_type.name)) + return result + + def _default_value(self, cursor, user, field, context=None): + if field in ('country_id', 'state_id'): + value = False + else: + value = '' + if not context.get('address', False): + return value + for ham, spam, address in context['address']: + if address.get('type', False) == 'default': + return address.get(field, value) + elif not address.get('type', False): + value = address.get(field, value) + return value + + _columns = { + 'name': fields.char('Description', size=128), + 'acc_number': fields.char('Account number', size=64, required=False), + 'bank': fields.many2one('res.bank', 'Bank'), + 'owner_name': fields.char('Account owner', size=64), + 'street': fields.char('Street', size=128), + 'zip': fields.char('Zip', change_default=True, size=24), + 'city': fields.char('City', size=128), + 'country_id': fields.many2one('res.country', 'Country', + change_default=True), + 'state_id': fields.many2one("res.country.state", 'State', + change_default=True, domain="[('country_id','=',country_id)]"), + 'partner_id': fields.many2one('res.partner', 'Partner', required=True, + ondelete='cascade', select=True), + 'state': fields.selection(_bank_type_get, 'Bank type', required=True, + change_default=True), + 'sequence': fields.integer('Sequence'), + 'state_id': fields.many2one('res.country.state', 'State', + domain="[('country_id', '=', country_id)]"), + } + _defaults = { + 'owner_name': lambda obj, cursor, user, context: obj._default_value( + cursor, user, 'name', context=context), + 'street': lambda obj, cursor, user, context: obj._default_value( + cursor, user, 'street', context=context), + 'city': lambda obj, cursor, user, context: obj._default_value( + cursor, user, 'city', context=context), + 'zip': lambda obj, cursor, user, context: obj._default_value( + cursor, user, 'zip', context=context), + 'country_id': lambda obj, cursor, user, context: obj._default_value( + cursor, user, 'country_id', context=context), + 'state_id': lambda obj, cursor, user, context: obj._default_value( + cursor, user, 'state_id', context=context), + } + + def fields_get(self, cr, uid, fields=None, context=None): + res = super(res_partner_bank, self).fields_get(cr, uid, fields, context) + bank_type_obj = self.pool.get('res.partner.bank.type') + type_ids = bank_type_obj.search(cr, uid, []) + types = bank_type_obj.browse(cr, uid, type_ids) + for type in types: + for field in type.field_ids: + if field.name in res: + res[field.name].setdefault('states', {}) + res[field.name]['states'][type.code] = [ + ('readonly', field.readonly), + ('required', field.required)] + return res + + def name_get(self, cr, uid, ids, context=None): + if not len(ids): + return [] + res = [] + for id in self.browse(cr, uid, ids): + res.append((id.id,id.acc_number)) + return res res_partner_bank() diff --git a/bin/addons/base/res/partner/wizard/wizard_clear_ids.py b/bin/addons/base/res/partner/wizard/wizard_clear_ids.py index b6dc6e5ccb0..457add8f688 100644 --- a/bin/addons/base/res/partner/wizard/wizard_clear_ids.py +++ b/bin/addons/base/res/partner/wizard/wizard_clear_ids.py @@ -31,16 +31,16 @@ import wizard import netsvc class wizard_clear_ids(wizard.interface): - def _clear_ids(self, cr, uid, data, context): - service = netsvc.LocalService("object_proxy") - service.execute(cr.dbname, uid, 'res.partner', 'write', data['ids'], {'ref': False}) - return {} - - states = { - 'init': { - 'actions': [_clear_ids], - 'result': {'type':'state', 'state':'end'} - } - } + def _clear_ids(self, cr, uid, data, context): + service = netsvc.LocalService("object_proxy") + service.execute(cr.dbname, uid, 'res.partner', 'write', data['ids'], {'ref': False}) + return {} + + states = { + 'init': { + 'actions': [_clear_ids], + 'result': {'type':'state', 'state':'end'} + } + } wizard_clear_ids('res.partner.clear_ids') diff --git a/bin/addons/base/res/partner/wizard/wizard_ean_check.py b/bin/addons/base/res/partner/wizard/wizard_ean_check.py index dea3c55a120..52af65e528f 100644 --- a/bin/addons/base/res/partner/wizard/wizard_ean_check.py +++ b/bin/addons/base/res/partner/wizard/wizard_ean_check.py @@ -34,70 +34,70 @@ from tools.misc import UpdateableStr import pooler def _is_pair(x): - return not x%2 + return not x%2 def _get_ean_key(string): - if not string or string=='': - return '0' - if len(string)!=12: - return '0' - sum=0 - for i in range(12): - if _is_pair(i): - sum+=int(string[i]) - else: - sum+=3*int(string[i]) - return str(int(math.ceil(sum/10.0)*10-sum)) + if not string or string=='': + return '0' + if len(string)!=12: + return '0' + sum=0 + for i in range(12): + if _is_pair(i): + sum+=int(string[i]) + else: + sum+=3*int(string[i]) + return str(int(math.ceil(sum/10.0)*10-sum)) #FIXME: this is not concurrency safe !!!! _check_arch = UpdateableStr() _check_fields = {} def _check_key(self, cr, uid, data, context): - partner_table=pooler.get_pool(cr.dbname).get('res.partner') - partners = partner_table.browse(cr, uid, data['ids']) - _check_arch_lst=['<?xml version="1.0"?>', '<form string="Check EAN13">', '<label string=""/>', '<label string=""/>','<label string="Original" />', '<label string="Computed" />'] - for partner in partners: - if partner['ean13'] and len(partner['ean13'])>11 and len(partner['ean13'])<14: - _check_arch_lst.append('<label colspan="2" string="%s" />' % partner['ean13']); - key=_get_ean_key(partner['ean13'][:12]) - _check_arch_lst.append('<label string=""/>') - if len(partner['ean13'])==12: - _check_arch_lst.append('<label string="" />'); - else: - _check_arch_lst.append('<label string="%s" />' % partner['ean13'][12]) - _check_arch_lst.append('<label string="%s" />' % key) - _check_arch_lst.append('</form>') - _check_arch.string = '\n'.join(_check_arch_lst) - return {} + partner_table=pooler.get_pool(cr.dbname).get('res.partner') + partners = partner_table.browse(cr, uid, data['ids']) + _check_arch_lst=['<?xml version="1.0"?>', '<form string="Check EAN13">', '<label string=""/>', '<label string=""/>','<label string="Original" />', '<label string="Computed" />'] + for partner in partners: + if partner['ean13'] and len(partner['ean13'])>11 and len(partner['ean13'])<14: + _check_arch_lst.append('<label colspan="2" string="%s" />' % partner['ean13']); + key=_get_ean_key(partner['ean13'][:12]) + _check_arch_lst.append('<label string=""/>') + if len(partner['ean13'])==12: + _check_arch_lst.append('<label string="" />'); + else: + _check_arch_lst.append('<label string="%s" />' % partner['ean13'][12]) + _check_arch_lst.append('<label string="%s" />' % key) + _check_arch_lst.append('</form>') + _check_arch.string = '\n'.join(_check_arch_lst) + return {} def _update_ean(self, cr, uid, data, context): - partner_table = pooler.get_pool(cr.dbname).get('res.partner') - partners = partner_table.browse(cr, uid, data['ids']) - for partner in partners: - partner_table.write(cr, uid, data['ids'], { - 'ean13': "%s%s" % (partner['ean13'][:12], _get_ean_key(partner['ean13'][:12])) - }) - return {} + partner_table = pooler.get_pool(cr.dbname).get('res.partner') + partners = partner_table.browse(cr, uid, data['ids']) + for partner in partners: + partner_table.write(cr, uid, data['ids'], { + 'ean13': "%s%s" % (partner['ean13'][:12], _get_ean_key(partner['ean13'][:12])) + }) + return {} class wiz_ean_check(wizard.interface): - states = { - 'init': { - 'actions': [_check_key], - 'result': { - 'type': 'form', - 'arch': _check_arch, - 'fields': _check_fields, - 'state': (('end', 'Ignore'), ('correct', 'Correct EAN13')) - } - }, - 'correct' : { - 'actions': [_update_ean], - 'result': { - 'type': 'state', - 'state': 'end' - } - } - } + states = { + 'init': { + 'actions': [_check_key], + 'result': { + 'type': 'form', + 'arch': _check_arch, + 'fields': _check_fields, + 'state': (('end', 'Ignore'), ('correct', 'Correct EAN13')) + } + }, + 'correct' : { + 'actions': [_update_ean], + 'result': { + 'type': 'state', + 'state': 'end' + } + } + } wiz_ean_check('res.partner.ean13') diff --git a/bin/addons/base/res/partner/wizard/wizard_sms.py b/bin/addons/base/res/partner/wizard/wizard_sms.py index c3391852abe..fbd671e9764 100644 --- a/bin/addons/base/res/partner/wizard/wizard_sms.py +++ b/bin/addons/base/res/partner/wizard/wizard_sms.py @@ -33,47 +33,47 @@ import tools sms_send_form = '''<?xml version="1.0"?> <form string="%s"> - <separator string="%s" colspan="4"/> - <field name="app_id"/> - <newline/> - <field name="user"/> - <field name="password"/> - <newline/> - <field name="text" colspan="4"/> + <separator string="%s" colspan="4"/> + <field name="app_id"/> + <newline/> + <field name="user"/> + <field name="password"/> + <newline/> + <field name="text" colspan="4"/> </form>''' % ('SMS - Gateway: clickatell','Bulk SMS send') sms_send_fields = { - 'app_id': {'string':'API ID', 'type':'char', 'required':True}, - 'user': {'string':'Login', 'type':'char', 'required':True}, - 'password': {'string':'Password', 'type':'char', 'required':True}, - 'text': {'string':'SMS Message', 'type':'text', 'required':True} + 'app_id': {'string':'API ID', 'type':'char', 'required':True}, + 'user': {'string':'Login', 'type':'char', 'required':True}, + 'password': {'string':'Password', 'type':'char', 'required':True}, + 'text': {'string':'SMS Message', 'type':'text', 'required':True} } def _sms_send(self, cr, uid, data, context): - service = netsvc.LocalService("object_proxy") + service = netsvc.LocalService("object_proxy") - res_ids = service.execute(cr.dbname, uid, 'res.partner.address', 'search', [('partner_id','in',data['ids']),('type','=','default')]) - res = service.execute(cr.dbname, uid, 'res.partner.address', 'read', res_ids, ['mobile']) + res_ids = service.execute(cr.dbname, uid, 'res.partner.address', 'search', [('partner_id','in',data['ids']),('type','=','default')]) + res = service.execute(cr.dbname, uid, 'res.partner.address', 'read', res_ids, ['mobile']) - nbr = 0 - for r in res: - to = r['mobile'] - if to: - tools.sms_send(data['form']['user'], data['form']['password'], data['form']['app_id'], unicode(data['form']['text'], 'utf-8').encode('latin1'), to) - nbr += 1 - return {'sms_sent': nbr} + nbr = 0 + for r in res: + to = r['mobile'] + if to: + tools.sms_send(data['form']['user'], data['form']['password'], data['form']['app_id'], unicode(data['form']['text'], 'utf-8').encode('latin1'), to) + nbr += 1 + return {'sms_sent': nbr} class part_sms(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch':sms_send_form, 'fields': sms_send_fields, 'state':[('end','Cancel'), ('send','Send SMS')]} - }, - 'send': { - 'actions': [_sms_send], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch':sms_send_form, 'fields': sms_send_fields, 'state':[('end','Cancel'), ('send','Send SMS')]} + }, + 'send': { + 'actions': [_sms_send], + 'result': {'type': 'state', 'state':'end'} + } + } part_sms('res.partner.sms_send') diff --git a/bin/addons/base/res/partner/wizard/wizard_spam.py b/bin/addons/base/res/partner/wizard/wizard_spam.py index 95508f4c35b..c5353021264 100644 --- a/bin/addons/base/res/partner/wizard/wizard_spam.py +++ b/bin/addons/base/res/partner/wizard/wizard_spam.py @@ -33,48 +33,48 @@ import tools email_send_form = '''<?xml version="1.0"?> <form string="Mass Mailing"> - <field name="from"/> - <newline/> - <field name="subject"/> - <newline/> - <field name="text"/> + <field name="from"/> + <newline/> + <field name="subject"/> + <newline/> + <field name="text"/> </form>''' email_send_fields = { - 'from': {'string':"Sender's email", 'type':'char', 'size':64, 'required':True}, - 'subject': {'string':'Subject', 'type':'char', 'size':64, 'required':True}, - 'text': {'string':'Message', 'type':'text_tag', 'required':True} + 'from': {'string':"Sender's email", 'type':'char', 'size':64, 'required':True}, + 'subject': {'string':'Subject', 'type':'char', 'size':64, 'required':True}, + 'text': {'string':'Message', 'type':'text_tag', 'required':True} } # this sends an email to ALL the addresses of the selected partners. def _mass_mail_send(self, cr, uid, data, context): - nbr = 0 - partners = pooler.get_pool(cr.dbname).get('res.partner').browse(cr, uid, data['ids'], context) - for partner in partners: - for adr in partner.address: - if adr.email: - name = adr.name or partner.name - to = '%s <%s>' % (name, adr.email) + nbr = 0 + partners = pooler.get_pool(cr.dbname).get('res.partner').browse(cr, uid, data['ids'], context) + for partner in partners: + for adr in partner.address: + if adr.email: + name = adr.name or partner.name + to = '%s <%s>' % (name, adr.email) #TODO: add some tests to check for invalid email addresses #CHECKME: maybe we should use res.partner/email_send - tools.email_send(data['form']['from'], [to], data['form']['subject'], data['form']['text']) - nbr += 1 - pooler.get_pool(cr.dbname).get('res.partner.event').create(cr, uid, - {'name': 'Email sent through mass mailing', - 'partner_id': partner.id, - 'description': data['form']['text'], }) + tools.email_send(data['form']['from'], [to], data['form']['subject'], data['form']['text']) + nbr += 1 + pooler.get_pool(cr.dbname).get('res.partner.event').create(cr, uid, + {'name': 'Email sent through mass mailing', + 'partner_id': partner.id, + 'description': data['form']['text'], }) #TODO: log number of message sent - return {'email_sent': nbr} + return {'email_sent': nbr} class part_email(wizard.interface): - states = { - 'init': { - 'actions': [], - 'result': {'type': 'form', 'arch': email_send_form, 'fields': email_send_fields, 'state':[('end','Cancel'), ('send','Send Email')]} - }, - 'send': { - 'actions': [_mass_mail_send], - 'result': {'type': 'state', 'state':'end'} - } - } + states = { + 'init': { + 'actions': [], + 'result': {'type': 'form', 'arch': email_send_form, 'fields': email_send_fields, 'state':[('end','Cancel'), ('send','Send Email')]} + }, + 'send': { + 'actions': [_mass_mail_send], + 'result': {'type': 'state', 'state':'end'} + } + } part_email('res.partner.spam_send') diff --git a/bin/addons/base/res/res_company.py b/bin/addons/base/res/res_company.py index 88b3c1779f7..1c5776dda88 100644 --- a/bin/addons/base/res/res_company.py +++ b/bin/addons/base/res/res_company.py @@ -31,152 +31,152 @@ from osv import fields,osv import tools class res_company(osv.osv): - _name = "res.company" - - _columns = { - 'name': fields.char('Company Name', size=64, required=True), - 'parent_id': fields.many2one('res.company', 'Parent Company', select=True), - 'child_ids': fields.one2many('res.company', 'parent_id', 'Childs Company'), - 'partner_id': fields.many2one('res.partner', 'Partner', required=True), - 'rml_header1': fields.char('Report Header', size=200), - 'rml_footer1': fields.char('Report Footer 1', size=200), - 'rml_footer2': fields.char('Report Footer 2', size=200), - 'rml_header' : fields.text('RML Header'), - 'rml_header2' : fields.text('RML Internal Header'), - 'logo' : fields.binary('Logo'), - 'currency_id': fields.many2one('res.currency', 'Currency', required=True), - } - - def _get_child_ids(self, cr, uid, uid2, context={}): - company = self.pool.get('res.users').company_get(cr, uid, uid2) - ids = self._get_company_children(cr, uid, company) - return ids - - def _get_company_children(self, cr, uid=None, company=None): - if not company: - return [] - ids = self.search(cr, uid, [('parent_id','child_of',[company])]) - return ids - _get_company_children = tools.cache()(_get_company_children) - - def _get_partner_hierarchy(self, cr, uid, company_id, context={}): - if company_id: - parent_id = self.browse(cr, uid, company_id)['parent_id'] - if parent_id: - return self._get_partner_hierarchy(cr, uid, parent_id.id, context) - else: - return self._get_partner_descendance(cr, uid, company_id, [], context) - return [] - - def _get_partner_descendance(self, cr, uid, company_id, descendance, context={}): - descendance.append(self.browse(cr, uid, company_id).partner_id.id) - for child_id in self._get_company_children(cr, uid, company_id): - if child_id != company_id: - descendance = self._get_partner_descendance(cr, uid, child_id, descendance) - return descendance - - def __init__(self, *args, **argv): - return super(res_company, self).__init__(*args, **argv) - - # - # This function restart the cache on the _get_company_children method - # - def cache_restart(self, uid=None): - self._get_company_children() - - def create(self, *args, **argv): - self.cache_restart() - return super(res_company, self).create(*args, **argv) - - def write(self, *args, **argv): - self.cache_restart() - # Restart the cache on the company_get method - self.pool.get('ir.rule').domain_get() - return super(res_company, self).write(*args, **argv) - - def _get_euro(self, cr, uid, context={}): - try: - return self.pool.get('res.currency').search(cr, uid, [('rate', '=', 1.0),])[0] - except: - return 1 - - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from res_company where id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - - def _get_header2(self,cr,uid,ids): - return """ - <header> - <pageTemplate> - <frame id="first" x1="1.3cm" y1="1.5cm" width="18.4cm" height="26.5cm"/> - <pageGraphics> - <fill color="black"/> - <stroke color="black"/> - <setFont name="Helvetica" size="8"/> - <drawString x="1.3cm" y="28.3cm"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString> - <setFont name="Helvetica-Bold" size="10"/> - <drawString x="9.8cm" y="28.3cm">[[ company.partner_id.name ]]</drawString> - <setFont name="Helvetica" size="8"/> - <drawRightString x="19.7cm" y="28.3cm"><pageNumber/> / </drawRightString> - <drawString x="19.8cm" y="28.3cm"><pageCount/></drawString> - <stroke color="#aaaaaa"/> - <lines>1.3cm 28.1cm 20cm 28.1cm</lines> - </pageGraphics> - </pageTemplate> + _name = "res.company" + + _columns = { + 'name': fields.char('Company Name', size=64, required=True), + 'parent_id': fields.many2one('res.company', 'Parent Company', select=True), + 'child_ids': fields.one2many('res.company', 'parent_id', 'Childs Company'), + 'partner_id': fields.many2one('res.partner', 'Partner', required=True), + 'rml_header1': fields.char('Report Header', size=200), + 'rml_footer1': fields.char('Report Footer 1', size=200), + 'rml_footer2': fields.char('Report Footer 2', size=200), + 'rml_header' : fields.text('RML Header'), + 'rml_header2' : fields.text('RML Internal Header'), + 'logo' : fields.binary('Logo'), + 'currency_id': fields.many2one('res.currency', 'Currency', required=True), + } + + def _get_child_ids(self, cr, uid, uid2, context={}): + company = self.pool.get('res.users').company_get(cr, uid, uid2) + ids = self._get_company_children(cr, uid, company) + return ids + + def _get_company_children(self, cr, uid=None, company=None): + if not company: + return [] + ids = self.search(cr, uid, [('parent_id','child_of',[company])]) + return ids + _get_company_children = tools.cache()(_get_company_children) + + def _get_partner_hierarchy(self, cr, uid, company_id, context={}): + if company_id: + parent_id = self.browse(cr, uid, company_id)['parent_id'] + if parent_id: + return self._get_partner_hierarchy(cr, uid, parent_id.id, context) + else: + return self._get_partner_descendance(cr, uid, company_id, [], context) + return [] + + def _get_partner_descendance(self, cr, uid, company_id, descendance, context={}): + descendance.append(self.browse(cr, uid, company_id).partner_id.id) + for child_id in self._get_company_children(cr, uid, company_id): + if child_id != company_id: + descendance = self._get_partner_descendance(cr, uid, child_id, descendance) + return descendance + + def __init__(self, *args, **argv): + return super(res_company, self).__init__(*args, **argv) + + # + # This function restart the cache on the _get_company_children method + # + def cache_restart(self, uid=None): + self._get_company_children() + + def create(self, *args, **argv): + self.cache_restart() + return super(res_company, self).create(*args, **argv) + + def write(self, *args, **argv): + self.cache_restart() + # Restart the cache on the company_get method + self.pool.get('ir.rule').domain_get() + return super(res_company, self).write(*args, **argv) + + def _get_euro(self, cr, uid, context={}): + try: + return self.pool.get('res.currency').search(cr, uid, [('rate', '=', 1.0),])[0] + except: + return 1 + + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from res_company where id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + + def _get_header2(self,cr,uid,ids): + return """ + <header> + <pageTemplate> + <frame id="first" x1="1.3cm" y1="1.5cm" width="18.4cm" height="26.5cm"/> + <pageGraphics> + <fill color="black"/> + <stroke color="black"/> + <setFont name="Helvetica" size="8"/> + <drawString x="1.3cm" y="28.3cm"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString> + <setFont name="Helvetica-Bold" size="10"/> + <drawString x="9.8cm" y="28.3cm">[[ company.partner_id.name ]]</drawString> + <setFont name="Helvetica" size="8"/> + <drawRightString x="19.7cm" y="28.3cm"><pageNumber/> / </drawRightString> + <drawString x="19.8cm" y="28.3cm"><pageCount/></drawString> + <stroke color="#aaaaaa"/> + <lines>1.3cm 28.1cm 20cm 28.1cm</lines> + </pageGraphics> + </pageTemplate> </header>""" - def _get_header(self,cr,uid,ids): - try : - return tools.file_open('custom/corporate_rml_header.rml').read() - except: - return """ - <header> - <pageTemplate> - <frame id="first" x1="1.3cm" y1="2.5cm" height="23.0cm" width="19cm"/> - <pageGraphics> - <!-- You Logo - Change X,Y,Width and Height --> - <image x="1.3cm" y="27.6cm" height="40.0" >[[company.logo]]</image> - <setFont name="Helvetica" size="8"/> - <fill color="black"/> - <stroke color="black"/> - <lines>1.3cm 27.7cm 20cm 27.7cm</lines> - - <drawRightString x="20cm" y="27.8cm">[[ company.rml_header1 ]]</drawRightString> - - - <drawString x="1.3cm" y="27.2cm">[[ company.partner_id.name ]]</drawString> - <drawString x="1.3cm" y="26.8cm">[[ company.partner_id.address and company.partner_id.address[0].street ]]</drawString> - <drawString x="1.3cm" y="26.4cm">[[ company.partner_id.address and company.partner_id.address[0].zip ]] [[ company.partner_id.address and company.partner_id.address[0].city ]] - [[ company.partner_id.address and company.partner_id.address[0].country_id and company.partner_id.address[0].country_id.name ]]</drawString> - <drawString x="1.3cm" y="26.0cm">Phone:</drawString> - <drawRightString x="7cm" y="26.0cm">[[ company.partner_id.address and company.partner_id.address[0].phone ]]</drawRightString> - <drawString x="1.3cm" y="25.6cm">Mail:</drawString> - <drawRightString x="7cm" y="25.6cm">[[ company.partner_id.address and company.partner_id.address[0].email ]]</drawRightString> - <lines>1.3cm 25.5cm 7cm 25.5cm</lines> - - <!--page bottom--> - - <lines>1.2cm 2.15cm 19.9cm 2.15cm</lines> - - <drawCentredString x="10.5cm" y="1.7cm">[[ company.rml_footer1 ]]</drawCentredString> - <drawCentredString x="10.5cm" y="1.25cm">[[ company.rml_footer2 ]]</drawCentredString> - <drawCentredString x="10.5cm" y="0.8cm">Contact : [[ user.name ]] - Page: <pageNumber/></drawCentredString> - </pageGraphics> - </pageTemplate> + def _get_header(self,cr,uid,ids): + try : + return tools.file_open('custom/corporate_rml_header.rml').read() + except: + return """ + <header> + <pageTemplate> + <frame id="first" x1="1.3cm" y1="2.5cm" height="23.0cm" width="19cm"/> + <pageGraphics> + <!-- You Logo - Change X,Y,Width and Height --> + <image x="1.3cm" y="27.6cm" height="40.0" >[[company.logo]]</image> + <setFont name="Helvetica" size="8"/> + <fill color="black"/> + <stroke color="black"/> + <lines>1.3cm 27.7cm 20cm 27.7cm</lines> + + <drawRightString x="20cm" y="27.8cm">[[ company.rml_header1 ]]</drawRightString> + + + <drawString x="1.3cm" y="27.2cm">[[ company.partner_id.name ]]</drawString> + <drawString x="1.3cm" y="26.8cm">[[ company.partner_id.address and company.partner_id.address[0].street ]]</drawString> + <drawString x="1.3cm" y="26.4cm">[[ company.partner_id.address and company.partner_id.address[0].zip ]] [[ company.partner_id.address and company.partner_id.address[0].city ]] - [[ company.partner_id.address and company.partner_id.address[0].country_id and company.partner_id.address[0].country_id.name ]]</drawString> + <drawString x="1.3cm" y="26.0cm">Phone:</drawString> + <drawRightString x="7cm" y="26.0cm">[[ company.partner_id.address and company.partner_id.address[0].phone ]]</drawRightString> + <drawString x="1.3cm" y="25.6cm">Mail:</drawString> + <drawRightString x="7cm" y="25.6cm">[[ company.partner_id.address and company.partner_id.address[0].email ]]</drawRightString> + <lines>1.3cm 25.5cm 7cm 25.5cm</lines> + + <!--page bottom--> + + <lines>1.2cm 2.15cm 19.9cm 2.15cm</lines> + + <drawCentredString x="10.5cm" y="1.7cm">[[ company.rml_footer1 ]]</drawCentredString> + <drawCentredString x="10.5cm" y="1.25cm">[[ company.rml_footer2 ]]</drawCentredString> + <drawCentredString x="10.5cm" y="0.8cm">Contact : [[ user.name ]] - Page: <pageNumber/></drawCentredString> + </pageGraphics> + </pageTemplate> </header>""" - _defaults = { - 'currency_id': _get_euro, - 'rml_header':_get_header, - 'rml_header2': _get_header2 - } - - _constraints = [ - (_check_recursion, 'Error! You can not create recursive companies.', ['parent_id']) - ] + _defaults = { + 'currency_id': _get_euro, + 'rml_header':_get_header, + 'rml_header2': _get_header2 + } + + _constraints = [ + (_check_recursion, 'Error! You can not create recursive companies.', ['parent_id']) + ] res_company() diff --git a/bin/addons/base/res/res_currency.py b/bin/addons/base/res/res_currency.py index 30b61e0f0ac..6f6a84cb0d1 100644 --- a/bin/addons/base/res/res_currency.py +++ b/bin/addons/base/res/res_currency.py @@ -39,90 +39,90 @@ import mx.DateTime from mx.DateTime import RelativeDateTime, now, DateTime, localtime class res_currency(osv.osv): - def _current_rate(self, cr, uid, ids, name, arg, context={}): - res={} - if 'date' in context: - date=context['date'] - else: - date=time.strftime('%Y-%m-%d') - for id in ids: - cr.execute("SELECT currency_id, rate FROM res_currency_rate WHERE currency_id = %d AND name <= '%s' ORDER BY name desc LIMIT 1" % (id, date)) - if cr.rowcount: - id, rate=cr.fetchall()[0] - res[id]=rate - else: - res[id]=0 - return res - _name = "res.currency" - _description = "Currency" - _columns = { - 'name': fields.char('Currency', size=32, required=True), - 'code': fields.char('Code', size=3), - 'rate': fields.function(_current_rate, method=True, string='Current rate', digits=(12,6), - help='The rate of the currency to the currency of rate 1'), - 'rate_ids': fields.one2many('res.currency.rate', 'currency_id', 'Rates'), - 'accuracy': fields.integer('Computational Accuracy'), - 'rounding': fields.float('Rounding factor', digits=(12,6)), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': lambda *a: 1, - } - _order = "code" + def _current_rate(self, cr, uid, ids, name, arg, context={}): + res={} + if 'date' in context: + date=context['date'] + else: + date=time.strftime('%Y-%m-%d') + for id in ids: + cr.execute("SELECT currency_id, rate FROM res_currency_rate WHERE currency_id = %d AND name <= '%s' ORDER BY name desc LIMIT 1" % (id, date)) + if cr.rowcount: + id, rate=cr.fetchall()[0] + res[id]=rate + else: + res[id]=0 + return res + _name = "res.currency" + _description = "Currency" + _columns = { + 'name': fields.char('Currency', size=32, required=True), + 'code': fields.char('Code', size=3), + 'rate': fields.function(_current_rate, method=True, string='Current rate', digits=(12,6), + help='The rate of the currency to the currency of rate 1'), + 'rate_ids': fields.one2many('res.currency.rate', 'currency_id', 'Rates'), + 'accuracy': fields.integer('Computational Accuracy'), + 'rounding': fields.float('Rounding factor', digits=(12,6)), + 'active': fields.boolean('Active'), + } + _defaults = { + 'active': lambda *a: 1, + } + _order = "code" - def round(self, cr, uid, currency, amount): - return round(amount / currency.rounding) * currency.rounding + def round(self, cr, uid, currency, amount): + return round(amount / currency.rounding) * currency.rounding - def is_zero(self, cr, uid, currency, amount): - return abs(self.round(cr, uid, currency, amount)) < currency.rounding + def is_zero(self, cr, uid, currency, amount): + return abs(self.round(cr, uid, currency, amount)) < currency.rounding - def compute(self, cr, uid, from_currency_id, to_currency_id, from_amount, round=True, context={}): - if not from_currency_id: - from_currency_id = to_currency_id - xc=self.browse(cr, uid, [from_currency_id,to_currency_id], context=context) - from_currency = (xc[0].id == from_currency_id and xc[0]) or xc[1] - to_currency = (xc[0].id == to_currency_id and xc[0]) or xc[1] - if from_currency['rate'] == 0 or to_currency['rate'] == 0: - date = context.get('date', time.strftime('%Y-%m-%d')) - if from_currency['rate'] == 0: - code = from_currency.code - else: - code = to_currency.code - raise osv.except_osv(_('Error'), _('No rate found \n' \ - 'for the currency: %s \n' \ - 'at the date: %s') % (code, date)) - if to_currency_id==from_currency_id: - if round: - return self.round(cr, uid, to_currency, from_amount) - else: - return from_amount - else: - if round: - return self.round(cr, uid, to_currency, from_amount * to_currency.rate/from_currency.rate) - else: - return (from_amount * to_currency.rate/from_currency.rate) - def name_search(self, cr, uid, name, args=[], operator='ilike', context={}, limit=80): - args2 = args[:] - if name: - args += [('name', operator, name)] - args2 += [('code', operator, name)] - ids = self.search(cr, uid, args, limit=limit) - ids += self.search(cr, uid, args2, limit=limit) - res = self.name_get(cr, uid, ids, context) - return res + def compute(self, cr, uid, from_currency_id, to_currency_id, from_amount, round=True, context={}): + if not from_currency_id: + from_currency_id = to_currency_id + xc=self.browse(cr, uid, [from_currency_id,to_currency_id], context=context) + from_currency = (xc[0].id == from_currency_id and xc[0]) or xc[1] + to_currency = (xc[0].id == to_currency_id and xc[0]) or xc[1] + if from_currency['rate'] == 0 or to_currency['rate'] == 0: + date = context.get('date', time.strftime('%Y-%m-%d')) + if from_currency['rate'] == 0: + code = from_currency.code + else: + code = to_currency.code + raise osv.except_osv(_('Error'), _('No rate found \n' \ + 'for the currency: %s \n' \ + 'at the date: %s') % (code, date)) + if to_currency_id==from_currency_id: + if round: + return self.round(cr, uid, to_currency, from_amount) + else: + return from_amount + else: + if round: + return self.round(cr, uid, to_currency, from_amount * to_currency.rate/from_currency.rate) + else: + return (from_amount * to_currency.rate/from_currency.rate) + def name_search(self, cr, uid, name, args=[], operator='ilike', context={}, limit=80): + args2 = args[:] + if name: + args += [('name', operator, name)] + args2 += [('code', operator, name)] + ids = self.search(cr, uid, args, limit=limit) + ids += self.search(cr, uid, args2, limit=limit) + res = self.name_get(cr, uid, ids, context) + return res res_currency() class res_currency_rate(osv.osv): - _name = "res.currency.rate" - _description = "Currency Rate" - _columns = { - 'name': fields.date('Date', required=True, select=True), - 'rate': fields.float('Rate', digits=(12,6), required=True, - help='The rate of the currency to the currency of rate 1'), - 'currency_id': fields.many2one('res.currency', 'Currency', readonly=True), - } - _defaults = { - 'name': lambda *a: time.strftime('%Y-%m-%d'), - } - _order = "name desc" + _name = "res.currency.rate" + _description = "Currency Rate" + _columns = { + 'name': fields.date('Date', required=True, select=True), + 'rate': fields.float('Rate', digits=(12,6), required=True, + help='The rate of the currency to the currency of rate 1'), + 'currency_id': fields.many2one('res.currency', 'Currency', readonly=True), + } + _defaults = { + 'name': lambda *a: time.strftime('%Y-%m-%d'), + } + _order = "name desc" res_currency_rate() diff --git a/bin/addons/base/res/res_lang.py b/bin/addons/base/res/res_lang.py index 9f3e1dcf5a9..7ef19b65078 100644 --- a/bin/addons/base/res/res_lang.py +++ b/bin/addons/base/res/res_lang.py @@ -30,19 +30,19 @@ from osv import fields, osv class lang(osv.osv): - _name = "res.lang" - _columns = { - 'name': fields.char('Name', size=64, required=True), - 'code': fields.char('Code', size=5, required=True), - 'translatable': fields.boolean('Translatable'), - 'active': fields.boolean('Active'), - 'direction': fields.selection([('ltr', 'Left-to-right'), ('rtl', 'Right-to-left')], 'Direction',resuired=True), - } - _defaults = { - 'active': lambda *a: 1, - 'translatable': lambda *a: 0, - 'direction': lambda *a: 'ltr', - } + _name = "res.lang" + _columns = { + 'name': fields.char('Name', size=64, required=True), + 'code': fields.char('Code', size=5, required=True), + 'translatable': fields.boolean('Translatable'), + 'active': fields.boolean('Active'), + 'direction': fields.selection([('ltr', 'Left-to-right'), ('rtl', 'Right-to-left')], 'Direction',resuired=True), + } + _defaults = { + 'active': lambda *a: 1, + 'translatable': lambda *a: 0, + 'direction': lambda *a: 'ltr', + } lang() diff --git a/bin/addons/base/res/res_request.py b/bin/addons/base/res/res_request.py index 790d0e7624f..539449246cc 100644 --- a/bin/addons/base/res/res_request.py +++ b/bin/addons/base/res/res_request.py @@ -31,97 +31,97 @@ from osv import osv, fields import time def _links_get(self, cr, uid, context={}): - obj = self.pool.get('res.request.link') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['object', 'name'], context) - return [(r['object'], r['name']) for r in res] + obj = self.pool.get('res.request.link') + ids = obj.search(cr, uid, []) + res = obj.read(cr, uid, ids, ['object', 'name'], context) + return [(r['object'], r['name']) for r in res] class res_request(osv.osv): - _name = 'res.request' + _name = 'res.request' - def request_send(self, cr, uid, ids, *args): - for id in ids: - cr.execute('update res_request set state=%s,date_sent=%s where id=%d', ('waiting', time.strftime('%Y-%m-%d %H:%M:%S'), id)) - cr.execute('select act_from,act_to,body,date_sent from res_request where id=%d', (id,)) - values = cr.dictfetchone() - if values['body'] and (len(values['body']) > 128): - values['name'] = values['body'][:125] + '...' - else: - values['name'] = values['body'] or '/' - values['req_id'] = id - self.pool.get('res.request.history').create(cr, uid, values) - return True + def request_send(self, cr, uid, ids, *args): + for id in ids: + cr.execute('update res_request set state=%s,date_sent=%s where id=%d', ('waiting', time.strftime('%Y-%m-%d %H:%M:%S'), id)) + cr.execute('select act_from,act_to,body,date_sent from res_request where id=%d', (id,)) + values = cr.dictfetchone() + if values['body'] and (len(values['body']) > 128): + values['name'] = values['body'][:125] + '...' + else: + values['name'] = values['body'] or '/' + values['req_id'] = id + self.pool.get('res.request.history').create(cr, uid, values) + return True - def request_reply(self, cr, uid, ids, *args): - for id in ids: - cr.execute("update res_request set state='active', act_from=%d, act_to=act_from, trigger_date=NULL, body='' where id=%d", (uid,id)) - return True + def request_reply(self, cr, uid, ids, *args): + for id in ids: + cr.execute("update res_request set state='active', act_from=%d, act_to=act_from, trigger_date=NULL, body='' where id=%d", (uid,id)) + return True - def request_close(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'closed', 'active':False}) - return True + def request_close(self, cr, uid, ids, *args): + self.write(cr, uid, ids, {'state':'closed', 'active':False}) + return True - def request_get(self, cr, uid): - cr.execute('select id from res_request where act_to=%d and (trigger_date<=%s or trigger_date is null) and active=True', (uid,time.strftime('%Y-%m-%d'))) - ids = map(lambda x:x[0], cr.fetchall()) - cr.execute('select id from res_request where act_from=%d and (act_to<>%d) and (trigger_date<=%s or trigger_date is null) and active=True', (uid,uid,time.strftime('%Y-%m-%d'))) - ids2 = map(lambda x:x[0], cr.fetchall()) - return (ids, ids2) + def request_get(self, cr, uid): + cr.execute('select id from res_request where act_to=%d and (trigger_date<=%s or trigger_date is null) and active=True', (uid,time.strftime('%Y-%m-%d'))) + ids = map(lambda x:x[0], cr.fetchall()) + cr.execute('select id from res_request where act_from=%d and (act_to<>%d) and (trigger_date<=%s or trigger_date is null) and active=True', (uid,uid,time.strftime('%Y-%m-%d'))) + ids2 = map(lambda x:x[0], cr.fetchall()) + return (ids, ids2) - _columns = { - 'create_date': fields.datetime('Created date', readonly=True), - 'name': fields.char('Subject', states={'waiting':[('readonly',True)],'active':[('readonly',True)],'closed':[('readonly',True)]}, required=True, size=128), - 'active': fields.boolean('Active'), - 'priority': fields.selection([('0','Low'),('1','Normal'),('2','High')], 'Priority', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}, required=True), - 'act_from': fields.many2one('res.users', 'From', required=True, readonly=True, states={'closed':[('readonly',True)]}), - 'act_to': fields.many2one('res.users', 'To', required=True, states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}), - 'body': fields.text('Request', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}), - 'date_sent': fields.datetime('Date', readonly=True), - 'trigger_date': fields.datetime('Trigger Date', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}), - 'ref_partner_id':fields.many2one('res.partner', 'Partner Ref.', states={'closed':[('readonly',True)]}), - 'ref_doc1':fields.reference('Document Ref 1', selection=_links_get, size=128, states={'closed':[('readonly',True)]}), - 'ref_doc2':fields.reference('Document Ref 2', selection=_links_get, size=128, states={'closed':[('readonly',True)]}), - 'state': fields.selection([('draft','draft'),('waiting','waiting'),('active','active'),('closed','closed')], 'State', required=True, readonly=True), - 'history': fields.one2many('res.request.history','req_id', 'History') - } - _defaults = { - 'act_from': lambda obj,cr,uid,context={}: uid, - 'state': lambda obj,cr,uid,context={}: 'draft', - 'active': lambda obj,cr,uid,context={}: True, - 'priority': lambda obj,cr,uid,context={}: '1', - } - _order = 'priority desc, trigger_date, create_date desc' - _table = 'res_request' + _columns = { + 'create_date': fields.datetime('Created date', readonly=True), + 'name': fields.char('Subject', states={'waiting':[('readonly',True)],'active':[('readonly',True)],'closed':[('readonly',True)]}, required=True, size=128), + 'active': fields.boolean('Active'), + 'priority': fields.selection([('0','Low'),('1','Normal'),('2','High')], 'Priority', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}, required=True), + 'act_from': fields.many2one('res.users', 'From', required=True, readonly=True, states={'closed':[('readonly',True)]}), + 'act_to': fields.many2one('res.users', 'To', required=True, states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}), + 'body': fields.text('Request', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}), + 'date_sent': fields.datetime('Date', readonly=True), + 'trigger_date': fields.datetime('Trigger Date', states={'waiting':[('readonly',True)],'closed':[('readonly',True)]}), + 'ref_partner_id':fields.many2one('res.partner', 'Partner Ref.', states={'closed':[('readonly',True)]}), + 'ref_doc1':fields.reference('Document Ref 1', selection=_links_get, size=128, states={'closed':[('readonly',True)]}), + 'ref_doc2':fields.reference('Document Ref 2', selection=_links_get, size=128, states={'closed':[('readonly',True)]}), + 'state': fields.selection([('draft','draft'),('waiting','waiting'),('active','active'),('closed','closed')], 'State', required=True, readonly=True), + 'history': fields.one2many('res.request.history','req_id', 'History') + } + _defaults = { + 'act_from': lambda obj,cr,uid,context={}: uid, + 'state': lambda obj,cr,uid,context={}: 'draft', + 'active': lambda obj,cr,uid,context={}: True, + 'priority': lambda obj,cr,uid,context={}: '1', + } + _order = 'priority desc, trigger_date, create_date desc' + _table = 'res_request' res_request() class res_request_link(osv.osv): - _name = 'res.request.link' - _columns = { - 'name': fields.char('Name', size=64, required=True, translate=True), - 'object': fields.char('Object', size=64, required=True), - 'priority': fields.integer('Priority'), - } - _defaults = { - 'priority': lambda *a: 5, - } - _order = 'priority' + _name = 'res.request.link' + _columns = { + 'name': fields.char('Name', size=64, required=True, translate=True), + 'object': fields.char('Object', size=64, required=True), + 'priority': fields.integer('Priority'), + } + _defaults = { + 'priority': lambda *a: 5, + } + _order = 'priority' res_request_link() class res_request_history(osv.osv): - _name = 'res.request.history' - _columns = { - 'name': fields.char('Summary', size=128, states={'active':[('readonly',True)],'waiting':[('readonly',True)]}, required=True), - 'req_id': fields.many2one('res.request', 'Request', required=True, ondelete='cascade', select=True), - 'act_from': fields.many2one('res.users', 'From', required=True, readonly=True), - 'act_to': fields.many2one('res.users', 'To', required=True, states={'waiting':[('readonly',True)]}), - 'body': fields.text('Body', states={'waiting':[('readonly',True)]}), - 'date_sent': fields.datetime('Date sent', states={'waiting':[('readonly',True)]}, required=True) - } - _defaults = { - 'name': lambda *a: 'NoName', - 'act_from': lambda obj,cr,uid,context={}: uid, - 'act_to': lambda obj,cr,uid,context={}: uid, - 'date_sent': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), - } + _name = 'res.request.history' + _columns = { + 'name': fields.char('Summary', size=128, states={'active':[('readonly',True)],'waiting':[('readonly',True)]}, required=True), + 'req_id': fields.many2one('res.request', 'Request', required=True, ondelete='cascade', select=True), + 'act_from': fields.many2one('res.users', 'From', required=True, readonly=True), + 'act_to': fields.many2one('res.users', 'To', required=True, states={'waiting':[('readonly',True)]}), + 'body': fields.text('Body', states={'waiting':[('readonly',True)]}), + 'date_sent': fields.datetime('Date sent', states={'waiting':[('readonly',True)]}, required=True) + } + _defaults = { + 'name': lambda *a: 'NoName', + 'act_from': lambda obj,cr,uid,context={}: uid, + 'act_to': lambda obj,cr,uid,context={}: uid, + 'date_sent': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'), + } res_request_history() diff --git a/bin/addons/base/res/res_user.py b/bin/addons/base/res/res_user.py index 24e16e6a03a..4b856dff182 100644 --- a/bin/addons/base/res/res_user.py +++ b/bin/addons/base/res/res_user.py @@ -32,243 +32,243 @@ import tools import pytz class groups(osv.osv): - _name = "res.groups" - _columns = { - 'name': fields.char('Group Name', size=64, required=True), - 'model_access': fields.one2many('ir.model.access', 'group_id', 'Access Controls'), - 'rule_groups': fields.many2many('ir.rule.group', 'group_rule_group_rel', - 'group_id', 'rule_group_id', 'Rules', domain="[('global', '<>', True)]"), - 'menu_access': fields.many2many('ir.ui.menu', 'ir_ui_menu_group_rel', 'gid', 'menu_id', 'Access Menu'), - 'comment' : fields.text('Comment',size=250), - } - _sql_constraints = [ - ('name_uniq', 'unique (name)', 'The name of the group must be unique !') - ] + _name = "res.groups" + _columns = { + 'name': fields.char('Group Name', size=64, required=True), + 'model_access': fields.one2many('ir.model.access', 'group_id', 'Access Controls'), + 'rule_groups': fields.many2many('ir.rule.group', 'group_rule_group_rel', + 'group_id', 'rule_group_id', 'Rules', domain="[('global', '<>', True)]"), + 'menu_access': fields.many2many('ir.ui.menu', 'ir_ui_menu_group_rel', 'gid', 'menu_id', 'Access Menu'), + 'comment' : fields.text('Comment',size=250), + } + _sql_constraints = [ + ('name_uniq', 'unique (name)', 'The name of the group must be unique !') + ] - def write(self, cr, uid, ids, vals, context=None): - if 'name' in vals: - if vals['name'].startswith('-'): - raise osv.except_osv(_('Error'), - _('The name of the group can not start with "-"')) - res = super(groups, self).write(cr, uid, ids, vals, context=context) - # Restart the cache on the company_get method - self.pool.get('ir.rule').domain_get() - return res + def write(self, cr, uid, ids, vals, context=None): + if 'name' in vals: + if vals['name'].startswith('-'): + raise osv.except_osv(_('Error'), + _('The name of the group can not start with "-"')) + res = super(groups, self).write(cr, uid, ids, vals, context=context) + # Restart the cache on the company_get method + self.pool.get('ir.rule').domain_get() + return res - def create(self, cr, uid, vals, context=None): - if 'name' in vals: - if vals['name'].startswith('-'): - raise osv.except_osv(_('Error'), - _('The name of the group can not start with "-"')) - return super(groups, self).create(cr, uid, vals, context=context) + def create(self, cr, uid, vals, context=None): + if 'name' in vals: + if vals['name'].startswith('-'): + raise osv.except_osv(_('Error'), + _('The name of the group can not start with "-"')) + return super(groups, self).create(cr, uid, vals, context=context) groups() class roles(osv.osv): - _name = "res.roles" - _columns = { - 'name': fields.char('Role Name', size=64, required=True), - 'parent_id': fields.many2one('res.roles', 'Parent', select=True), - 'child_id': fields.one2many('res.roles', 'parent_id', 'Childs') - } - _defaults = { - } - def check(self, cr, uid, ids, role_id): - if role_id in ids: - return True - cr.execute('select parent_id from res_roles where id=%d', (role_id,)) - roles = cr.fetchone()[0] - if roles: - return self.check(cr, uid, ids, roles) - return False + _name = "res.roles" + _columns = { + 'name': fields.char('Role Name', size=64, required=True), + 'parent_id': fields.many2one('res.roles', 'Parent', select=True), + 'child_id': fields.one2many('res.roles', 'parent_id', 'Childs') + } + _defaults = { + } + def check(self, cr, uid, ids, role_id): + if role_id in ids: + return True + cr.execute('select parent_id from res_roles where id=%d', (role_id,)) + roles = cr.fetchone()[0] + if roles: + return self.check(cr, uid, ids, roles) + return False roles() def _lang_get(self, cr, uid, context={}): - obj = self.pool.get('res.lang') - ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, ids, ['code', 'name'], context) - res = [(r['code'], r['name']) for r in res] - return res + obj = self.pool.get('res.lang') + ids = obj.search(cr, uid, []) + res = obj.read(cr, uid, ids, ['code', 'name'], context) + res = [(r['code'], r['name']) for r in res] + return res def _tz_get(self,cr,uid, context={}): - return [(x, x) for x in pytz.all_timezones] + return [(x, x) for x in pytz.all_timezones] class users(osv.osv): - _name = "res.users" - _log_access = False - _columns = { - 'name': fields.char('Name', size=64, required=True, select=True), - 'login': fields.char('Login', size=64, required=True), - 'password': fields.char('Password', size=64, invisible=True), - 'signature': fields.text('Signature', size=64), - 'address_id': fields.many2one('res.partner.address', 'Address'), - 'active': fields.boolean('Active'), - 'action_id': fields.many2one('ir.actions.actions', 'Home Action'), - 'menu_id': fields.many2one('ir.actions.actions', 'Menu Action'), - 'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'), - 'roles_id': fields.many2many('res.roles', 'res_roles_users_rel', 'uid', 'rid', 'Roles'), - 'company_id': fields.many2one('res.company', 'Company'), - 'context_lang': fields.selection(_lang_get, 'Language', required=True), - 'context_tz': fields.selection(_tz_get, 'Timezone', size=64) - } - def read(self,cr, uid, ids, fields=None, context=None, load='_classic_read'): - result = super(users, self).read(cr, uid, ids, fields, context, load) - canwrite = self.pool.get('ir.model.access').check(cr, uid, 'res.users', 'write', raise_exception=False) - if not canwrite: - for r in result: - if 'password' in r: - r['password'] = '********' - return result + _name = "res.users" + _log_access = False + _columns = { + 'name': fields.char('Name', size=64, required=True, select=True), + 'login': fields.char('Login', size=64, required=True), + 'password': fields.char('Password', size=64, invisible=True), + 'signature': fields.text('Signature', size=64), + 'address_id': fields.many2one('res.partner.address', 'Address'), + 'active': fields.boolean('Active'), + 'action_id': fields.many2one('ir.actions.actions', 'Home Action'), + 'menu_id': fields.many2one('ir.actions.actions', 'Menu Action'), + 'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'), + 'roles_id': fields.many2many('res.roles', 'res_roles_users_rel', 'uid', 'rid', 'Roles'), + 'company_id': fields.many2one('res.company', 'Company'), + 'context_lang': fields.selection(_lang_get, 'Language', required=True), + 'context_tz': fields.selection(_tz_get, 'Timezone', size=64) + } + def read(self,cr, uid, ids, fields=None, context=None, load='_classic_read'): + result = super(users, self).read(cr, uid, ids, fields, context, load) + canwrite = self.pool.get('ir.model.access').check(cr, uid, 'res.users', 'write', raise_exception=False) + if not canwrite: + for r in result: + if 'password' in r: + r['password'] = '********' + return result - _sql_constraints = [ - ('login_key', 'UNIQUE (login)', 'You can not have two users with the same login !') - ] - def _get_action(self,cr, uid, context={}): - ids = self.pool.get('ir.ui.menu').search(cr, uid, [('usage','=','menu')]) - return ids and ids[0] or False + _sql_constraints = [ + ('login_key', 'UNIQUE (login)', 'You can not have two users with the same login !') + ] + def _get_action(self,cr, uid, context={}): + ids = self.pool.get('ir.ui.menu').search(cr, uid, [('usage','=','menu')]) + return ids and ids[0] or False - def _get_company(self,cr, uid, context={}): - return self.pool.get('res.users').browse(cr, uid, uid, context).company_id.id + def _get_company(self,cr, uid, context={}): + return self.pool.get('res.users').browse(cr, uid, uid, context).company_id.id - def _get_menu(self,cr, uid, context={}): - ids = self.pool.get('ir.actions.act_window').search(cr, uid, [('usage','=','menu')]) - return ids and ids[0] or False + def _get_menu(self,cr, uid, context={}): + ids = self.pool.get('ir.actions.act_window').search(cr, uid, [('usage','=','menu')]) + return ids and ids[0] or False - _defaults = { - 'password' : lambda obj,cr,uid,context={} : '', - 'context_lang': lambda *args: 'en_US', - 'active' : lambda obj,cr,uid,context={} : True, - 'menu_id': _get_menu, - 'action_id': _get_menu, - 'company_id': _get_company, - } - def company_get(self, cr, uid, uid2): - company_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.id - return company_id - company_get = tools.cache()(company_get) + _defaults = { + 'password' : lambda obj,cr,uid,context={} : '', + 'context_lang': lambda *args: 'en_US', + 'active' : lambda obj,cr,uid,context={} : True, + 'menu_id': _get_menu, + 'action_id': _get_menu, + 'company_id': _get_company, + } + def company_get(self, cr, uid, uid2): + company_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.id + return company_id + company_get = tools.cache()(company_get) - def write(self, cr, uid, ids, values, *args, **argv): - if (ids == [uid]): - ok = True - for k in values.keys(): - if k not in ('password','signature','action_id', 'context_lang', 'context_tz'): - ok=False - if ok: - uid = 1 - res = super(users, self).write(cr, uid, ids, values, *args, **argv) - self.company_get() - # Restart the cache on the company_get method - self.pool.get('ir.rule').domain_get() - return res + def write(self, cr, uid, ids, values, *args, **argv): + if (ids == [uid]): + ok = True + for k in values.keys(): + if k not in ('password','signature','action_id', 'context_lang', 'context_tz'): + ok=False + if ok: + uid = 1 + res = super(users, self).write(cr, uid, ids, values, *args, **argv) + self.company_get() + # Restart the cache on the company_get method + self.pool.get('ir.rule').domain_get() + return res - def unlink(self, cr, uid, ids): - if 1 in ids: - raise osv.except_osv(_('Can not remove root user!'), _('You can not remove the root user as it is used internally for resources created by Tiny ERP (updates, module installation, ...)')) - return super(users, self).unlink(cr, uid, ids) + def unlink(self, cr, uid, ids): + if 1 in ids: + raise osv.except_osv(_('Can not remove root user!'), _('You can not remove the root user as it is used internally for resources created by Tiny ERP (updates, module installation, ...)')) + return super(users, self).unlink(cr, uid, ids) - def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - ids = [] - if name: - ids = self.search(cr, user, [('login','=',name)]+ args, limit=limit) - if not ids: - ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit) - return self.name_get(cr, user, ids) + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + ids = [] + if name: + ids = self.search(cr, user, [('login','=',name)]+ args, limit=limit) + if not ids: + ids = self.search(cr, user, [('name',operator,name)]+ args, limit=limit) + return self.name_get(cr, user, ids) - def copy(self, cr, uid, id, default=None, context={}): - login = self.read(cr, uid, [id], ['login'])[0]['login'] - default.update({'login': login+' (copy)'}) - return super(users, self).copy(cr, uid, id, default, context) + def copy(self, cr, uid, id, default=None, context={}): + login = self.read(cr, uid, [id], ['login'])[0]['login'] + default.update({'login': login+' (copy)'}) + return super(users, self).copy(cr, uid, id, default, context) - def context_get(self, cr, uid, context={}): - user = self.browse(cr, uid, uid, context) - result = {} - for k in self._columns.keys(): - if k.startswith('context_'): - result[k[8:]] = getattr(user,k) - return result + def context_get(self, cr, uid, context={}): + user = self.browse(cr, uid, uid, context) + result = {} + for k in self._columns.keys(): + if k.startswith('context_'): + result[k[8:]] = getattr(user,k) + return result - def action_get(self, cr, uid, context={}): - dataobj = self.pool.get('ir.model.data') - data_id = dataobj._get_id(cr, 1, 'base', 'action_res_users_my') - return dataobj.browse(cr, uid, data_id, context).res_id + def action_get(self, cr, uid, context={}): + dataobj = self.pool.get('ir.model.data') + data_id = dataobj._get_id(cr, 1, 'base', 'action_res_users_my') + return dataobj.browse(cr, uid, data_id, context).res_id - def action_next(self,cr,uid,ids,context=None): - return{ - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', - } + def action_next(self,cr,uid,ids,context=None): + return{ + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', + } - def action_continue(self,cr,uid,ids,context={}): - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', - } - def action_new(self,cr,uid,ids,context={}): - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'res.users', - 'view_id':self.pool.get('ir.ui.view').search(cr,uid,[('name','=','res.users.confirm.form')]), - 'type': 'ir.actions.act_window', - 'target':'new', - } + def action_continue(self,cr,uid,ids,context={}): + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', + } + def action_new(self,cr,uid,ids,context={}): + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'res.users', + 'view_id':self.pool.get('ir.ui.view').search(cr,uid,[('name','=','res.users.confirm.form')]), + 'type': 'ir.actions.act_window', + 'target':'new', + } users() class groups2(osv.osv): - _inherit = 'res.groups' - _columns = { - 'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users'), - } + _inherit = 'res.groups' + _columns = { + 'users': fields.many2many('res.users', 'res_groups_users_rel', 'gid', 'uid', 'Users'), + } groups2() class res_config_view(osv.osv_memory): - _name='res.config.view' - _columns = { - 'name':fields.char('Name', size=64), - 'view': fields.selection([('simple','Simple'),('extended','Extended')], 'View', required=True ), + _name='res.config.view' + _columns = { + 'name':fields.char('Name', size=64), + 'view': fields.selection([('simple','Simple'),('extended','Extended')], 'View', required=True ), } - _defaults={ - 'view':lambda *args: 'simple', - } + _defaults={ + 'view':lambda *args: 'simple', + } - def action_cancel(self,cr,uid,ids,conect=None): - print ' Cancel action' - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', + def action_cancel(self,cr,uid,ids,conect=None): + print ' Cancel action' + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', } - def action_set(self, cr, uid, ids, context=None): - res=self.read(cr,uid,ids)[0] - users_obj = self.pool.get('res.users') - group_obj=self.pool.get('res.groups') - if 'view' in res and res['view'] and res['view']=='extended': - group_ids=group_obj.search(cr,uid,[('name','=','Extended View')]) - if group_ids and len(group_ids): - users_obj.write(cr, uid, [3],{ - 'groups_id':[(4,group_ids[0])] - }, context=context) - return { - 'view_type': 'form', - "view_mode": 'form', - 'res_model': 'ir.module.module.configuration.wizard', - 'type': 'ir.actions.act_window', - 'target':'new', - } + def action_set(self, cr, uid, ids, context=None): + res=self.read(cr,uid,ids)[0] + users_obj = self.pool.get('res.users') + group_obj=self.pool.get('res.groups') + if 'view' in res and res['view'] and res['view']=='extended': + group_ids=group_obj.search(cr,uid,[('name','=','Extended View')]) + if group_ids and len(group_ids): + users_obj.write(cr, uid, [3],{ + 'groups_id':[(4,group_ids[0])] + }, context=context) + return { + 'view_type': 'form', + "view_mode": 'form', + 'res_model': 'ir.module.module.configuration.wizard', + 'type': 'ir.actions.act_window', + 'target':'new', + } res_config_view() diff --git a/bin/addons/module_graph.py b/bin/addons/module_graph.py index 6df397ac8af..a853eb8aa1f 100644 --- a/bin/addons/module_graph.py +++ b/bin/addons/module_graph.py @@ -45,13 +45,13 @@ done = [] print 'digraph G {' while len(modules): - f = modules.pop(0) - done.append(f) - if os.path.isfile(os.path.join(f,"__terp__.py")): - info=eval(file(os.path.join(f,"__terp__.py")).read()) - if info.get('installable', True): - for name in info['depends']: - if name not in done+modules: - modules.append(name) - print '\t%s -> %s;' % (f, name) + f = modules.pop(0) + done.append(f) + if os.path.isfile(os.path.join(f,"__terp__.py")): + info=eval(file(os.path.join(f,"__terp__.py")).read()) + if info.get('installable', True): + for name in info['depends']: + if name not in done+modules: + modules.append(name) + print '\t%s -> %s;' % (f, name) print '}' diff --git a/bin/ir/ir.py b/bin/ir/ir.py index 1c7854bb39d..d745ee3b33a 100644 --- a/bin/ir/ir.py +++ b/bin/ir/ir.py @@ -31,14 +31,14 @@ import osv import 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) + obj = pooler.get_pool(cr.dbname).get('ir.values') + return 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]) + obj = pooler.get_pool(cr.dbname).get('ir.values') + return obj.unlink(cr, uid, [id]) def ir_get(cr, uid, key, key2, models, meta=False, context={}, 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) - return res + 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) + return res diff --git a/bin/netsvc.py b/bin/netsvc.py index d014288a9e7..b6cc5d06ff9 100644 --- a/bin/netsvc.py +++ b/bin/netsvc.py @@ -42,97 +42,97 @@ _res_id=1 _res={} class ServiceEndPointCall(object): - def __init__(self,id,method): - self._id=id - self._meth=method - def __call__(self,*args): - _res[self._id]=self._meth(*args) - return self._id + def __init__(self,id,method): + self._id=id + self._meth=method + def __call__(self,*args): + _res[self._id]=self._meth(*args) + return self._id class ServiceEndPoint(object): - def __init__(self, name, id): - self._id = id - self._meth={} - s=_service[name] - for m in s._method: - self._meth[m]=s._method[m] - def __getattr__(self, name): - return ServiceEndPointCall(self._id, self._meth[name]) + def __init__(self, name, id): + self._id = id + self._meth={} + s=_service[name] + for m in s._method: + self._meth[m]=s._method[m] + def __getattr__(self, name): + return ServiceEndPointCall(self._id, self._meth[name]) class Service(object): - _serviceEndPointID = 0 - def __init__(self, name, audience=''): - _service[name]=self - self.__name=name - self._method={} - self.exportedMethods=None - self._response_process=None - self._response_process_id=None - self._response=None - - def joinGroup(self,name): - if not name in _group: - _group[name]={} - _group[name][self.__name]=self - - def exportMethod(self, m): - if callable(m): - self._method[m.__name__]=m - - def serviceEndPoint(self,s): - if Service._serviceEndPointID >= 2**16: - Service._serviceEndPointID = 0 - Service._serviceEndPointID += 1 - return ServiceEndPoint(s, self._serviceEndPointID) - - def conversationId(self): - return 1 - - def processResponse(self,s,id): - self._response_process, self._response_process_id = s, id - - def processFailure(self,s,id): - pass - - def resumeResponse(self,s): - pass - - def cancelResponse(self,s): - pass - - def suspendResponse(self,s): - if self._response_process: - self._response_process(self._response_process_id, - _res[self._response_process_id]) - self._response_process=None - self._response=s(self._response_process_id) - - def abortResponse(self, error, description, origin, details): - import tools - if not tools.config['debug_mode']: - raise Exception("%s -- %s\n\n%s"%(origin,description,details)) - else: - raise - - def currentFailure(self,s): - pass + _serviceEndPointID = 0 + def __init__(self, name, audience=''): + _service[name]=self + self.__name=name + self._method={} + self.exportedMethods=None + self._response_process=None + self._response_process_id=None + self._response=None + + def joinGroup(self,name): + if not name in _group: + _group[name]={} + _group[name][self.__name]=self + + def exportMethod(self, m): + if callable(m): + self._method[m.__name__]=m + + def serviceEndPoint(self,s): + if Service._serviceEndPointID >= 2**16: + Service._serviceEndPointID = 0 + Service._serviceEndPointID += 1 + return ServiceEndPoint(s, self._serviceEndPointID) + + def conversationId(self): + return 1 + + def processResponse(self,s,id): + self._response_process, self._response_process_id = s, id + + def processFailure(self,s,id): + pass + + def resumeResponse(self,s): + pass + + def cancelResponse(self,s): + pass + + def suspendResponse(self,s): + if self._response_process: + self._response_process(self._response_process_id, + _res[self._response_process_id]) + self._response_process=None + self._response=s(self._response_process_id) + + def abortResponse(self, error, description, origin, details): + import tools + if not tools.config['debug_mode']: + raise Exception("%s -- %s\n\n%s"%(origin,description,details)) + else: + raise + + def currentFailure(self,s): + pass class LocalService(Service): - def __init__(self, name): - self.__name=name - s=_service[name] - self._service=s - for m in s._method: - setattr(self,m,s._method[m]) + def __init__(self, name): + self.__name=name + s=_service[name] + self._service=s + for m in s._method: + setattr(self,m,s._method[m]) class ServiceUnavailable(Exception): - pass + pass def service_exist(name): - return (name in _service) and bool(_service[name]) + return (name in _service) and bool(_service[name]) def get_rpc_paths(): - return map(lambda s: '/xmlrpc/%s' % s, _service) + return map(lambda s: '/xmlrpc/%s' % s, _service) LOG_DEBUG='debug' LOG_INFO='info' @@ -141,269 +141,269 @@ LOG_ERROR='error' LOG_CRITICAL='critical' def init_logger(): - from tools import config - import os - - if config['logfile']: - logf = config['logfile'] - # test if the directories exist, else create them - try: - if not os.path.exists(os.path.dirname(logf)): - os.makedirs(os.path.dirname(logf)) - try: - fd = open(logf, 'a') - handler = logging.StreamHandler(fd) - except IOError: - sys.stderr.write("ERROR: couldn't open the logfile\n") - handler = logging.StreamHandler(sys.stdout) - except OSError: - sys.stderr.write("ERROR: couldn't create the logfile directory\n") - handler = logging.StreamHandler(sys.stdout) - else: - handler = logging.StreamHandler(sys.stdout) - - # create a format for log messages and dates - formatter = logging.Formatter('[%(asctime)s] %(levelname)s:%(name)s:%(message)s', '%a %b %d %H:%M:%S %Y') - - # tell the handler to use this format - handler.setFormatter(formatter) - - # add the handler to the root logger - logging.getLogger().addHandler(handler) - logging.getLogger().setLevel(logging.INFO) + from tools import config + import os + + if config['logfile']: + logf = config['logfile'] + # test if the directories exist, else create them + try: + if not os.path.exists(os.path.dirname(logf)): + os.makedirs(os.path.dirname(logf)) + try: + fd = open(logf, 'a') + handler = logging.StreamHandler(fd) + except IOError: + sys.stderr.write("ERROR: couldn't open the logfile\n") + handler = logging.StreamHandler(sys.stdout) + except OSError: + sys.stderr.write("ERROR: couldn't create the logfile directory\n") + handler = logging.StreamHandler(sys.stdout) + else: + handler = logging.StreamHandler(sys.stdout) + + # create a format for log messages and dates + formatter = logging.Formatter('[%(asctime)s] %(levelname)s:%(name)s:%(message)s', '%a %b %d %H:%M:%S %Y') + + # tell the handler to use this format + handler.setFormatter(formatter) + + # add the handler to the root logger + logging.getLogger().addHandler(handler) + logging.getLogger().setLevel(logging.INFO) class Logger(object): - def notifyChannel(self,name,level,msg): - log = logging.getLogger(name) - getattr(log,level)(msg) + def notifyChannel(self,name,level,msg): + log = logging.getLogger(name) + getattr(log,level)(msg) class Agent(object): - _timers = [] - _logger = Logger() - - def setAlarm(self, fn, dt, args=None, kwargs=None): - if not args: - args=[] - if not kwargs: - kwargs={} - wait = dt - time.time() - if wait > 0: - self._logger.notifyChannel('timers', LOG_DEBUG, "Job scheduled in %s seconds for %s.%s" % (wait, fn.im_class.__name__, fn.func_name)) - timer = threading.Timer(wait, fn, args, kwargs) - timer.start() - self._timers.append(timer) - for timer in self._timers[:]: - if not timer.isAlive(): - self._timers.remove(timer) - - def quit(cls): - for timer in cls._timers: - timer.cancel() - quit=classmethod(quit) + _timers = [] + _logger = Logger() + + def setAlarm(self, fn, dt, args=None, kwargs=None): + if not args: + args=[] + if not kwargs: + kwargs={} + wait = dt - time.time() + if wait > 0: + self._logger.notifyChannel('timers', LOG_DEBUG, "Job scheduled in %s seconds for %s.%s" % (wait, fn.im_class.__name__, fn.func_name)) + timer = threading.Timer(wait, fn, args, kwargs) + timer.start() + self._timers.append(timer) + for timer in self._timers[:]: + if not timer.isAlive(): + self._timers.remove(timer) + + def quit(cls): + for timer in cls._timers: + timer.cancel() + quit=classmethod(quit) class RpcGateway(object): - def __init__(self, name): - self.name=name + def __init__(self, name): + self.name=name class Dispatcher(object): - def __init__(self): - pass - def monitor(self,signal): - pass - def run(self): - pass + def __init__(self): + pass + def monitor(self,signal): + pass + def run(self): + pass class xmlrpc(object): - class RpcGateway(object): - def __init__(self, name): - self.name=name + class RpcGateway(object): + def __init__(self, name): + self.name=name class GenericXMLRPCRequestHandler: - def _dispatch(self, method, params): - import traceback - try: - n=self.path.split("/")[-1] - s=LocalService(n) - m=getattr(s,method) - s._service._response=None - r=m(*params) - res=s._service._response - if res!=None: - r=res - return r - except Exception, e: - tb_s = reduce(lambda x, y: x+y, traceback.format_exception( - sys.exc_type, sys.exc_value, sys.exc_traceback)) - s=str(e) - import tools - if tools.config['debug_mode']: - import pdb - tb = sys.exc_info()[2] - pdb.post_mortem(tb) - raise xmlrpclib.Fault(s, tb_s) + def _dispatch(self, method, params): + import traceback + try: + n=self.path.split("/")[-1] + s=LocalService(n) + m=getattr(s,method) + s._service._response=None + r=m(*params) + res=s._service._response + if res!=None: + r=res + return r + except Exception, e: + tb_s = reduce(lambda x, y: x+y, traceback.format_exception( + sys.exc_type, sys.exc_value, sys.exc_traceback)) + s=str(e) + import tools + if tools.config['debug_mode']: + import pdb + tb = sys.exc_info()[2] + pdb.post_mortem(tb) + raise xmlrpclib.Fault(s, tb_s) class SimpleXMLRPCRequestHandler(GenericXMLRPCRequestHandler, - SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): - SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.rpc_paths = get_rpc_paths() + SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): + SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.rpc_paths = get_rpc_paths() class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn, - SimpleXMLRPCServer.SimpleXMLRPCServer): + SimpleXMLRPCServer.SimpleXMLRPCServer): - def server_bind(self): - self.socket.setsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR, 1) - SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self) + def server_bind(self): + self.socket.setsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR, 1) + SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self) class HttpDaemon(threading.Thread): - def __init__(self, interface,port, secure=False): - threading.Thread.__init__(self) - self.__port=port - self.__interface=interface - self.secure = secure - if secure: - from ssl import SecureXMLRPCServer - class SecureXMLRPCRequestHandler(GenericXMLRPCRequestHandler, - SecureXMLRPCServer.SecureXMLRPCRequestHandler): - SecureXMLRPCServer.SecureXMLRPCRequestHandler.rpc_paths = get_rpc_paths() - class SecureThreadedXMLRPCServer(SocketServer.ThreadingMixIn, - SecureXMLRPCServer.SecureXMLRPCServer): - - def server_bind(self): - self.socket.setsockopt(socket.SOL_SOCKET, - socket.SO_REUSEADDR, 1) - SecureXMLRPCServer.SecureXMLRPCServer.server_bind(self) - - self.server = SecureThreadedXMLRPCServer((interface, port), - SecureXMLRPCRequestHandler,0) - else: - self.server = SimpleThreadedXMLRPCServer((interface, port), - SimpleXMLRPCRequestHandler,0) - - def attach(self,path,gw): - pass - - def stop(self): - self.running = False - if os.name <> 'nt': - if hasattr(socket, 'SHUT_RDWR'): - if self.secure: - self.server.socket.sock_shutdown(socket.SHUT_RDWR) - else: - self.server.socket.shutdown(socket.SHUT_RDWR) - else: - if self.secure: - self.server.socket.sock_shutdown(2) - else: - self.server.socket.shutdown(2) - self.server.socket.close() - - def run(self): - self.server.register_introspection_functions() - - self.running = True - while self.running: - self.server.handle_request() - return True - - # If the server need to be run recursively - # - #signal.signal(signal.SIGALRM, self.my_handler) - #signal.alarm(6) - #while True: - # self.server.handle_request() - #signal.alarm(0) # Disable the alarm + def __init__(self, interface,port, secure=False): + threading.Thread.__init__(self) + self.__port=port + self.__interface=interface + self.secure = secure + if secure: + from ssl import SecureXMLRPCServer + class SecureXMLRPCRequestHandler(GenericXMLRPCRequestHandler, + SecureXMLRPCServer.SecureXMLRPCRequestHandler): + SecureXMLRPCServer.SecureXMLRPCRequestHandler.rpc_paths = get_rpc_paths() + class SecureThreadedXMLRPCServer(SocketServer.ThreadingMixIn, + SecureXMLRPCServer.SecureXMLRPCServer): + + def server_bind(self): + self.socket.setsockopt(socket.SOL_SOCKET, + socket.SO_REUSEADDR, 1) + SecureXMLRPCServer.SecureXMLRPCServer.server_bind(self) + + self.server = SecureThreadedXMLRPCServer((interface, port), + SecureXMLRPCRequestHandler,0) + else: + self.server = SimpleThreadedXMLRPCServer((interface, port), + SimpleXMLRPCRequestHandler,0) + + def attach(self,path,gw): + pass + + def stop(self): + self.running = False + if os.name <> 'nt': + if hasattr(socket, 'SHUT_RDWR'): + if self.secure: + self.server.socket.sock_shutdown(socket.SHUT_RDWR) + else: + self.server.socket.shutdown(socket.SHUT_RDWR) + else: + if self.secure: + self.server.socket.sock_shutdown(2) + else: + self.server.socket.shutdown(2) + self.server.socket.close() + + def run(self): + self.server.register_introspection_functions() + + self.running = True + while self.running: + self.server.handle_request() + return True + + # If the server need to be run recursively + # + #signal.signal(signal.SIGALRM, self.my_handler) + #signal.alarm(6) + #while True: + # self.server.handle_request() + #signal.alarm(0) # Disable the alarm import tiny_socket class TinySocketClientThread(threading.Thread): - def __init__(self, sock, threads): - threading.Thread.__init__(self) - self.sock = sock - self.threads = threads - - def run(self): - import traceback - import time - import select - self.running = True - try: - ts = tiny_socket.mysocket(self.sock) - except: - self.sock.close() - self.threads.remove(self) - return False - while self.running: - try: - msg = ts.myreceive() - except: - self.sock.close() - self.threads.remove(self) - return False - try: - s=LocalService(msg[0]) - m=getattr(s,msg[1]) - s._service._response=None - r=m(*msg[2:]) - res=s._service._response - if res!=None: - r=res - ts.mysend(r) - except Exception, e: - tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) - s=str(e) - import tools - if tools.config['debug_mode']: - import pdb - tb = sys.exc_info()[2] - pdb.post_mortem(tb) - ts.mysend(e, exception=True, traceback=tb_s) - except: - pass - self.sock.close() - self.threads.remove(self) - return True - - def stop(self): - self.running = False + def __init__(self, sock, threads): + threading.Thread.__init__(self) + self.sock = sock + self.threads = threads + + def run(self): + import traceback + import time + import select + self.running = True + try: + ts = tiny_socket.mysocket(self.sock) + except: + self.sock.close() + self.threads.remove(self) + return False + while self.running: + try: + msg = ts.myreceive() + except: + self.sock.close() + self.threads.remove(self) + return False + try: + s=LocalService(msg[0]) + m=getattr(s,msg[1]) + s._service._response=None + r=m(*msg[2:]) + res=s._service._response + if res!=None: + r=res + ts.mysend(r) + except Exception, e: + tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + s=str(e) + import tools + if tools.config['debug_mode']: + import pdb + tb = sys.exc_info()[2] + pdb.post_mortem(tb) + ts.mysend(e, exception=True, traceback=tb_s) + except: + pass + self.sock.close() + self.threads.remove(self) + return True + + def stop(self): + self.running = False class TinySocketServerThread(threading.Thread): - def __init__(self, interface, port, secure=False): - threading.Thread.__init__(self) - self.__port=port - self.__interface=interface - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.socket.bind((self.__interface, self.__port)) - self.socket.listen(5) - self.threads = [] - - def run(self): - import select - try: - self.running = True - while self.running: - (clientsocket, address) = self.socket.accept() - ct = TinySocketClientThread(clientsocket, self.threads) - self.threads.append(ct) - ct.start() - self.socket.close() - except Exception, e: - self.socket.close() - return False - - def stop(self): - self.running=False - for t in self.threads: - t.stop() - try: - if hasattr(socket, 'SHUT_RDWR'): - self.socket.shutdown(socket.SHUT_RDWR) - else: - self.socket.shutdown(2) - self.socket.close() - except: - return False + def __init__(self, interface, port, secure=False): + threading.Thread.__init__(self) + self.__port=port + self.__interface=interface + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.socket.bind((self.__interface, self.__port)) + self.socket.listen(5) + self.threads = [] + + def run(self): + import select + try: + self.running = True + while self.running: + (clientsocket, address) = self.socket.accept() + ct = TinySocketClientThread(clientsocket, self.threads) + self.threads.append(ct) + ct.start() + self.socket.close() + except Exception, e: + self.socket.close() + return False + + def stop(self): + self.running=False + for t in self.threads: + t.stop() + try: + if hasattr(socket, 'SHUT_RDWR'): + self.socket.shutdown(socket.SHUT_RDWR) + else: + self.socket.shutdown(2) + self.socket.close() + except: + return False # vim:noexpandtab: diff --git a/bin/osv/fields.py b/bin/osv/fields.py index 8f6ee185ad8..ab10cf9ade5 100644 --- a/bin/osv/fields.py +++ b/bin/osv/fields.py @@ -49,149 +49,149 @@ import warnings import tools def _symbol_set(symb): - if symb==None or symb==False: - return None - elif isinstance(symb, unicode): - return symb.encode('utf-8') - return str(symb) + if symb==None or symb==False: + return None + elif isinstance(symb, unicode): + return symb.encode('utf-8') + return str(symb) class _column(object): - _classic_read = True - _classic_write = True - _properties = False - _type = 'unknown' - _obj = None - _symbol_c = '%s' - _symbol_f = _symbol_set - _symbol_set = (_symbol_c, _symbol_f) - _symbol_get = None - - def __init__(self, string='unknown', required=False, readonly=False, domain=None, context='', states=None, priority=0, change_default=False, size=None, ondelete="set null", translate=False, select=False, **args): - self.states = states or {} - self.string = string - self.readonly = readonly - self.required = required - self.size = size - self.help = args.get('help', '') - self.priority = priority - self.change_default = change_default - self.ondelete = ondelete - self.translate = translate - self._domain = domain or [] - self.relate =False - self._context = context - self.group_name = False - self.write = False - self.read = False - self.view_load = 0 - self.select=select - for a in args: - if args[a]: - setattr(self, a, args[a]) - if self.relate: - warnings.warn("The relate attribute doesn't work anymore, use act_window tag instead", DeprecationWarning) - - def restart(self): - pass - - def set(self, cr, obj, id, name, value, user=None, context=None): - cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%d', (self._symbol_set[1](value),id) ) - def set_memory(self, cr, obj, id, name, value, user=None, context=None): - raise Exception, _('Not implemented set_memory method !') - - def get_memory(self, cr, obj, ids, name, context=None, values=None): - raise Exception, _('Not implemented get_memory method !') - def get(self, cr, obj, ids, name, context=None, values=None): - raise Exception, _('undefined get method !') - - def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): - ids = obj.search(cr, uid, args+self._domain+[(name,'ilike',value)], offset, limit) - res = obj.read(cr, uid, ids, [name]) - return [x[name] for x in res] - def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None): - raise Exception, _('Not implemented search_memory method !') + _classic_read = True + _classic_write = True + _properties = False + _type = 'unknown' + _obj = None + _symbol_c = '%s' + _symbol_f = _symbol_set + _symbol_set = (_symbol_c, _symbol_f) + _symbol_get = None + + def __init__(self, string='unknown', required=False, readonly=False, domain=None, context='', states=None, priority=0, change_default=False, size=None, ondelete="set null", translate=False, select=False, **args): + self.states = states or {} + self.string = string + self.readonly = readonly + self.required = required + self.size = size + self.help = args.get('help', '') + self.priority = priority + self.change_default = change_default + self.ondelete = ondelete + self.translate = translate + self._domain = domain or [] + self.relate =False + self._context = context + self.group_name = False + self.write = False + self.read = False + self.view_load = 0 + self.select=select + for a in args: + if args[a]: + setattr(self, a, args[a]) + if self.relate: + warnings.warn("The relate attribute doesn't work anymore, use act_window tag instead", DeprecationWarning) + + def restart(self): + pass + + def set(self, cr, obj, id, name, value, user=None, context=None): + cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%d', (self._symbol_set[1](value),id) ) + def set_memory(self, cr, obj, id, name, value, user=None, context=None): + raise Exception, _('Not implemented set_memory method !') + + def get_memory(self, cr, obj, ids, name, context=None, values=None): + raise Exception, _('Not implemented get_memory method !') + def get(self, cr, obj, ids, name, context=None, values=None): + raise Exception, _('undefined get method !') + + def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): + ids = obj.search(cr, uid, args+self._domain+[(name,'ilike',value)], offset, limit) + res = obj.read(cr, uid, ids, [name]) + return [x[name] for x in res] + def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, context=None): + raise Exception, _('Not implemented search_memory method !') # --------------------------------------------------------- # Simple fields # --------------------------------------------------------- class boolean(_column): - _type = 'boolean' - _symbol_c = '%s' - _symbol_f = lambda x: x and 'True' or 'False' - _symbol_set = (_symbol_c, _symbol_f) + _type = 'boolean' + _symbol_c = '%s' + _symbol_f = lambda x: x and 'True' or 'False' + _symbol_set = (_symbol_c, _symbol_f) class integer(_column): - _type = 'integer' - _symbol_c = '%d' - _symbol_f = lambda x: int(x or 0) - _symbol_set = (_symbol_c, _symbol_f) + _type = 'integer' + _symbol_c = '%d' + _symbol_f = lambda x: int(x or 0) + _symbol_set = (_symbol_c, _symbol_f) class reference(_column): - _type = 'reference' - def __init__(self, string, selection, size, **args): - _column.__init__(self, string=string, size=size, selection=selection, **args) + _type = 'reference' + def __init__(self, string, selection, size, **args): + _column.__init__(self, string=string, size=size, selection=selection, **args) class char(_column): - _type = 'char' - - def __init__(self, string, size, **args): - _column.__init__(self, string=string, size=size, **args) - self._symbol_set = (self._symbol_c, self._symbol_set_char) - - # takes a string (encoded in utf8) and returns a string (encoded in utf8) - def _symbol_set_char(self, symb): - #TODO: - # * we need to remove the "symb==False" from the next line BUT - # for now too many things rely on this broken behavior - # * the symb==None test should be common to all data types - if symb==None or symb==False: - return None - - # we need to convert the string to a unicode object to be able - # to evaluate its length (and possibly truncate it) reliably - if isinstance(symb, str): - u_symb = unicode(symb, 'utf8') - elif isinstance(symb, unicode): - u_symb = symb - else: - u_symb = unicode(symb) - return u_symb.encode('utf8')[:self.size] + _type = 'char' + + def __init__(self, string, size, **args): + _column.__init__(self, string=string, size=size, **args) + self._symbol_set = (self._symbol_c, self._symbol_set_char) + + # takes a string (encoded in utf8) and returns a string (encoded in utf8) + def _symbol_set_char(self, symb): + #TODO: + # * we need to remove the "symb==False" from the next line BUT + # for now too many things rely on this broken behavior + # * the symb==None test should be common to all data types + if symb==None or symb==False: + return None + + # we need to convert the string to a unicode object to be able + # to evaluate its length (and possibly truncate it) reliably + if isinstance(symb, str): + u_symb = unicode(symb, 'utf8') + elif isinstance(symb, unicode): + u_symb = symb + else: + u_symb = unicode(symb) + return u_symb.encode('utf8')[:self.size] class text(_column): - _type = 'text' + _type = 'text' import __builtin__ class float(_column): - _type = 'float' - _symbol_c = '%f' - _symbol_f = lambda x: __builtin__.float(x or 0.0) - _symbol_set = (_symbol_c, _symbol_f) - def __init__(self, string='unknown', digits=None, **args): - _column.__init__(self, string=string, **args) - self.digits = digits + _type = 'float' + _symbol_c = '%f' + _symbol_f = lambda x: __builtin__.float(x or 0.0) + _symbol_set = (_symbol_c, _symbol_f) + def __init__(self, string='unknown', digits=None, **args): + _column.__init__(self, string=string, **args) + self.digits = digits class date(_column): - _type = 'date' + _type = 'date' class datetime(_column): - _type = 'datetime' + _type = 'datetime' class time(_column): - _type = 'time' + _type = 'time' class binary(_column): - _type = 'binary' - _symbol_c = '%s' - _symbol_f = lambda symb: symb and psycopg.Binary(symb) or None - _symbol_set = (_symbol_c, _symbol_f) + _type = 'binary' + _symbol_c = '%s' + _symbol_f = lambda symb: symb and psycopg.Binary(symb) or None + _symbol_set = (_symbol_c, _symbol_f) class selection(_column): - _type = 'selection' - - def __init__(self, selection, string='unknown', **args): - _column.__init__(self, string=string, **args) - self.selection = selection + _type = 'selection' + + def __init__(self, selection, string='unknown', **args): + _column.__init__(self, string=string, **args) + self.selection = selection # --------------------------------------------------------- # Relationals fields @@ -207,208 +207,208 @@ class selection(_column): # #CHECKME: dans la pratique c'est quoi la syntaxe utilisee pour le 5? (5) ou (5, 0)? class one2one(_column): - _classic_read = False - _classic_write = True - _type = 'one2one' - - def __init__(self, obj, string='unknown', **args): - warnings.warn("The one2one field doesn't work anymore", DeprecationWarning) - _column.__init__(self, string=string, **args) - self._obj = obj - - def set(self, cr, obj_src, id, field, act, user=None, context=None): - if not context: - context={} - obj = obj_src.pool.get(self._obj) - self._table = obj_src.pool.get(self._obj)._table - if act[0]==0: - id_new = obj.create(cr, user, act[1]) - cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new,id)) - else: - cr.execute('select '+field+' from '+obj_src._table+' where id=%d', (act[0],)) - id =cr.fetchone()[0] - obj.write(cr, user, [id] , act[1], context=context) - - def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): - return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name','like',value)], offset, limit) + _classic_read = False + _classic_write = True + _type = 'one2one' + + def __init__(self, obj, string='unknown', **args): + warnings.warn("The one2one field doesn't work anymore", DeprecationWarning) + _column.__init__(self, string=string, **args) + self._obj = obj + + def set(self, cr, obj_src, id, field, act, user=None, context=None): + if not context: + context={} + obj = obj_src.pool.get(self._obj) + self._table = obj_src.pool.get(self._obj)._table + if act[0]==0: + id_new = obj.create(cr, user, act[1]) + cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new,id)) + else: + cr.execute('select '+field+' from '+obj_src._table+' where id=%d', (act[0],)) + id =cr.fetchone()[0] + obj.write(cr, user, [id] , act[1], context=context) + + def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): + return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name','like',value)], offset, limit) class many2one(_column): - _classic_read = False - _classic_write = True - _type = 'many2one' - - def __init__(self, obj, string='unknown', **args): - _column.__init__(self, string=string, **args) - self._obj = obj - - # - # TODO: speed improvement - # - # name is the name of the relation field - def get_memory(self, cr, obj, ids, name, user=None, context=None, values=None): - result = {} - for id in ids: - result[id] = obj.datas[id][name] - return result - - def get(self, cr, obj, ids, name, user=None, context=None, values=None): - if not context: - context={} - if not values: - values={} - res = {} - for r in values: - res[r['id']] = r[name] - for id in ids: - res.setdefault(id, '') - obj = obj.pool.get(self._obj) - # build a dictionary of the form {'id_of_distant_resource': name_of_distant_resource} - from orm import except_orm - try: - names = dict(obj.name_get(cr, user, filter(None, res.values()), context)) - except except_orm: - names={} - - iids = filter(None, res.values()) - cr.execute('select id,'+obj._rec_name+' from '+obj._table+' where id in ('+','.join(map(str,iids))+')') - for res22 in cr.fetchall(): - names[res22[0]] = res22[1] - - for r in res.keys(): - if res[r] and res[r] in names: - res[r] = (res[r], names[res[r]]) - else: - res[r] = False - return res - - def set(self, cr, obj_src, id, field, values, user=None, context=None): - if not context: - context={} - obj = obj_src.pool.get(self._obj) - self._table = obj_src.pool.get(self._obj)._table - if type(values)==type([]): - for act in values: - if act[0]==0: - id_new = obj.create(cr, act[2]) - cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new,id)) - elif act[0]==1: - obj.write(cr, [act[1]], act[2], context=context) - elif act[0]==2: - cr.execute('delete from '+self._table+' where id=%d', (act[1],)) - elif act[0]==3 or act[0]==5: - cr.execute('update '+obj_src._table+' set '+field+'=null where id=%d', (id,)) - elif act[0]==4: - cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (act[1],id)) - else: - if values: - cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (values,id)) - else: - cr.execute('update '+obj_src._table+' set '+field+'=null where id=%d', (id,)) - - def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): - return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name','like',value)], offset, limit) + _classic_read = False + _classic_write = True + _type = 'many2one' + + def __init__(self, obj, string='unknown', **args): + _column.__init__(self, string=string, **args) + self._obj = obj + + # + # TODO: speed improvement + # + # name is the name of the relation field + def get_memory(self, cr, obj, ids, name, user=None, context=None, values=None): + result = {} + for id in ids: + result[id] = obj.datas[id][name] + return result + + def get(self, cr, obj, ids, name, user=None, context=None, values=None): + if not context: + context={} + if not values: + values={} + res = {} + for r in values: + res[r['id']] = r[name] + for id in ids: + res.setdefault(id, '') + obj = obj.pool.get(self._obj) + # build a dictionary of the form {'id_of_distant_resource': name_of_distant_resource} + from orm import except_orm + try: + names = dict(obj.name_get(cr, user, filter(None, res.values()), context)) + except except_orm: + names={} + + iids = filter(None, res.values()) + cr.execute('select id,'+obj._rec_name+' from '+obj._table+' where id in ('+','.join(map(str,iids))+')') + for res22 in cr.fetchall(): + names[res22[0]] = res22[1] + + for r in res.keys(): + if res[r] and res[r] in names: + res[r] = (res[r], names[res[r]]) + else: + res[r] = False + return res + + def set(self, cr, obj_src, id, field, values, user=None, context=None): + if not context: + context={} + obj = obj_src.pool.get(self._obj) + self._table = obj_src.pool.get(self._obj)._table + if type(values)==type([]): + for act in values: + if act[0]==0: + id_new = obj.create(cr, act[2]) + cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new,id)) + elif act[0]==1: + obj.write(cr, [act[1]], act[2], context=context) + elif act[0]==2: + cr.execute('delete from '+self._table+' where id=%d', (act[1],)) + elif act[0]==3 or act[0]==5: + cr.execute('update '+obj_src._table+' set '+field+'=null where id=%d', (id,)) + elif act[0]==4: + cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (act[1],id)) + else: + if values: + cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (values,id)) + else: + cr.execute('update '+obj_src._table+' set '+field+'=null where id=%d', (id,)) + + def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): + return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name','like',value)], offset, limit) class one2many(_column): - _classic_read = False - _classic_write = False - _type = 'one2many' - - def __init__(self, obj, fields_id, string='unknown', limit=None, **args): - _column.__init__(self, string=string, **args) - self._obj = obj - self._fields_id = fields_id - self._limit = limit - #one2many can't be used as condition for defaults - assert(self.change_default != True) - - def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): - if not context: - context = {} - if not values: - values = {} - res = {} - for id in ids: - res[id] = [] - ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit) - for r in obj.pool.get(self._obj).read(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): - if r[self._fields_id] in res: - res[r[self._fields_id]].append( r['id'] ) - return res - - def set_memory(self, cr, obj, id, field, values, user=None, context=None): - if not context: - context={} - if not values: - return - obj = obj.pool.get(self._obj) - for act in values: - if act[0]==0: - act[2][self._fields_id] = id - obj.create(cr, user, act[2], context=context) - elif act[0]==1: - obj.write(cr, user, [act[1]] , act[2], context=context) - elif act[0]==2: - obj.unlink(cr, user, [act[1]], context=context) - elif act[0]==3: - obj.datas[act[1]][self._fields_id] = False - elif act[0]==4: - obj.datas[act[1]] = id - elif act[0]==5: - for o in obj.datas.values(): - if o[self._fields_id]==id: - o[self._fields_id] = False - elif act[0]==6: - for id2 in (act[2] or []): - obj.datas[id2][self._fields_id] = id - - def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None): - raise _('Not Implemented') - - - def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): - if not context: - context = {} - if not values: - values = {} - res = {} - for id in ids: - res[id] = [] - ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit) - for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): - res[r[self._fields_id]].append( r['id'] ) - return res - - def set(self, cr, obj, id, field, values, user=None, context=None): - if not context: - context={} - if not values: - return - _table = obj.pool.get(self._obj)._table - obj = obj.pool.get(self._obj) - for act in values: - if act[0]==0: - act[2][self._fields_id] = id - obj.create(cr, user, act[2], context=context) - elif act[0]==1: - obj.write(cr, user, [act[1]] , act[2], context=context) - elif act[0]==2: - obj.unlink(cr, user, [act[1]], context=context) - elif act[0]==3: - cr.execute('update '+_table+' set '+self._fields_id+'=null where id=%d', (act[1],)) - elif act[0]==4: - cr.execute('update '+_table+' set '+self._fields_id+'=%d where id=%d', (id,act[1])) - elif act[0]==5: - cr.execute('update '+_table+' set '+self._fields_id+'=null where '+self._fields_id+'=%d', (id,)) - elif act[0]==6: - if not act[2]: - ids2 = [0] - else: - ids2 = act[2] - cr.execute('update '+_table+' set '+self._fields_id+'=NULL where '+self._fields_id+'=%d and id not in ('+','.join(map(str, ids2))+')', (id,)) - if act[2]: - cr.execute('update '+_table+' set '+self._fields_id+'=%d where id in ('+','.join(map(str, act[2]))+')', (id,)) - def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'): - return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, offset, limit) + _classic_read = False + _classic_write = False + _type = 'one2many' + + def __init__(self, obj, fields_id, string='unknown', limit=None, **args): + _column.__init__(self, string=string, **args) + self._obj = obj + self._fields_id = fields_id + self._limit = limit + #one2many can't be used as condition for defaults + assert(self.change_default != True) + + def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): + if not context: + context = {} + if not values: + values = {} + res = {} + for id in ids: + res[id] = [] + ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit) + for r in obj.pool.get(self._obj).read(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): + if r[self._fields_id] in res: + res[r[self._fields_id]].append( r['id'] ) + return res + + def set_memory(self, cr, obj, id, field, values, user=None, context=None): + if not context: + context={} + if not values: + return + obj = obj.pool.get(self._obj) + for act in values: + if act[0]==0: + act[2][self._fields_id] = id + obj.create(cr, user, act[2], context=context) + elif act[0]==1: + obj.write(cr, user, [act[1]] , act[2], context=context) + elif act[0]==2: + obj.unlink(cr, user, [act[1]], context=context) + elif act[0]==3: + obj.datas[act[1]][self._fields_id] = False + elif act[0]==4: + obj.datas[act[1]] = id + elif act[0]==5: + for o in obj.datas.values(): + if o[self._fields_id]==id: + o[self._fields_id] = False + elif act[0]==6: + for id2 in (act[2] or []): + obj.datas[id2][self._fields_id] = id + + def search_memory(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like', context=None): + raise _('Not Implemented') + + + def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): + if not context: + context = {} + if not values: + values = {} + res = {} + for id in ids: + res[id] = [] + ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit) + for r in obj.pool.get(self._obj)._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): + res[r[self._fields_id]].append( r['id'] ) + return res + + def set(self, cr, obj, id, field, values, user=None, context=None): + if not context: + context={} + if not values: + return + _table = obj.pool.get(self._obj)._table + obj = obj.pool.get(self._obj) + for act in values: + if act[0]==0: + act[2][self._fields_id] = id + obj.create(cr, user, act[2], context=context) + elif act[0]==1: + obj.write(cr, user, [act[1]] , act[2], context=context) + elif act[0]==2: + obj.unlink(cr, user, [act[1]], context=context) + elif act[0]==3: + cr.execute('update '+_table+' set '+self._fields_id+'=null where id=%d', (act[1],)) + elif act[0]==4: + cr.execute('update '+_table+' set '+self._fields_id+'=%d where id=%d', (id,act[1])) + elif act[0]==5: + cr.execute('update '+_table+' set '+self._fields_id+'=null where '+self._fields_id+'=%d', (id,)) + elif act[0]==6: + if not act[2]: + ids2 = [0] + else: + ids2 = act[2] + cr.execute('update '+_table+' set '+self._fields_id+'=NULL where '+self._fields_id+'=%d and id not in ('+','.join(map(str, ids2))+')', (id,)) + if act[2]: + cr.execute('update '+_table+' set '+self._fields_id+'=%d where id in ('+','.join(map(str, act[2]))+')', (id,)) + def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'): + return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, offset, limit) # # Values: (0, 0, { fields }) create @@ -420,107 +420,107 @@ class one2many(_column): # (6, ?, ids) set a list of links # class many2many(_column): - _classic_read = False - _classic_write = False - _type = 'many2many' - - def __init__(self, obj, rel, id1, id2, string='unknown', limit=None, **args): - _column.__init__(self, string=string, **args) - self._obj = obj - self._rel = rel - self._id1 = id1 - self._id2 = id2 - self._limit = limit - - def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): - if not context: - context={} - if not values: - values={} - res = {} - if not ids: - return res - for id in ids: - res[id] = [] - ids_s = ','.join(map(str,ids)) - limit_str = self._limit is not None and ' limit %d' % self._limit or '' - obj = obj.pool.get(self._obj) - - d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name) - if d1: - d1 = ' and '+d1 - - cr.execute('SELECT '+self._rel+'.'+self._id2+','+self._rel+'.'+self._id1+' \ - FROM '+self._rel+' , '+obj._table+' \ - WHERE '+self._rel+'.'+self._id1+' in ('+ids_s+') \ - AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+d1 - +limit_str+' order by '+obj._table+'.'+obj._order+' offset %d', - d2+[offset]) - for r in cr.fetchall(): - res[r[1]].append(r[0]) - return res - - def set(self, cr, obj, id, name, values, user=None, context=None): - if not context: - context={} - if not values: - return - obj = obj.pool.get(self._obj) - for act in values: - if act[0]==0: - idnew = obj.create(cr, user, act[2]) - cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id,idnew)) - elif act[0]==1: - obj.write(cr, user, [act[1]] , act[2], context=context) - elif act[0]==2: - obj.unlink(cr, user, [act[1]], context=context) - elif act[0]==3: - cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%d and '+ self._id2 + '=%d', (id,act[1])) - elif act[0]==4: - cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id,act[1])) - elif act[0]==5: - cr.execute('update '+self._rel+' set '+self._id2+'=null where '+self._id2+'=%d', (id,)) - elif act[0]==6: - - d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name) - if d1: - d1 = ' and '+d1 - cr.execute('delete from '+self._rel+' where '+self._id1+'=%d AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+obj._table+' WHERE '+self._rel+'.'+self._id1+'=%d AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2 ) - - for act_nbr in act[2]: - cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d, %d)', (id, act_nbr)) - - # - # TODO: use a name_search - # - def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'): - return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name',operator,value)], offset, limit) - - def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): - result = {} - for id in ids: - result[id] = obj.datas[id].get(name, []) - return result - - def set_memory(self, cr, obj, id, name, values, user=None, context=None): - if not values: - return - for act in values: - # TODO: use constants instead of these magic numbers - if act[0]==0: - raise _('Not Implemented') - elif act[0]==1: - raise _('Not Implemented') - elif act[0]==2: - raise _('Not Implemented') - elif act[0]==3: - raise _('Not Implemented') - elif act[0]==4: - raise _('Not Implemented') - elif act[0]==5: - raise _('Not Implemented') - elif act[0]==6: - obj.datas[id][name] = act[2] + _classic_read = False + _classic_write = False + _type = 'many2many' + + def __init__(self, obj, rel, id1, id2, string='unknown', limit=None, **args): + _column.__init__(self, string=string, **args) + self._obj = obj + self._rel = rel + self._id1 = id1 + self._id2 = id2 + self._limit = limit + + def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): + if not context: + context={} + if not values: + values={} + res = {} + if not ids: + return res + for id in ids: + res[id] = [] + ids_s = ','.join(map(str,ids)) + limit_str = self._limit is not None and ' limit %d' % self._limit or '' + obj = obj.pool.get(self._obj) + + d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name) + if d1: + d1 = ' and '+d1 + + cr.execute('SELECT '+self._rel+'.'+self._id2+','+self._rel+'.'+self._id1+' \ + FROM '+self._rel+' , '+obj._table+' \ + WHERE '+self._rel+'.'+self._id1+' in ('+ids_s+') \ + AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+d1 + +limit_str+' order by '+obj._table+'.'+obj._order+' offset %d', + d2+[offset]) + for r in cr.fetchall(): + res[r[1]].append(r[0]) + return res + + def set(self, cr, obj, id, name, values, user=None, context=None): + if not context: + context={} + if not values: + return + obj = obj.pool.get(self._obj) + for act in values: + if act[0]==0: + idnew = obj.create(cr, user, act[2]) + cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id,idnew)) + elif act[0]==1: + obj.write(cr, user, [act[1]] , act[2], context=context) + elif act[0]==2: + obj.unlink(cr, user, [act[1]], context=context) + elif act[0]==3: + cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%d and '+ self._id2 + '=%d', (id,act[1])) + elif act[0]==4: + cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id,act[1])) + elif act[0]==5: + cr.execute('update '+self._rel+' set '+self._id2+'=null where '+self._id2+'=%d', (id,)) + elif act[0]==6: + + d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name) + if d1: + d1 = ' and '+d1 + cr.execute('delete from '+self._rel+' where '+self._id1+'=%d AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+obj._table+' WHERE '+self._rel+'.'+self._id1+'=%d AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2 ) + + for act_nbr in act[2]: + cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d, %d)', (id, act_nbr)) + + # + # TODO: use a name_search + # + def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'): + return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name',operator,value)], offset, limit) + + def get_memory(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None): + result = {} + for id in ids: + result[id] = obj.datas[id].get(name, []) + return result + + def set_memory(self, cr, obj, id, name, values, user=None, context=None): + if not values: + return + for act in values: + # TODO: use constants instead of these magic numbers + if act[0]==0: + raise _('Not Implemented') + elif act[0]==1: + raise _('Not Implemented') + elif act[0]==2: + raise _('Not Implemented') + elif act[0]==3: + raise _('Not Implemented') + elif act[0]==4: + raise _('Not Implemented') + elif act[0]==5: + raise _('Not Implemented') + elif act[0]==6: + obj.datas[id][name] = act[2] # --------------------------------------------------------- @@ -528,151 +528,151 @@ class many2many(_column): # --------------------------------------------------------- class function(_column): - _classic_read = False - _classic_write = False - _type = 'function' - _properties = True - - def __init__(self, fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float', fnct_search=None, obj=None, method=False, store=False, **args): - _column.__init__(self, **args) - self._obj = obj - self._method = method - self._fnct = fnct - self._fnct_inv = fnct_inv - self._arg = arg - if 'relation' in args: - self._obj = args['relation'] - self._fnct_inv_arg = fnct_inv_arg - if not fnct_inv: - self.readonly = 1 - self._type = type - self._fnct_search = fnct_search - self.store = store - if type == 'float': - self._symbol_c = '%f' - self._symbol_f = lambda x: __builtin__.float(x or 0.0) - self._symbol_set = (self._symbol_c, self._symbol_f) - - def search(self, cr, uid, obj, name, args): - if not self._fnct_search: - #CHECKME: should raise an exception - return [] - return self._fnct_search(obj, cr, uid, obj, name, args) - - def get(self, cr, obj, ids, name, user=None, context=None, values=None): - if not context: - context={} - if not values: - values={} - res = {} - table = obj._table - if self._method: - # TODO get HAS to receive uid for permissions ! - return self._fnct(obj, cr, user, ids, name, self._arg, context) - else: - return self._fnct(cr, table, ids, name, self._arg, context) - - def set(self, cr, obj, id, name, value, user=None, context=None): - if not context: - context={} - if self._fnct_inv: - self._fnct_inv(obj, cr, user, id, name, value, self._fnct_inv_arg, context) + _classic_read = False + _classic_write = False + _type = 'function' + _properties = True + + def __init__(self, fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float', fnct_search=None, obj=None, method=False, store=False, **args): + _column.__init__(self, **args) + self._obj = obj + self._method = method + self._fnct = fnct + self._fnct_inv = fnct_inv + self._arg = arg + if 'relation' in args: + self._obj = args['relation'] + self._fnct_inv_arg = fnct_inv_arg + if not fnct_inv: + self.readonly = 1 + self._type = type + self._fnct_search = fnct_search + self.store = store + if type == 'float': + self._symbol_c = '%f' + self._symbol_f = lambda x: __builtin__.float(x or 0.0) + self._symbol_set = (self._symbol_c, self._symbol_f) + + def search(self, cr, uid, obj, name, args): + if not self._fnct_search: + #CHECKME: should raise an exception + return [] + return self._fnct_search(obj, cr, uid, obj, name, args) + + def get(self, cr, obj, ids, name, user=None, context=None, values=None): + if not context: + context={} + if not values: + values={} + res = {} + table = obj._table + if self._method: + # TODO get HAS to receive uid for permissions ! + return self._fnct(obj, cr, user, ids, name, self._arg, context) + else: + return self._fnct(cr, table, ids, name, self._arg, context) + + def set(self, cr, obj, id, name, value, user=None, context=None): + if not context: + context={} + if self._fnct_inv: + self._fnct_inv(obj, cr, user, id, name, value, self._fnct_inv_arg, context) # --------------------------------------------------------- # Serialized fields # --------------------------------------------------------- class serialized(_column): - def __init__(self, string='unknown', serialize_func=repr, deserialize_func=eval, type='text', **args): - self._serialize_func = serialize_func - self._deserialize_func = deserialize_func - self._type = type - self._symbol_set = (self._symbol_c, self._serialize_func) - self._symbol_get = self._deserialize_func - super(serialized, self).__init__(string=string, **args) + def __init__(self, string='unknown', serialize_func=repr, deserialize_func=eval, type='text', **args): + self._serialize_func = serialize_func + self._deserialize_func = deserialize_func + self._type = type + self._symbol_set = (self._symbol_c, self._serialize_func) + self._symbol_get = self._deserialize_func + super(serialized, self).__init__(string=string, **args) class property(function): - def _fnct_write(self, obj, cr, uid, id, prop, id_val, val, context=None): - if not context: - context={} - (obj_dest,) = val - definition_id = self._field_get(cr, uid, obj._name, prop) - - property = obj.pool.get('ir.property') - nid = property.search(cr, uid, [('fields_id', '=', definition_id), - ('res_id', '=', obj._name+','+str(id))]) - while len(nid): - cr.execute('DELETE FROM ir_property WHERE id=%d', (nid.pop(),)) - - nid = property.search(cr, uid, [('fields_id', '=', definition_id), - ('res_id', '=', False)]) - default_val = False - if nid: - default_val = property.browse(cr, uid, nid[0], context).value - - company_id = obj.pool.get('res.users').company_get(cr, uid, uid) - res = False - newval = (id_val and obj_dest+','+str(id_val)) or False - if (newval != default_val) and newval: - propdef = obj.pool.get('ir.model.fields').browse(cr, uid, - definition_id, context=context) - res = property.create(cr, uid, { - 'name': propdef.name, - 'value': newval, - 'res_id': obj._name+','+str(id), - 'company_id': company_id, - 'fields_id': definition_id - }, context=context) - return res - - def _fnct_read(self, obj, cr, uid, ids, prop, val, context=None): - if not context: - context={} - property = obj.pool.get('ir.property') - definition_id = self._field_get(cr, uid, obj._name, prop) - - nid = property.search(cr, uid, [('fields_id', '=', definition_id), - ('res_id', '=', False)]) - default_val = False - if nid: - d = property.browse(cr, uid, nid[0], context).value - default_val = (d and int(d.split(',')[1])) or False - - vids = [obj._name + ',' + str(id) for id in ids] - nids = property.search(cr, uid, [('fields_id', '=', definition_id), - ('res_id', 'in', vids)]) - - res = {} - for id in ids: - res[id]= default_val - for prop in property.browse(cr, uid, nids): - res[int(prop.res_id.split(',')[1])] = (prop.value and \ - int(prop.value.split(',')[1])) or False - - obj = obj.pool.get(self._obj) - names = dict(obj.name_get(cr, uid, filter(None, res.values()), context)) - for r in res.keys(): - if res[r] and res[r] in names: - res[r] = (res[r], names[res[r]]) - else: - res[r] = False - return res - - def _field_get(self, cr, uid, model_name, prop): - if not self.field_id.get(cr.dbname): - cr.execute('SELECT id \ - FROM ir_model_fields \ - WHERE name=%s AND model=%s', (prop, model_name)) - res = cr.fetchone() - self.field_id[cr.dbname] = res and res[0] - return self.field_id[cr.dbname] - - def __init__(self, obj_prop, **args): - self.field_id = {} - function.__init__(self, self._fnct_read, False, self._fnct_write, - (obj_prop, ), **args) - - def restart(self): - self.field_id = {} + def _fnct_write(self, obj, cr, uid, id, prop, id_val, val, context=None): + if not context: + context={} + (obj_dest,) = val + definition_id = self._field_get(cr, uid, obj._name, prop) + + property = obj.pool.get('ir.property') + nid = property.search(cr, uid, [('fields_id', '=', definition_id), + ('res_id', '=', obj._name+','+str(id))]) + while len(nid): + cr.execute('DELETE FROM ir_property WHERE id=%d', (nid.pop(),)) + + nid = property.search(cr, uid, [('fields_id', '=', definition_id), + ('res_id', '=', False)]) + default_val = False + if nid: + default_val = property.browse(cr, uid, nid[0], context).value + + company_id = obj.pool.get('res.users').company_get(cr, uid, uid) + res = False + newval = (id_val and obj_dest+','+str(id_val)) or False + if (newval != default_val) and newval: + propdef = obj.pool.get('ir.model.fields').browse(cr, uid, + definition_id, context=context) + res = property.create(cr, uid, { + 'name': propdef.name, + 'value': newval, + 'res_id': obj._name+','+str(id), + 'company_id': company_id, + 'fields_id': definition_id + }, context=context) + return res + + def _fnct_read(self, obj, cr, uid, ids, prop, val, context=None): + if not context: + context={} + property = obj.pool.get('ir.property') + definition_id = self._field_get(cr, uid, obj._name, prop) + + nid = property.search(cr, uid, [('fields_id', '=', definition_id), + ('res_id', '=', False)]) + default_val = False + if nid: + d = property.browse(cr, uid, nid[0], context).value + default_val = (d and int(d.split(',')[1])) or False + + vids = [obj._name + ',' + str(id) for id in ids] + nids = property.search(cr, uid, [('fields_id', '=', definition_id), + ('res_id', 'in', vids)]) + + res = {} + for id in ids: + res[id]= default_val + for prop in property.browse(cr, uid, nids): + res[int(prop.res_id.split(',')[1])] = (prop.value and \ + int(prop.value.split(',')[1])) or False + + obj = obj.pool.get(self._obj) + names = dict(obj.name_get(cr, uid, filter(None, res.values()), context)) + for r in res.keys(): + if res[r] and res[r] in names: + res[r] = (res[r], names[res[r]]) + else: + res[r] = False + return res + + def _field_get(self, cr, uid, model_name, prop): + if not self.field_id.get(cr.dbname): + cr.execute('SELECT id \ + FROM ir_model_fields \ + WHERE name=%s AND model=%s', (prop, model_name)) + res = cr.fetchone() + self.field_id[cr.dbname] = res and res[0] + return self.field_id[cr.dbname] + + def __init__(self, obj_prop, **args): + self.field_id = {} + function.__init__(self, self._fnct_read, False, self._fnct_write, + (obj_prop, ), **args) + + def restart(self): + self.field_id = {} diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 789d2fa27f8..26fad04a900 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -68,2547 +68,2547 @@ ID_MAX = 1000 regex_order = re.compile('^([a-zA-Z0-9_]+( desc)?( asc)?,?)+$', re.I) def intersect(la, lb): - return filter(lambda x: x in lb, la) + return filter(lambda x: x in lb, la) class except_orm(Exception): - def __init__(self, name, value): - self.name = name - self.value = value - self.args = (name, value) + def __init__(self, name, value): + self.name = name + self.value = value + self.args = (name, value) # Readonly python database object browser class browse_null(object): - def __init__(self): - self.id=False + def __init__(self): + self.id=False - def __getitem__(self, name): - return False + def __getitem__(self, name): + return False - def __int__(self): - return False + def __int__(self): + return False - def __str__(self): - return '' + def __str__(self): + return '' - def __nonzero__(self): - return False + def __nonzero__(self): + return False # # TODO: execute an object method on browse_record_list # class browse_record_list(list): - def __init__(self, lst, context=None): - if not context: - context = {} - super(browse_record_list, self).__init__(lst) - self.context = context + def __init__(self, lst, context=None): + if not context: + context = {} + super(browse_record_list, self).__init__(lst) + self.context = context class browse_record(object): - def __init__(self, cr, uid, id, table, cache, context=None, list_class = None, fields_process={}): - ''' - table : the object (inherited from orm) - context : a dictionnary with an optionnal context - ''' - if not context: - context = {} - assert id, _('Wrong ID for the browse record, got %s, expected an integer.') % str(id) - self._list_class = list_class or browse_record_list - self._cr = cr - self._uid = uid - self._id = id - self._table = table - self._table_name = self._table._name - self._context = context - self._fields_process = fields_process - - cache.setdefault(table._name, {}) - self._data = cache[table._name] - if not id in self._data: - self._data[id] = {'id':id} - self._cache = cache - - def __getitem__(self, name): - if name == 'id': - return self._id - if not self._data[self._id].has_key(name): - # build the list of fields we will fetch - - # fetch the definition of the field which was asked for - if name in self._table._columns: - col = self._table._columns[name] - elif name in self._table._inherit_fields: - col = self._table._inherit_fields[name][2] - elif hasattr(self._table, name): - if isinstance( getattr(self._table, name), (types.MethodType,types.LambdaType,types.FunctionType)): - return lambda *args, **argv: getattr(self._table, name)(self._cr, self._uid, [self._id], *args, **argv) - else: - return getattr(self._table, name) - else: - logger = netsvc.Logger() - logger.notifyChannel('orm', netsvc.LOG_ERROR, "Programming error: field '%s' does not exist in object '%s' !" % (name, self._table._name)) - return False - - # if the field is a classic one or a many2one, we'll fetch all classic and many2one fields - if col._classic_write: - # gen the list of "local" (ie not inherited) fields which are classic or many2one - ffields = filter(lambda x: x[1]._classic_write, self._table._columns.items()) - # gen the list of inherited fields - inherits = map(lambda x: (x[0], x[1][2]), self._table._inherit_fields.items()) - # complete the field list with the inherited fields which are classic or many2one - ffields += filter(lambda x: x[1]._classic_write, inherits) - # otherwise we fetch only that field - else: - ffields = [(name,col)] - ids = filter(lambda id: not self._data[id].has_key(name), self._data.keys()) - # read the data - fffields = map(lambda x: x[0], ffields) - datas = self._table.read(self._cr, self._uid, ids, fffields, context=self._context, load="_classic_write") - if self._fields_process: - for n,f in ffields: - if f._type in self._fields_process: - for d in datas: - d[n] = self._fields_process[f._type](d[n]) - d[n].set_value(d[n], self, f) - - - # create browse records for 'remote' objects - for data in datas: - for n,f in ffields: - if f._type in ('many2one', 'one2one'): - if data[n]: - obj = self._table.pool.get(f._obj) - compids=False - if not f._classic_write: - ids2 = data[n][0] - else: - ids2 = data[n] - if ids2: - data[n] = browse_record(self._cr, self._uid, ids2, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) - else: - data[n] = browse_null() - else: - data[n] = browse_null() - elif f._type in ('one2many', 'many2many') and len(data[n]): - data[n] = self._list_class([browse_record(self._cr,self._uid,id,self._table.pool.get(f._obj),self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in data[n]], self._context) - self._data[data['id']].update(data) - return self._data[self._id][name] - - def __getattr__(self, name): -# raise an AttributeError exception. - return self[name] - - def __contains__(self, name): - return (name in self._table._columns) or (name in self._table._inherit_fields) or hasattr(self._table, name) - - def __hasattr__(self, name): - return name in self - - def __int__(self): - return self._id - - def __str__(self): - return "browse_record(%s, %d)" % (self._table_name, self._id) - - def __eq__(self, other): - return (self._table_name, self._id) == (other._table_name, other._id) - - def __ne__(self, other): - return (self._table_name, self._id) != (other._table_name, other._id) - - # we need to define __unicode__ even though we've already defined __str__ - # because we have overridden __getattr__ - def __unicode__(self): - return unicode(str(self)) - - def __hash__(self): - return hash((self._table_name, self._id)) - - __repr__ = __str__ + def __init__(self, cr, uid, id, table, cache, context=None, list_class = None, fields_process={}): + ''' + table : the object (inherited from orm) + context : a dictionnary with an optionnal context + ''' + if not context: + context = {} + assert id, _('Wrong ID for the browse record, got %s, expected an integer.') % str(id) + self._list_class = list_class or browse_record_list + self._cr = cr + self._uid = uid + self._id = id + self._table = table + self._table_name = self._table._name + self._context = context + self._fields_process = fields_process + + cache.setdefault(table._name, {}) + self._data = cache[table._name] + if not id in self._data: + self._data[id] = {'id':id} + self._cache = cache + + def __getitem__(self, name): + if name == 'id': + return self._id + if not self._data[self._id].has_key(name): + # build the list of fields we will fetch + + # fetch the definition of the field which was asked for + if name in self._table._columns: + col = self._table._columns[name] + elif name in self._table._inherit_fields: + col = self._table._inherit_fields[name][2] + elif hasattr(self._table, name): + if isinstance( getattr(self._table, name), (types.MethodType,types.LambdaType,types.FunctionType)): + return lambda *args, **argv: getattr(self._table, name)(self._cr, self._uid, [self._id], *args, **argv) + else: + return getattr(self._table, name) + else: + logger = netsvc.Logger() + logger.notifyChannel('orm', netsvc.LOG_ERROR, "Programming error: field '%s' does not exist in object '%s' !" % (name, self._table._name)) + return False + + # if the field is a classic one or a many2one, we'll fetch all classic and many2one fields + if col._classic_write: + # gen the list of "local" (ie not inherited) fields which are classic or many2one + ffields = filter(lambda x: x[1]._classic_write, self._table._columns.items()) + # gen the list of inherited fields + inherits = map(lambda x: (x[0], x[1][2]), self._table._inherit_fields.items()) + # complete the field list with the inherited fields which are classic or many2one + ffields += filter(lambda x: x[1]._classic_write, inherits) + # otherwise we fetch only that field + else: + ffields = [(name,col)] + ids = filter(lambda id: not self._data[id].has_key(name), self._data.keys()) + # read the data + fffields = map(lambda x: x[0], ffields) + datas = self._table.read(self._cr, self._uid, ids, fffields, context=self._context, load="_classic_write") + if self._fields_process: + for n,f in ffields: + if f._type in self._fields_process: + for d in datas: + d[n] = self._fields_process[f._type](d[n]) + d[n].set_value(d[n], self, f) + + + # create browse records for 'remote' objects + for data in datas: + for n,f in ffields: + if f._type in ('many2one', 'one2one'): + if data[n]: + obj = self._table.pool.get(f._obj) + compids=False + if not f._classic_write: + ids2 = data[n][0] + else: + ids2 = data[n] + if ids2: + data[n] = browse_record(self._cr, self._uid, ids2, obj, self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) + else: + data[n] = browse_null() + else: + data[n] = browse_null() + elif f._type in ('one2many', 'many2many') and len(data[n]): + data[n] = self._list_class([browse_record(self._cr,self._uid,id,self._table.pool.get(f._obj),self._cache, context=self._context, list_class=self._list_class, fields_process=self._fields_process) for id in data[n]], self._context) + self._data[data['id']].update(data) + return self._data[self._id][name] + + def __getattr__(self, name): +# raise an AttributeError exception. + return self[name] + + def __contains__(self, name): + return (name in self._table._columns) or (name in self._table._inherit_fields) or hasattr(self._table, name) + + def __hasattr__(self, name): + return name in self + + def __int__(self): + return self._id + + def __str__(self): + return "browse_record(%s, %d)" % (self._table_name, self._id) + + def __eq__(self, other): + return (self._table_name, self._id) == (other._table_name, other._id) + + def __ne__(self, other): + return (self._table_name, self._id) != (other._table_name, other._id) + + # we need to define __unicode__ even though we've already defined __str__ + # because we have overridden __getattr__ + def __unicode__(self): + return unicode(str(self)) + + def __hash__(self): + return hash((self._table_name, self._id)) + + __repr__ = __str__ def get_pg_type(f): - ''' - returns a tuple - (type returned by postgres when the column was created, type expression to create the column) - ''' - - type_dict = { - fields.boolean:'bool', - fields.integer:'int4', - fields.text:'text', - fields.date:'date', - fields.time:'time', - fields.datetime:'timestamp', - fields.binary:'bytea', - fields.many2one:'int4', - } - if type_dict.has_key(type(f)): - f_type = (type_dict[type(f)], type_dict[type(f)]) - elif isinstance(f, fields.float): - if f.digits: - f_type = ('numeric', 'NUMERIC(%d,%d)' % (f.digits[0],f.digits[1])) - else: - f_type = ('float8', 'DOUBLE PRECISION') - elif isinstance(f, (fields.char, fields.reference)): - f_type = ('varchar', 'VARCHAR(%d)' % (f.size,)) - elif isinstance(f, fields.selection): - if isinstance(f.selection, list) and isinstance(f.selection[0][0], (str, unicode)): - f_size = reduce(lambda x,y: max(x,len(y[0])), f.selection, f.size or 16) - elif isinstance(f.selection, list) and isinstance(f.selection[0][0], int): - f_size = -1 - else: - f_size = (hasattr(f,'size') and f.size) or 16 - - if f_size == -1: - f_type = ('int4', 'INTEGER') - else: - f_type = ('varchar', 'VARCHAR(%d)' % f_size) - elif isinstance(f, fields.function) and type_dict.has_key(eval('fields.'+(f._type))): - t=eval('fields.'+(f._type)) - f_type = (type_dict[t], type_dict[t]) - elif isinstance(f, fields.function) and f._type == 'float': - f_type = ('float8', 'DOUBLE PRECISION') - elif isinstance(f, fields.function) and f._type == 'selection': - f_type = ('text', 'text') - else: - logger = netsvc.Logger() - logger.notifyChannel("init", netsvc.LOG_WARNING, '%s type not supported!' % (type(f))) - f_type = None - return f_type + ''' + returns a tuple + (type returned by postgres when the column was created, type expression to create the column) + ''' + + type_dict = { + fields.boolean:'bool', + fields.integer:'int4', + fields.text:'text', + fields.date:'date', + fields.time:'time', + fields.datetime:'timestamp', + fields.binary:'bytea', + fields.many2one:'int4', + } + if type_dict.has_key(type(f)): + f_type = (type_dict[type(f)], type_dict[type(f)]) + elif isinstance(f, fields.float): + if f.digits: + f_type = ('numeric', 'NUMERIC(%d,%d)' % (f.digits[0],f.digits[1])) + else: + f_type = ('float8', 'DOUBLE PRECISION') + elif isinstance(f, (fields.char, fields.reference)): + f_type = ('varchar', 'VARCHAR(%d)' % (f.size,)) + elif isinstance(f, fields.selection): + if isinstance(f.selection, list) and isinstance(f.selection[0][0], (str, unicode)): + f_size = reduce(lambda x,y: max(x,len(y[0])), f.selection, f.size or 16) + elif isinstance(f.selection, list) and isinstance(f.selection[0][0], int): + f_size = -1 + else: + f_size = (hasattr(f,'size') and f.size) or 16 + + if f_size == -1: + f_type = ('int4', 'INTEGER') + else: + f_type = ('varchar', 'VARCHAR(%d)' % f_size) + elif isinstance(f, fields.function) and type_dict.has_key(eval('fields.'+(f._type))): + t=eval('fields.'+(f._type)) + f_type = (type_dict[t], type_dict[t]) + elif isinstance(f, fields.function) and f._type == 'float': + f_type = ('float8', 'DOUBLE PRECISION') + elif isinstance(f, fields.function) and f._type == 'selection': + f_type = ('text', 'text') + else: + logger = netsvc.Logger() + logger.notifyChannel("init", netsvc.LOG_WARNING, '%s type not supported!' % (type(f))) + f_type = None + return f_type class orm_template(object): - _name = None - _columns = {} - _constraints = [] - _defaults = {} - _rec_name = 'name' - _parent_name = 'parent_id' - _date_name = 'date' - _order = 'id' - _sequence = None - _description = None - _inherits = {} - _table = None - - def _field_create(self, cr, context={}): - cr.execute("SELECT id FROM ir_model WHERE model='%s'" % self._name) - if not cr.rowcount: - # reference model in order to have a description of its fonctionnality in custom_report - cr.execute('SELECT nextval(%s)', ('ir_model_id_seq',)) - id = cr.fetchone()[0] - cr.execute("INSERT INTO ir_model (id,model, name, info) VALUES (%s, %s, %s, %s)", (id,self._name, self._description, self.__doc__)) - if 'module' in context: - cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ - ( 'model_'+self._table, context['module'], 'ir.model', id) - ) - cr.commit() - - cr.execute("SELECT * FROM ir_model_fields WHERE model=%s", (self._name,)) - cols = {} - for rec in cr.dictfetchall(): - cols[rec['name']] = rec - - cr.execute("SELECT id FROM ir_model WHERE model='%s'" % self._name) - model_id = cr.fetchone()[0] - - for (k,f) in self._columns.items(): - vals = { - 'model_id':model_id, - 'model':self._name, - 'name':k, - 'field_description':f.string.replace("'", " "), - 'ttype':f._type, - 'relate':(f.relate and 1) or 0, - 'relation':f._obj or 'NULL', - 'group_name':f.group_name or '', - 'view_load':(f.view_load and 1) or 0, - 'select_level':str(f.select or 0) - } - if k not in cols: - cr.execute('select nextval(%s)', ('ir_model_fields_id_seq',)) - id = cr.fetchone()[0] - vals['id'] = id - cr.execute("""INSERT INTO ir_model_fields ( - id, model_id, model, name, field_description, ttype, - relate,relation,group_name,view_load,state,select_level - ) VALUES ( - %d,%s,%s,%s,%s,%s, %s,%s,%s,%s,%s, %s - )""", ( - id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'], - bool(vals['relate']), vals['relation'], vals['group_name'], bool(vals['view_load']), 'base', - vals['select_level'] - )) - if 'module' in context: - cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ - ( ('field_'+self._table+'_'+k)[:64], context['module'], 'ir.model.fields', id) - ) - else: - for key,val in vals.items(): - if cols[k][key]<>vals[key]: - print 'Different', cols[k][key], vals[key], k, key, self._name - cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'],vals['model'],vals['name'])) - cr.commit() - cr.execute("""UPDATE ir_model_fields SET - model_id=%s, field_description=%s, ttype=%s, relate=%s, relation=%s, - group_name=%s, view_load=%s, select_level=%s - WHERE - model=%s AND name=%s""", ( - vals['model_id'],vals['field_description'],vals['ttype'],bool(vals['relate']), - vals['relation'], vals['group_name'], bool(vals['view_load']), - vals['select_level'], vals['model'], vals['name'] - )) - continue - cr.commit() - - def _auto_init(self, cr, context={}): - self._field_create(cr, context) - - def __init__(self, cr): - if not self._description: - self._description = self._name - if not self._table: - self._table=self._name.replace('.','_') - - def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}): - if not context: - context={} - self._list_class = list_class or browse_record_list - cache = {} - # need to accepts ints and longs because ids coming from a method - # launched by button in the interface have a type long... - if isinstance(select, (int, long)): - return browse_record(cr,uid,select,self,cache, context=context, list_class=self._list_class, fields_process=fields_process) - elif isinstance(select,list): - return self._list_class([browse_record(cr,uid,id,self,cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context) - else: - return browse_null() - - def __export_row(self, cr, uid, row, fields, context=None): - lines = [] - data = map(lambda x: '', range(len(fields))) - done = [] - for fpos in range(len(fields)): - f = fields[fpos] - if f: - r = row - i = 0 - while i<len(f): - r = r[f[i]] - if not r: - break - if isinstance(r, (browse_record_list, list)): - first = True - fields2 = map(lambda x: (x[:i+1]==f[:i+1] and x[i+1:]) \ - or [], fields) - if fields2 in done: - break - done.append(fields2) - for row2 in r: - lines2 = self.__export_row(cr, uid, row2, fields2, - context) - if first: - for fpos2 in range(len(fields)): - if lines2 and lines2[0][fpos2]: - data[fpos2] = lines2[0][fpos2] - lines+= lines2[1:] - first = False - else: - lines+= lines2 - break - i+=1 - if i==len(f): - data[fpos] = str(r or '') - return [data] + lines - - def export_data(self, cr, uid, ids, fields, context=None): - if not context: - context={} - fields = map(lambda x: x.split('/'), fields) - datas = [] - for row in self.browse(cr, uid, ids, context): - datas += self.__export_row(cr, uid, row, fields, context) - return datas - - def import_data(self, cr, uid, fields, datas, mode='init', - current_module=None, noupdate=False, context=None): - if not context: - context={} - fields = map(lambda x: x.split('/'), fields) - logger = netsvc.Logger() - def process_liness(self, datas, prefix, fields_def, position=0): - line = datas[position] - row = {} - translate = {} - todo = [] - warning = '' - data_id= False - # - # Import normal fields - # - for i in range(len(fields)): - if i>=len(line): - raise Exception, _('Please check that all your lines have %d columns.') % (len(fields),) - field = fields[i] - if field == ["id"]: - data_id= line[i] - continue - if (len(field)==len(prefix)+1) and field[len(prefix)].endswith(':id'): - res_id = False - if line[i]: - if fields_def[field[len(prefix)][:-3]]['type']=='many2many': - res_id = [] - for word in line[i].split(','): - if '.' in word: - module, xml_id = word.rsplit('.', 1) - else: - module, xml_id = current_module, word - ir_model_data_obj = self.pool.get('ir.model.data') - id = ir_model_data_obj._get_id(cr, uid, module, - xml_id) - res_id2 = ir_model_data_obj.read(cr, uid, [id], - ['res_id'])[0]['res_id'] - if res_id2: - res_id.append(res_id2) - if len(res_id): - res_id=[(6,0,res_id)] - else: - if '.' in line[i]: - module, xml_id = line[i].rsplit('.', 1) - else: - module, xml_id = current_module, line[i] - ir_model_data_obj = self.pool.get('ir.model.data') - id = ir_model_data_obj._get_id(cr, uid, module, xml_id) - res_id = ir_model_data_obj.read(cr, uid, [id], - ['res_id'])[0]['res_id'] - row[field[0][:-3]] = res_id or False - continue - if (len(field) == len(prefix)+1) and \ - len(field[len(prefix)].split(':lang=')) == 2: - f, lang = field[len(prefix)].split(':lang=') - translate.setdefault(lang, {})[f]=line[i] or False - continue - if (len(field) == len(prefix)+1) and \ - (prefix == field[0:len(prefix)]): - if fields_def[field[len(prefix)]]['type'] == 'integer': - res =line[i] and int(line[i]) - elif fields_def[field[len(prefix)]]['type'] == 'float': - res =line[i] and float(line[i]) - elif fields_def[field[len(prefix)]]['type'] == 'selection': - res = False - if isinstance(fields_def[field[len(prefix)]]['selection'], - (tuple, list)): - sel = fields_def[field[len(prefix)]]['selection'] - else: - sel = fields_def[field[len(prefix)]]['selection'](self, - cr, uid, context) - for key, val in sel: - if str(key) == line[i]: - res = key - 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)])) - elif fields_def[field[len(prefix)]]['type']=='many2one': - res = False - if line[i]: - relation = fields_def[field[len(prefix)]]['relation'] - res2 = self.pool.get(relation).name_search(cr, uid, - line[i], [], operator='=') - res = (res2 and res2[0][0]) or False - if not res: - warning += ('Relation not found: ' + line[i] + \ - ' on ' + relation + ' !\n') - logger.notifyChannel("import", netsvc.LOG_WARNING, - 'Relation not found: ' + line[i] + \ - ' on ' + relation + ' !\n') - elif fields_def[field[len(prefix)]]['type']=='many2many': - res = [] - if line[i]: - relation = fields_def[field[len(prefix)]]['relation'] - for word in line[i].split(','): - res2 = self.pool.get(relation).name_search(cr, - uid, word, [], operator='=') - res3 = (res2 and res2[0][0]) or False - if not res3: - warning += ('Relation not found: ' + \ - line[i] + ' on '+relation + ' !\n') - logger.notifyChannel("import", - netsvc.LOG_WARNING, - 'Relation not found: ' + line[i] + \ - ' on '+relation + ' !\n') - else: - res.append(res3) - if len(res): - res= [(6,0,res)] - else: - res = line[i] or False - row[field[len(prefix)]] = res - elif (prefix==field[0:len(prefix)]): - if field[0] not in todo: - todo.append(field[len(prefix)]) - # - # Import one2many fields - # - nbrmax = 1 - for field in todo: - newfd = self.pool.get(fields_def[field]['relation']).fields_get( - cr, uid, context=context) - res = process_liness(self, datas, prefix + [field], newfd, position) - (newrow, max2, w2, translate2, data_id2) = res - nbrmax = max(nbrmax, max2) - warning = warning + w2 - reduce(lambda x,y: x and y, newrow) - row[field] = (reduce(lambda x, y: x or y, newrow.values()) and \ - [(0,0,newrow)]) or [] - i = max2 - while (position+i)<len(datas): - ok = True - for j in range(len(fields)): - field2 = fields[j] - if (len(field2) <= (len(prefix)+1)) and datas[position+i][j]: - ok = False - if not ok: - break - - (newrow, max2, w2, translate2, data_id2) = process_liness( - self, datas, prefix+[field], newfd, position+i) - warning = warning+w2 - if reduce(lambda x,y: x or y, newrow.values()): - row[field].append((0,0,newrow)) - i+=max2 - nbrmax = max(nbrmax, i) - - if len(prefix)==0: - for i in range(max(nbrmax,1)): - #if datas: - datas.pop(0) - result = (row, nbrmax, warning, translate, data_id) - return result - - fields_def = self.fields_get(cr, uid, context=context) - done = 0 - - while len(datas): - res = {} - try: - (res, other, warning, translate, data_id) = \ - process_liness(self, datas, [], fields_def) - if warning: - cr.rollback() - return (-1, res, warning, '') - id= self.pool.get('ir.model.data')._update(cr, uid, self._name, - current_module, res, xml_id=data_id, mode=mode, - noupdate=noupdate) - for lang in translate: - context2=context.copy() - context2['lang']=lang - self.write(cr, uid, [id], translate[lang], context2) - if config.get('commit_mode', False): - cr.commit() - except Exception, e: - logger.notifyChannel("import",netsvc.LOG_ERROR, e) - cr.rollback() - try: - return (-1, res, e[0], warning) - except: - return (-1, res, e[0], '') - done += 1 - # - # TODO: Send a request with the result and multi-thread ! - # - return (done, 0, 0, 0) - - def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): - raise _('The read method is not implemented on this object !') - - def _validate(self, cr, uid, ids, context=None): - context = context or {} - lng = context.get('lang', False) or 'en_US' - trans = self.pool.get('ir.translation') - field_error = [] - field_err_str = [] - for constraint in self._constraints: - fun, msg, fields = constraint - if not fun(self, cr, uid, ids): - field_error += fields - field_err_str.append( trans._get_source(cr, uid, self._name, 'constraint', lng, source=msg) ) - if len(field_err_str): - cr.rollback() - raise except_orm('ValidateError', ('\n'.join(field_err_str), ','.join(field_error))) - - def default_get(self, cr, uid, fields_list, context=None): - return {} - - def perm_read(self, cr, user, ids, context=None, details=True): - raise _('The perm_read method is not implemented on this object !') - - def unlink(self, cr, uid, ids, context=None): - raise _('The unlink method is not implemented on this object !') - - def write(self, cr, user, ids, vals, context=None): - raise _('The write method is not implemented on this object !') - - def create(self, cr, user, vals, context=None): - raise _('The create method is not implemented on this object !') - - # returns the definition of each field in the object - # the optional fields parameter can limit the result to some fields - def fields_get(self, cr, user, fields=None, context=None, read_access=True): - if context is None: - context = {} - res = {} - translation_obj = self.pool.get('ir.translation') - model_access_obj = self.pool.get('ir.model.access') - for parent in self._inherits: - res.update(self.pool.get(parent).fields_get(cr, user, fields, - context)) - for f in self._columns.keys(): - res[f] = {'type': self._columns[f]._type} - for arg in ('string', 'readonly', 'states', 'size', 'required', - 'change_default', 'translate', 'help', 'select'): - if getattr(self._columns[f], arg): - res[f][arg] = getattr(self._columns[f], arg) - if not read_access: - res[f]['readonly']= True - res[f]['states'] = {} - for arg in ('digits', 'invisible'): - if hasattr(self._columns[f], arg) \ - and getattr(self._columns[f], arg): - res[f][arg] = getattr(self._columns[f], arg) - - # translate the field label - res_trans = translation_obj._get_source(cr, user, - self._name + ',' + f, 'field', context.get('lang', False) or 'en_US') - if res_trans: - res[f]['string'] = res_trans - help_trans = translation_obj._get_source(cr, user, - self._name + ',' + f, 'help', context.get('lang', False) or 'en_US') - if help_trans: - res[f]['help'] = help_trans - - if hasattr(self._columns[f], 'selection'): - if isinstance(self._columns[f].selection, (tuple, list)): - sel = self._columns[f].selection - # translate each selection option - sel2 = [] - for (key,val) in sel: - val2 = translation_obj._get_source(cr, user, - self._name + ',' + f, 'selection', - context.get('lang', False) or 'en_US', val) - sel2.append((key, val2 or val)) - sel = sel2 - res[f]['selection'] = sel - else: - # call the 'dynamic selection' function - res[f]['selection'] = self._columns[f].selection(self, cr, - user, context) - if res[f]['type'] in ('one2many', 'many2many', - 'many2one', 'one2one'): - res[f]['relation'] = self._columns[f]._obj - res[f]['domain'] = self._columns[f]._domain - res[f]['context'] = self._columns[f]._context - - if fields: - # filter out fields which aren't in the fields list - for r in res.keys(): - if r not in fields: - del res[r] - return res - - # - # Overload this method if you need a window title which depends on the context - # - def view_header_get(self, cr, user, view_id=None, view_type='form', context=None): - return False - - def __view_look_dom(self, cr, user, node, context=None): - if not context: - context={} - result = False - fields = {} - childs = True - - if node.nodeType==node.ELEMENT_NODE and node.localName=='field': - if node.hasAttribute('name'): - attrs = {} - try: - if node.getAttribute('name') in self._columns: - relation = self._columns[node.getAttribute('name')]._obj - else: - relation = self._inherit_fields[node.getAttribute('name')][2]._obj - except: - relation = False - - if relation: - childs = False - views = {} - for f in node.childNodes: - if f.nodeType==f.ELEMENT_NODE and f.localName in ('form','tree','graph'): - node.removeChild(f) - xarch,xfields = self.pool.get(relation).__view_look_dom_arch(cr, user, f, context) - views[str(f.localName)] = { - 'arch': xarch, - 'fields': xfields - } - attrs = {'views': views} - fields[node.getAttribute('name')] = attrs - - elif node.nodeType==node.ELEMENT_NODE and node.localName in ('form','tree'): - result = self.view_header_get(cr, user, False, node.localName, context) - if result: - node.setAttribute('string', result.decode('utf-8')) - if node.nodeType==node.ELEMENT_NODE and node.hasAttribute('groups'): - groups = None - group_str = node.getAttribute('groups') - if ',' in group_str: - groups = group_str.split(','); - readonly = False - access_pool = self.pool.get('ir.model.access') - for group in groups: - readonly = readonly and access_pool.check_groups(cr, user, group) - - if readonly: - parent = node.parentNode - parent.removeChild(node) - - else: - if not self.pool.get('ir.model.access').check_groups(cr, user, group_str): - parent = node.parentNode - parent.removeChild(node) - - if node.nodeType == node.ELEMENT_NODE: - # translate view - if ('lang' in context) and not result: - if node.hasAttribute('string') and node.getAttribute('string'): - trans = tools.translate(cr, self._name, 'view', context['lang'], node.getAttribute('string').encode('utf8')) - if trans: - node.setAttribute('string', trans.decode('utf8')) - if node.hasAttribute('sum') and node.getAttribute('sum'): - trans = tools.translate(cr, self._name, 'view', context['lang'], node.getAttribute('sum').encode('utf8')) - if trans: - node.setAttribute('sum', trans.decode('utf8')) - # - # Add view for properties ! - # - if node.localName=='properties': - parent = node.parentNode - doc = node.ownerDocument - models = map(lambda x: "'"+x+"'", [self._name] + self._inherits.keys()) - cr.execute('select id,name,group_name from ir_model_fields where model in ('+','.join(models)+') and view_load order by group_name, id') - oldgroup = None - res = cr.fetchall() - for id, fname, gname in res: - if oldgroup != gname: - child = doc.createElement('separator') - child.setAttribute('string', gname.decode('utf-8')) - child.setAttribute('colspan', "4") - oldgroup = gname - parent.insertBefore(child, node) - - child = doc.createElement('field') - child.setAttribute('name', fname.decode('utf-8')) - parent.insertBefore(child, node) - parent.removeChild(node) - - if childs: - for f in node.childNodes: - fields.update(self.__view_look_dom(cr, user, f,context)) - return fields - - def __view_look_dom_arch(self, cr, user, node, context=None): - if not context: - context={} - fields_def = self.__view_look_dom(cr, user, node, context=context) - - buttons = xpath.Evaluate('//button', node) - if buttons: - for button in buttons: - if button.getAttribute('type') == 'object': - continue - - ok = True - - serv = netsvc.LocalService('object_proxy') - user_roles = serv.execute_cr(cr, user, 'res.users', 'read', [user], ['roles_id'])[0]['roles_id'] - cr.execute("select role_id from wkf_transition where signal='%s'" % button.getAttribute('name')) - roles = cr.fetchall() - for role in roles: - if role[0]: - ok = ok and serv.execute_cr(cr, user, 'res.roles', 'check', user_roles, role[0]) - - if not ok: - button.setAttribute('readonly', '1') - else: - button.setAttribute('readonly', '0') - - arch = node.toxml(encoding="utf-8").replace('\t', '') - - fields = self.fields_get(cr, user, fields_def.keys(), context) - for field in fields_def: - fields[field].update(fields_def[field]) - return arch, fields - - - def __get_default_calendar_view(self): - """Generate a default calendar view (For internal use only). - """ - - arch = ('<?xml version="1.0" encoding="utf-8"?>\n' - '<calendar string="%s" date_start="%s"') % (self._description, self._date_name) - - if 'user_id' in self._columns: - arch += ' color="user_id"' - - elif 'partner_id' in self._columns: - arch += ' color="partner_id"' - - if 'date_stop' in self._columns: - arch += ' date_stop="date_stop"' - - elif 'date_end' in self._columns: - arch += ' date_stop="date_end"' - - elif 'date_delay' in self._columns: - arch += ' date_delay="date_delay"' - - elif 'planned_hours' in self._columns: - arch += ' date_delay="planned_hours"' - - arch += ('>\n' - ' <field name="%s"/>\n' - '</calendar>') % (self._rec_name) - - return arch - - # - # if view_id, view_type is not required - # - def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False): - if not context: - context={} - def _inherit_apply(src, inherit): - def _find(node, node2): - # Check if xpath query or normal inherit (with field matching) - if node2.nodeType==node2.ELEMENT_NODE and node2.localName=='xpath': - res = xpath.Evaluate(node2.getAttribute('expr'), node) - return res and res[0] - else: - if node.nodeType==node.ELEMENT_NODE and node.localName==node2.localName: - res = True - for attr in node2.attributes.keys(): - if attr=='position': - continue - if node.hasAttribute(attr): - if node.getAttribute(attr)==node2.getAttribute(attr): - continue - res = False - if res: - return node - for child in node.childNodes: - res = _find(child, node2) - if res: return res - return None - - doc_src = dom.minidom.parseString(src) - doc_dest = dom.minidom.parseString(inherit) - toparse = doc_dest.childNodes - while len(toparse): - node2 = toparse.pop(0) - if not node2.nodeType==node2.ELEMENT_NODE: - continue - if node2.localName=='data': - toparse += node2.childNodes - continue - node = _find(doc_src, node2) - if node: - pos = 'inside' - if node2.hasAttribute('position'): - pos = node2.getAttribute('position') - if pos=='replace': - parent = node.parentNode - for child in node2.childNodes: - if child.nodeType==child.ELEMENT_NODE: - parent.insertBefore(child, node) - parent.removeChild(node) - else: - for child in node2.childNodes: - if child.nodeType==child.ELEMENT_NODE: - if pos=='inside': - node.appendChild(child) - elif pos=='after': - sib = node.nextSibling - if sib: - node.parentNode.insertBefore(child, sib) - else: - node.parentNode.appendChild(child) - elif pos=='before': - node.parentNode.insertBefore(child, node) - else: - raise AttributeError, _('Unknown position in inherited view %s !') % pos - else: - attrs = ''.join([ - ' %s="%s"' % (attr, node2.getAttribute(attr)) - for attr in node2.attributes.keys() - if attr != 'position' - ]) - tag = "<%s%s>" % (node2.localName, attrs) - raise AttributeError, _("Couldn't find tag '%s' in parent view !") % tag - return doc_src.toxml(encoding="utf-8").replace('\t', '') - - result = {'type':view_type, 'model':self._name} - - ok = True - model = True - sql_res = False - while ok: - if view_id: - where = (model and (" and model='%s'" % (self._name,))) or '' - cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE id=%d'+where, (view_id,)) - else: - cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE model=%s AND type=%s ORDER BY priority', (self._name,view_type)) - sql_res = cr.fetchone() - if not sql_res: - break - ok = sql_res[5] - view_id = ok or sql_res[3] - model = False - - # if a view was found - if sql_res: - result['type'] = sql_res[4] - result['view_id'] = sql_res[3] - result['arch'] = sql_res[0] - - def _inherit_apply_rec(result, inherit_id): - # get all views which inherit from (ie modify) this view - cr.execute('select arch,id from ir_ui_view where inherit_id=%d and model=%s order by priority', (inherit_id, self._name)) - sql_inherit = cr.fetchall() - for (inherit,id) in sql_inherit: - result = _inherit_apply(result, inherit) - result = _inherit_apply_rec(result, id) - return result - - result['arch'] = _inherit_apply_rec(result['arch'], sql_res[3]) - - result['name'] = sql_res[1] - result['field_parent'] = sql_res[2] or False - else: - # otherwise, build some kind of default view - if view_type == 'form': - res = self.fields_get(cr, user, context=context) - xml = '''<?xml version="1.0" encoding="utf-8"?>''' \ - '''<form string="%s">''' % (self._description,) - for x in res: - if res[x]['type'] not in ('one2many', 'many2many'): - xml += '<field name="%s"/>' % (x,) - if res[x]['type'] == 'text': - xml += "<newline/>" - xml += "</form>" - elif view_type == 'tree': - xml = '''<?xml version="1.0" encoding="utf-8"?>''' \ - '''<tree string="%s"><field name="%s"/></tree>''' \ - % (self._description, self._rec_name) - elif view_type == 'calendar': - xml = self.__get_default_calendar_view() - else: - xml = '' - result['arch'] = xml - result['name'] = 'default' - result['field_parent'] = False - result['view_id'] = 0 - - doc = dom.minidom.parseString(result['arch'].encode('utf-8')) - xarch, xfields = self.__view_look_dom_arch(cr, user, doc, context=context) - result['arch'] = xarch - result['fields'] = xfields - if toolbar: - def clean(x): - x = x[2] - for key in ('report_sxw_content', 'report_rml_content', - 'report_sxw', 'report_rml', - 'report_sxw_content_data', 'report_rml_content_data'): - if key in x: - del x[key] - return x - ir_values_obj = self.pool.get('ir.values') - resprint = ir_values_obj.get(cr, user, 'action', - 'client_print_multi', [(self._name, False)], False, - context) - resaction = ir_values_obj.get(cr, user, 'action', - 'client_action_multi', [(self._name, False)], False, - context) - - resrelate = ir_values_obj.get(cr, user, 'action', - 'client_action_relate', [(self._name, False)], False, - context) - resprint = map(clean, resprint) - resaction = map(clean, resaction) - resaction = filter(lambda x: not x.get('multi',False), resaction) - resprint = filter(lambda x: not x.get('multi',False), resprint) - resrelate = map(lambda x:x[2], resrelate) - - for x in resprint+resaction+resrelate: - x['string'] = x['name'] - - result['toolbar'] = { - 'print': resprint, - 'action': resaction, - 'relate': resrelate - } - return result - - _view_look_dom_arch=__view_look_dom_arch - - def search_count(self, cr, user, args, context=None): - if not context: - context = {} - res = self.search(cr, user, args, context=context, count=True) - if isinstance(res, list): - return len(res) - return res - - def search(self, cr, user, args, offset=0, limit=None, order=None, - context=None, count=False): - raise _('The search method is not implemented on this object !') - - def name_get(self, cr, user, ids, context=None): - raise _('The name_get method is not implemented on this object !') - - def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): - raise _('The name_search method is not implemented on this object !') - - def copy(self, cr, uid, id, default=None, context=None): - raise _('The copy method is not implemented on this object !') + _name = None + _columns = {} + _constraints = [] + _defaults = {} + _rec_name = 'name' + _parent_name = 'parent_id' + _date_name = 'date' + _order = 'id' + _sequence = None + _description = None + _inherits = {} + _table = None + + def _field_create(self, cr, context={}): + cr.execute("SELECT id FROM ir_model WHERE model='%s'" % self._name) + if not cr.rowcount: + # reference model in order to have a description of its fonctionnality in custom_report + cr.execute('SELECT nextval(%s)', ('ir_model_id_seq',)) + id = cr.fetchone()[0] + cr.execute("INSERT INTO ir_model (id,model, name, info) VALUES (%s, %s, %s, %s)", (id,self._name, self._description, self.__doc__)) + if 'module' in context: + cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ + ( 'model_'+self._table, context['module'], 'ir.model', id) + ) + cr.commit() + + cr.execute("SELECT * FROM ir_model_fields WHERE model=%s", (self._name,)) + cols = {} + for rec in cr.dictfetchall(): + cols[rec['name']] = rec + + cr.execute("SELECT id FROM ir_model WHERE model='%s'" % self._name) + model_id = cr.fetchone()[0] + + for (k,f) in self._columns.items(): + vals = { + 'model_id':model_id, + 'model':self._name, + 'name':k, + 'field_description':f.string.replace("'", " "), + 'ttype':f._type, + 'relate':(f.relate and 1) or 0, + 'relation':f._obj or 'NULL', + 'group_name':f.group_name or '', + 'view_load':(f.view_load and 1) or 0, + 'select_level':str(f.select or 0) + } + if k not in cols: + cr.execute('select nextval(%s)', ('ir_model_fields_id_seq',)) + id = cr.fetchone()[0] + vals['id'] = id + cr.execute("""INSERT INTO ir_model_fields ( + id, model_id, model, name, field_description, ttype, + relate,relation,group_name,view_load,state,select_level + ) VALUES ( + %d,%s,%s,%s,%s,%s, %s,%s,%s,%s,%s, %s + )""", ( + id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'], + bool(vals['relate']), vals['relation'], vals['group_name'], bool(vals['view_load']), 'base', + vals['select_level'] + )) + if 'module' in context: + cr.execute("INSERT INTO ir_model_data (name,date_init,date_update,module,model,res_id) VALUES (%s, now(), now(), %s, %s, %s)", \ + ( ('field_'+self._table+'_'+k)[:64], context['module'], 'ir.model.fields', id) + ) + else: + for key,val in vals.items(): + if cols[k][key]<>vals[key]: + print 'Different', cols[k][key], vals[key], k, key, self._name + cr.execute('update ir_model_fields set field_description=%s where model=%s and name=%s', (vals['field_description'],vals['model'],vals['name'])) + cr.commit() + cr.execute("""UPDATE ir_model_fields SET + model_id=%s, field_description=%s, ttype=%s, relate=%s, relation=%s, + group_name=%s, view_load=%s, select_level=%s + WHERE + model=%s AND name=%s""", ( + vals['model_id'],vals['field_description'],vals['ttype'],bool(vals['relate']), + vals['relation'], vals['group_name'], bool(vals['view_load']), + vals['select_level'], vals['model'], vals['name'] + )) + continue + cr.commit() + + def _auto_init(self, cr, context={}): + self._field_create(cr, context) + + def __init__(self, cr): + if not self._description: + self._description = self._name + if not self._table: + self._table=self._name.replace('.','_') + + def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}): + if not context: + context={} + self._list_class = list_class or browse_record_list + cache = {} + # need to accepts ints and longs because ids coming from a method + # launched by button in the interface have a type long... + if isinstance(select, (int, long)): + return browse_record(cr,uid,select,self,cache, context=context, list_class=self._list_class, fields_process=fields_process) + elif isinstance(select,list): + return self._list_class([browse_record(cr,uid,id,self,cache, context=context, list_class=self._list_class, fields_process=fields_process) for id in select], context) + else: + return browse_null() + + def __export_row(self, cr, uid, row, fields, context=None): + lines = [] + data = map(lambda x: '', range(len(fields))) + done = [] + for fpos in range(len(fields)): + f = fields[fpos] + if f: + r = row + i = 0 + while i<len(f): + r = r[f[i]] + if not r: + break + if isinstance(r, (browse_record_list, list)): + first = True + fields2 = map(lambda x: (x[:i+1]==f[:i+1] and x[i+1:]) \ + or [], fields) + if fields2 in done: + break + done.append(fields2) + for row2 in r: + lines2 = self.__export_row(cr, uid, row2, fields2, + context) + if first: + for fpos2 in range(len(fields)): + if lines2 and lines2[0][fpos2]: + data[fpos2] = lines2[0][fpos2] + lines+= lines2[1:] + first = False + else: + lines+= lines2 + break + i+=1 + if i==len(f): + data[fpos] = str(r or '') + return [data] + lines + + def export_data(self, cr, uid, ids, fields, context=None): + if not context: + context={} + fields = map(lambda x: x.split('/'), fields) + datas = [] + for row in self.browse(cr, uid, ids, context): + datas += self.__export_row(cr, uid, row, fields, context) + return datas + + def import_data(self, cr, uid, fields, datas, mode='init', + current_module=None, noupdate=False, context=None): + if not context: + context={} + fields = map(lambda x: x.split('/'), fields) + logger = netsvc.Logger() + def process_liness(self, datas, prefix, fields_def, position=0): + line = datas[position] + row = {} + translate = {} + todo = [] + warning = '' + data_id= False + # + # Import normal fields + # + for i in range(len(fields)): + if i>=len(line): + raise Exception, _('Please check that all your lines have %d columns.') % (len(fields),) + field = fields[i] + if field == ["id"]: + data_id= line[i] + continue + if (len(field)==len(prefix)+1) and field[len(prefix)].endswith(':id'): + res_id = False + if line[i]: + if fields_def[field[len(prefix)][:-3]]['type']=='many2many': + res_id = [] + for word in line[i].split(','): + if '.' in word: + module, xml_id = word.rsplit('.', 1) + else: + module, xml_id = current_module, word + ir_model_data_obj = self.pool.get('ir.model.data') + id = ir_model_data_obj._get_id(cr, uid, module, + xml_id) + res_id2 = ir_model_data_obj.read(cr, uid, [id], + ['res_id'])[0]['res_id'] + if res_id2: + res_id.append(res_id2) + if len(res_id): + res_id=[(6,0,res_id)] + else: + if '.' in line[i]: + module, xml_id = line[i].rsplit('.', 1) + else: + module, xml_id = current_module, line[i] + ir_model_data_obj = self.pool.get('ir.model.data') + id = ir_model_data_obj._get_id(cr, uid, module, xml_id) + res_id = ir_model_data_obj.read(cr, uid, [id], + ['res_id'])[0]['res_id'] + row[field[0][:-3]] = res_id or False + continue + if (len(field) == len(prefix)+1) and \ + len(field[len(prefix)].split(':lang=')) == 2: + f, lang = field[len(prefix)].split(':lang=') + translate.setdefault(lang, {})[f]=line[i] or False + continue + if (len(field) == len(prefix)+1) and \ + (prefix == field[0:len(prefix)]): + if fields_def[field[len(prefix)]]['type'] == 'integer': + res =line[i] and int(line[i]) + elif fields_def[field[len(prefix)]]['type'] == 'float': + res =line[i] and float(line[i]) + elif fields_def[field[len(prefix)]]['type'] == 'selection': + res = False + if isinstance(fields_def[field[len(prefix)]]['selection'], + (tuple, list)): + sel = fields_def[field[len(prefix)]]['selection'] + else: + sel = fields_def[field[len(prefix)]]['selection'](self, + cr, uid, context) + for key, val in sel: + if str(key) == line[i]: + res = key + 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)])) + elif fields_def[field[len(prefix)]]['type']=='many2one': + res = False + if line[i]: + relation = fields_def[field[len(prefix)]]['relation'] + res2 = self.pool.get(relation).name_search(cr, uid, + line[i], [], operator='=') + res = (res2 and res2[0][0]) or False + if not res: + warning += ('Relation not found: ' + line[i] + \ + ' on ' + relation + ' !\n') + logger.notifyChannel("import", netsvc.LOG_WARNING, + 'Relation not found: ' + line[i] + \ + ' on ' + relation + ' !\n') + elif fields_def[field[len(prefix)]]['type']=='many2many': + res = [] + if line[i]: + relation = fields_def[field[len(prefix)]]['relation'] + for word in line[i].split(','): + res2 = self.pool.get(relation).name_search(cr, + uid, word, [], operator='=') + res3 = (res2 and res2[0][0]) or False + if not res3: + warning += ('Relation not found: ' + \ + line[i] + ' on '+relation + ' !\n') + logger.notifyChannel("import", + netsvc.LOG_WARNING, + 'Relation not found: ' + line[i] + \ + ' on '+relation + ' !\n') + else: + res.append(res3) + if len(res): + res= [(6,0,res)] + else: + res = line[i] or False + row[field[len(prefix)]] = res + elif (prefix==field[0:len(prefix)]): + if field[0] not in todo: + todo.append(field[len(prefix)]) + # + # Import one2many fields + # + nbrmax = 1 + for field in todo: + newfd = self.pool.get(fields_def[field]['relation']).fields_get( + cr, uid, context=context) + res = process_liness(self, datas, prefix + [field], newfd, position) + (newrow, max2, w2, translate2, data_id2) = res + nbrmax = max(nbrmax, max2) + warning = warning + w2 + reduce(lambda x,y: x and y, newrow) + row[field] = (reduce(lambda x, y: x or y, newrow.values()) and \ + [(0,0,newrow)]) or [] + i = max2 + while (position+i)<len(datas): + ok = True + for j in range(len(fields)): + field2 = fields[j] + if (len(field2) <= (len(prefix)+1)) and datas[position+i][j]: + ok = False + if not ok: + break + + (newrow, max2, w2, translate2, data_id2) = process_liness( + self, datas, prefix+[field], newfd, position+i) + warning = warning+w2 + if reduce(lambda x,y: x or y, newrow.values()): + row[field].append((0,0,newrow)) + i+=max2 + nbrmax = max(nbrmax, i) + + if len(prefix)==0: + for i in range(max(nbrmax,1)): + #if datas: + datas.pop(0) + result = (row, nbrmax, warning, translate, data_id) + return result + + fields_def = self.fields_get(cr, uid, context=context) + done = 0 + + while len(datas): + res = {} + try: + (res, other, warning, translate, data_id) = \ + process_liness(self, datas, [], fields_def) + if warning: + cr.rollback() + return (-1, res, warning, '') + id= self.pool.get('ir.model.data')._update(cr, uid, self._name, + current_module, res, xml_id=data_id, mode=mode, + noupdate=noupdate) + for lang in translate: + context2=context.copy() + context2['lang']=lang + self.write(cr, uid, [id], translate[lang], context2) + if config.get('commit_mode', False): + cr.commit() + except Exception, e: + logger.notifyChannel("import",netsvc.LOG_ERROR, e) + cr.rollback() + try: + return (-1, res, e[0], warning) + except: + return (-1, res, e[0], '') + done += 1 + # + # TODO: Send a request with the result and multi-thread ! + # + return (done, 0, 0, 0) + + def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): + raise _('The read method is not implemented on this object !') + + def _validate(self, cr, uid, ids, context=None): + context = context or {} + lng = context.get('lang', False) or 'en_US' + trans = self.pool.get('ir.translation') + field_error = [] + field_err_str = [] + for constraint in self._constraints: + fun, msg, fields = constraint + if not fun(self, cr, uid, ids): + field_error += fields + field_err_str.append( trans._get_source(cr, uid, self._name, 'constraint', lng, source=msg) ) + if len(field_err_str): + cr.rollback() + raise except_orm('ValidateError', ('\n'.join(field_err_str), ','.join(field_error))) + + def default_get(self, cr, uid, fields_list, context=None): + return {} + + def perm_read(self, cr, user, ids, context=None, details=True): + raise _('The perm_read method is not implemented on this object !') + + def unlink(self, cr, uid, ids, context=None): + raise _('The unlink method is not implemented on this object !') + + def write(self, cr, user, ids, vals, context=None): + raise _('The write method is not implemented on this object !') + + def create(self, cr, user, vals, context=None): + raise _('The create method is not implemented on this object !') + + # returns the definition of each field in the object + # the optional fields parameter can limit the result to some fields + def fields_get(self, cr, user, fields=None, context=None, read_access=True): + if context is None: + context = {} + res = {} + translation_obj = self.pool.get('ir.translation') + model_access_obj = self.pool.get('ir.model.access') + for parent in self._inherits: + res.update(self.pool.get(parent).fields_get(cr, user, fields, + context)) + for f in self._columns.keys(): + res[f] = {'type': self._columns[f]._type} + for arg in ('string', 'readonly', 'states', 'size', 'required', + 'change_default', 'translate', 'help', 'select'): + if getattr(self._columns[f], arg): + res[f][arg] = getattr(self._columns[f], arg) + if not read_access: + res[f]['readonly']= True + res[f]['states'] = {} + for arg in ('digits', 'invisible'): + if hasattr(self._columns[f], arg) \ + and getattr(self._columns[f], arg): + res[f][arg] = getattr(self._columns[f], arg) + + # translate the field label + res_trans = translation_obj._get_source(cr, user, + self._name + ',' + f, 'field', context.get('lang', False) or 'en_US') + if res_trans: + res[f]['string'] = res_trans + help_trans = translation_obj._get_source(cr, user, + self._name + ',' + f, 'help', context.get('lang', False) or 'en_US') + if help_trans: + res[f]['help'] = help_trans + + if hasattr(self._columns[f], 'selection'): + if isinstance(self._columns[f].selection, (tuple, list)): + sel = self._columns[f].selection + # translate each selection option + sel2 = [] + for (key,val) in sel: + val2 = translation_obj._get_source(cr, user, + self._name + ',' + f, 'selection', + context.get('lang', False) or 'en_US', val) + sel2.append((key, val2 or val)) + sel = sel2 + res[f]['selection'] = sel + else: + # call the 'dynamic selection' function + res[f]['selection'] = self._columns[f].selection(self, cr, + user, context) + if res[f]['type'] in ('one2many', 'many2many', + 'many2one', 'one2one'): + res[f]['relation'] = self._columns[f]._obj + res[f]['domain'] = self._columns[f]._domain + res[f]['context'] = self._columns[f]._context + + if fields: + # filter out fields which aren't in the fields list + for r in res.keys(): + if r not in fields: + del res[r] + return res + + # + # Overload this method if you need a window title which depends on the context + # + def view_header_get(self, cr, user, view_id=None, view_type='form', context=None): + return False + + def __view_look_dom(self, cr, user, node, context=None): + if not context: + context={} + result = False + fields = {} + childs = True + + if node.nodeType==node.ELEMENT_NODE and node.localName=='field': + if node.hasAttribute('name'): + attrs = {} + try: + if node.getAttribute('name') in self._columns: + relation = self._columns[node.getAttribute('name')]._obj + else: + relation = self._inherit_fields[node.getAttribute('name')][2]._obj + except: + relation = False + + if relation: + childs = False + views = {} + for f in node.childNodes: + if f.nodeType==f.ELEMENT_NODE and f.localName in ('form','tree','graph'): + node.removeChild(f) + xarch,xfields = self.pool.get(relation).__view_look_dom_arch(cr, user, f, context) + views[str(f.localName)] = { + 'arch': xarch, + 'fields': xfields + } + attrs = {'views': views} + fields[node.getAttribute('name')] = attrs + + elif node.nodeType==node.ELEMENT_NODE and node.localName in ('form','tree'): + result = self.view_header_get(cr, user, False, node.localName, context) + if result: + node.setAttribute('string', result.decode('utf-8')) + if node.nodeType==node.ELEMENT_NODE and node.hasAttribute('groups'): + groups = None + group_str = node.getAttribute('groups') + if ',' in group_str: + groups = group_str.split(','); + readonly = False + access_pool = self.pool.get('ir.model.access') + for group in groups: + readonly = readonly and access_pool.check_groups(cr, user, group) + + if readonly: + parent = node.parentNode + parent.removeChild(node) + + else: + if not self.pool.get('ir.model.access').check_groups(cr, user, group_str): + parent = node.parentNode + parent.removeChild(node) + + if node.nodeType == node.ELEMENT_NODE: + # translate view + if ('lang' in context) and not result: + if node.hasAttribute('string') and node.getAttribute('string'): + trans = tools.translate(cr, self._name, 'view', context['lang'], node.getAttribute('string').encode('utf8')) + if trans: + node.setAttribute('string', trans.decode('utf8')) + if node.hasAttribute('sum') and node.getAttribute('sum'): + trans = tools.translate(cr, self._name, 'view', context['lang'], node.getAttribute('sum').encode('utf8')) + if trans: + node.setAttribute('sum', trans.decode('utf8')) + # + # Add view for properties ! + # + if node.localName=='properties': + parent = node.parentNode + doc = node.ownerDocument + models = map(lambda x: "'"+x+"'", [self._name] + self._inherits.keys()) + cr.execute('select id,name,group_name from ir_model_fields where model in ('+','.join(models)+') and view_load order by group_name, id') + oldgroup = None + res = cr.fetchall() + for id, fname, gname in res: + if oldgroup != gname: + child = doc.createElement('separator') + child.setAttribute('string', gname.decode('utf-8')) + child.setAttribute('colspan', "4") + oldgroup = gname + parent.insertBefore(child, node) + + child = doc.createElement('field') + child.setAttribute('name', fname.decode('utf-8')) + parent.insertBefore(child, node) + parent.removeChild(node) + + if childs: + for f in node.childNodes: + fields.update(self.__view_look_dom(cr, user, f,context)) + return fields + + def __view_look_dom_arch(self, cr, user, node, context=None): + if not context: + context={} + fields_def = self.__view_look_dom(cr, user, node, context=context) + + buttons = xpath.Evaluate('//button', node) + if buttons: + for button in buttons: + if button.getAttribute('type') == 'object': + continue + + ok = True + + serv = netsvc.LocalService('object_proxy') + user_roles = serv.execute_cr(cr, user, 'res.users', 'read', [user], ['roles_id'])[0]['roles_id'] + cr.execute("select role_id from wkf_transition where signal='%s'" % button.getAttribute('name')) + roles = cr.fetchall() + for role in roles: + if role[0]: + ok = ok and serv.execute_cr(cr, user, 'res.roles', 'check', user_roles, role[0]) + + if not ok: + button.setAttribute('readonly', '1') + else: + button.setAttribute('readonly', '0') + + arch = node.toxml(encoding="utf-8").replace('\t', '') + + fields = self.fields_get(cr, user, fields_def.keys(), context) + for field in fields_def: + fields[field].update(fields_def[field]) + return arch, fields + + + def __get_default_calendar_view(self): + """Generate a default calendar view (For internal use only). + """ + + arch = ('<?xml version="1.0" encoding="utf-8"?>\n' + '<calendar string="%s" date_start="%s"') % (self._description, self._date_name) + + if 'user_id' in self._columns: + arch += ' color="user_id"' + + elif 'partner_id' in self._columns: + arch += ' color="partner_id"' + + if 'date_stop' in self._columns: + arch += ' date_stop="date_stop"' + + elif 'date_end' in self._columns: + arch += ' date_stop="date_end"' + + elif 'date_delay' in self._columns: + arch += ' date_delay="date_delay"' + + elif 'planned_hours' in self._columns: + arch += ' date_delay="planned_hours"' + + arch += ('>\n' + ' <field name="%s"/>\n' + '</calendar>') % (self._rec_name) + + return arch + + # + # if view_id, view_type is not required + # + def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False): + if not context: + context={} + def _inherit_apply(src, inherit): + def _find(node, node2): + # Check if xpath query or normal inherit (with field matching) + if node2.nodeType==node2.ELEMENT_NODE and node2.localName=='xpath': + res = xpath.Evaluate(node2.getAttribute('expr'), node) + return res and res[0] + else: + if node.nodeType==node.ELEMENT_NODE and node.localName==node2.localName: + res = True + for attr in node2.attributes.keys(): + if attr=='position': + continue + if node.hasAttribute(attr): + if node.getAttribute(attr)==node2.getAttribute(attr): + continue + res = False + if res: + return node + for child in node.childNodes: + res = _find(child, node2) + if res: return res + return None + + doc_src = dom.minidom.parseString(src) + doc_dest = dom.minidom.parseString(inherit) + toparse = doc_dest.childNodes + while len(toparse): + node2 = toparse.pop(0) + if not node2.nodeType==node2.ELEMENT_NODE: + continue + if node2.localName=='data': + toparse += node2.childNodes + continue + node = _find(doc_src, node2) + if node: + pos = 'inside' + if node2.hasAttribute('position'): + pos = node2.getAttribute('position') + if pos=='replace': + parent = node.parentNode + for child in node2.childNodes: + if child.nodeType==child.ELEMENT_NODE: + parent.insertBefore(child, node) + parent.removeChild(node) + else: + for child in node2.childNodes: + if child.nodeType==child.ELEMENT_NODE: + if pos=='inside': + node.appendChild(child) + elif pos=='after': + sib = node.nextSibling + if sib: + node.parentNode.insertBefore(child, sib) + else: + node.parentNode.appendChild(child) + elif pos=='before': + node.parentNode.insertBefore(child, node) + else: + raise AttributeError, _('Unknown position in inherited view %s !') % pos + else: + attrs = ''.join([ + ' %s="%s"' % (attr, node2.getAttribute(attr)) + for attr in node2.attributes.keys() + if attr != 'position' + ]) + tag = "<%s%s>" % (node2.localName, attrs) + raise AttributeError, _("Couldn't find tag '%s' in parent view !") % tag + return doc_src.toxml(encoding="utf-8").replace('\t', '') + + result = {'type':view_type, 'model':self._name} + + ok = True + model = True + sql_res = False + while ok: + if view_id: + where = (model and (" and model='%s'" % (self._name,))) or '' + cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE id=%d'+where, (view_id,)) + else: + cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE model=%s AND type=%s ORDER BY priority', (self._name,view_type)) + sql_res = cr.fetchone() + if not sql_res: + break + ok = sql_res[5] + view_id = ok or sql_res[3] + model = False + + # if a view was found + if sql_res: + result['type'] = sql_res[4] + result['view_id'] = sql_res[3] + result['arch'] = sql_res[0] + + def _inherit_apply_rec(result, inherit_id): + # get all views which inherit from (ie modify) this view + cr.execute('select arch,id from ir_ui_view where inherit_id=%d and model=%s order by priority', (inherit_id, self._name)) + sql_inherit = cr.fetchall() + for (inherit,id) in sql_inherit: + result = _inherit_apply(result, inherit) + result = _inherit_apply_rec(result, id) + return result + + result['arch'] = _inherit_apply_rec(result['arch'], sql_res[3]) + + result['name'] = sql_res[1] + result['field_parent'] = sql_res[2] or False + else: + # otherwise, build some kind of default view + if view_type == 'form': + res = self.fields_get(cr, user, context=context) + xml = '''<?xml version="1.0" encoding="utf-8"?>''' \ + '''<form string="%s">''' % (self._description,) + for x in res: + if res[x]['type'] not in ('one2many', 'many2many'): + xml += '<field name="%s"/>' % (x,) + if res[x]['type'] == 'text': + xml += "<newline/>" + xml += "</form>" + elif view_type == 'tree': + xml = '''<?xml version="1.0" encoding="utf-8"?>''' \ + '''<tree string="%s"><field name="%s"/></tree>''' \ + % (self._description, self._rec_name) + elif view_type == 'calendar': + xml = self.__get_default_calendar_view() + else: + xml = '' + result['arch'] = xml + result['name'] = 'default' + result['field_parent'] = False + result['view_id'] = 0 + + doc = dom.minidom.parseString(result['arch'].encode('utf-8')) + xarch, xfields = self.__view_look_dom_arch(cr, user, doc, context=context) + result['arch'] = xarch + result['fields'] = xfields + if toolbar: + def clean(x): + x = x[2] + for key in ('report_sxw_content', 'report_rml_content', + 'report_sxw', 'report_rml', + 'report_sxw_content_data', 'report_rml_content_data'): + if key in x: + del x[key] + return x + ir_values_obj = self.pool.get('ir.values') + resprint = ir_values_obj.get(cr, user, 'action', + 'client_print_multi', [(self._name, False)], False, + context) + resaction = ir_values_obj.get(cr, user, 'action', + 'client_action_multi', [(self._name, False)], False, + context) + + resrelate = ir_values_obj.get(cr, user, 'action', + 'client_action_relate', [(self._name, False)], False, + context) + resprint = map(clean, resprint) + resaction = map(clean, resaction) + resaction = filter(lambda x: not x.get('multi',False), resaction) + resprint = filter(lambda x: not x.get('multi',False), resprint) + resrelate = map(lambda x:x[2], resrelate) + + for x in resprint+resaction+resrelate: + x['string'] = x['name'] + + result['toolbar'] = { + 'print': resprint, + 'action': resaction, + 'relate': resrelate + } + return result + + _view_look_dom_arch=__view_look_dom_arch + + def search_count(self, cr, user, args, context=None): + if not context: + context = {} + res = self.search(cr, user, args, context=context, count=True) + if isinstance(res, list): + return len(res) + return res + + def search(self, cr, user, args, offset=0, limit=None, order=None, + context=None, count=False): + raise _('The search method is not implemented on this object !') + + def name_get(self, cr, user, ids, context=None): + raise _('The name_get method is not implemented on this object !') + + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): + raise _('The name_search method is not implemented on this object !') + + def copy(self, cr, uid, id, default=None, context=None): + raise _('The copy method is not implemented on this object !') class orm_memory(orm_template): - _protected = ['read','write','create','default_get','perm_read','unlink','fields_get','fields_view_get','search','name_get','distinct_field_get','name_search','copy','import_data','search_count'] - _inherit_fields = {} - _max_count = 200 - _max_hours = 1 - _check_time = 20 - def __init__(self, cr): - super(orm_memory, self).__init__(cr) - self.datas = {} - self.next_id = 0 - self.check_id = 0 - cr.execute('delete from wkf_instance where res_type=%s', (self._name,)) - - def clear(self): - self.check_id+=1 - if self.check_id % self._check_time: - return True - tounlink = [] - max = time.time() - self._max_hours * 60 * 60 - for id in self.datas: - if self.datas[id]['internal.date_access'] < max: - tounlink.append(id) - self.unlink(tounlink) - if len(self.datas)>self._max_count: - sorted = map(lambda x: (x[1]['internal.date_access'], x[0]), self.datas.items()) - sorted.sort() - ids = map(lambda x: x[1], sorted[:len(self.datas)-self._max_count]) - self.unlink(ids) - return True - - def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): - if not fields: - fields = self._columns.keys() - result = [] - for id in ids: - r = {'id': id} - for f in fields: - r[f] = self.datas[id].get(f, False) - result.append(r) - self.datas[id]['internal.date_access'] = time.time() - fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) - for f in fields_post: - res2 = self._columns[f].get_memory(cr, self, ids, f, user, context=context, values=False) - for record in result: - record[f] = res2[record['id']] - return result - - def write(self, cr, user, ids, vals, context=None): - vals2 = {} - upd_todo = [] - for field in vals: - if self._columns[field]._classic_write: - vals2[field] = vals[field] - else: - upd_todo.append(field) - for id_new in ids: - self.datas[id_new].update(vals2) - self.datas[id_new]['internal.date_access'] = time.time() - for field in upd_todo: - self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context) - self._validate(cr, user, [id_new], context) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_write(user, self._name, id_new, cr) - self.clear() - return id_new - - def create(self, cr, user, vals, context=None): - self.next_id += 1 - id_new = self.next_id - default = [] - for f in self._columns.keys(): - if not f in vals: - default.append(f) - if len(default): - vals.update(self.default_get(cr, user, default, context)) - vals2 = {} - upd_todo = [] - for field in vals: - if self._columns[field]._classic_write: - vals2[field] = vals[field] - else: - upd_todo.append(field) - self.datas[id_new] = vals2 - self.datas[id_new]['internal.date_access'] = time.time() - for field in upd_todo: - self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context) - self._validate(cr, user, [id_new], context) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_create(user, self._name, id_new, cr) - self.clear() - return id_new - - def default_get(self, cr, uid, fields_list, context=None): - if not context: - context={} - value = {} - # get the default values for the inherited fields - for f in fields_list: - if f in self._defaults: - value[f] = self._defaults[f](self, cr, uid, context) - fld_def = ((f in self._columns) and self._columns[f]) \ - or ((f in self._inherit_fields ) and self._inherit_fields[f][2]) \ - or False - - # get the default values set by the user and override the default - # values defined in the object - ir_values_obj = self.pool.get('ir.values') - res = ir_values_obj.get(cr, uid, 'default', False, [self._name]) - for id, field, field_value in res: - if field in fields_list: - fld_def = (field in self._columns) - if fld_def._type in ('many2one', 'one2one'): - obj = self.pool.get(fld_def._obj) - if not obj.search(cr, uid, [('id', '=', field_value)]): - continue - if fld_def._type in ('many2many'): - obj = self.pool.get(fld_def._obj) - field_value2 = [] - for i in range(len(field_value)): - if not obj.search(cr, uid, [('id', '=', - field_value[i])]): - continue - field_value2.append(field_value[i]) - field_value = field_value2 - if fld_def._type in ('one2many'): - obj = self.pool.get(fld_def._obj) - field_value2 = [] - for i in range(len(field_value)): - field_value2.append({}) - for field2 in field_value[i]: - if obj._columns[field2]._type in ('many2one', 'one2one'): - obj2 = self.pool.get(obj._columns[field2]._obj) - if not obj2.search(cr, uid, - [('id', '=', field_value[i][field2])]): - continue - # TODO add test for many2many and one2many - field_value2[i][field2] = field_value[i][field2] - field_value = field_value2 - value[field] = field_value - return value - - def search(self, cr, user, args, offset=0, limit=None, order=None, - context=None, count=False): - return self.datas.keys() - - def unlink(self, cr, uid, ids, context=None): - for id in ids: - if id in self.datas: - del self.datas[id] - if len(ids): - cr.execute('delete from wkf_instance where res_type=%s and res_id in ('+','.join(map(str,ids))+')', (self._name, )) - return True - - def perm_read(self, cr, user, ids, context=None, details=True): - result = [] - for id in ids: - result.append({ - 'create_uid': (user, 'Root'), - 'create_date': time.strftime('%Y-%m-%d %H:%M:%S'), - 'write_uid': False, - 'write_date': False, - 'id': id - }) - return result + _protected = ['read','write','create','default_get','perm_read','unlink','fields_get','fields_view_get','search','name_get','distinct_field_get','name_search','copy','import_data','search_count'] + _inherit_fields = {} + _max_count = 200 + _max_hours = 1 + _check_time = 20 + def __init__(self, cr): + super(orm_memory, self).__init__(cr) + self.datas = {} + self.next_id = 0 + self.check_id = 0 + cr.execute('delete from wkf_instance where res_type=%s', (self._name,)) + + def clear(self): + self.check_id+=1 + if self.check_id % self._check_time: + return True + tounlink = [] + max = time.time() - self._max_hours * 60 * 60 + for id in self.datas: + if self.datas[id]['internal.date_access'] < max: + tounlink.append(id) + self.unlink(tounlink) + if len(self.datas)>self._max_count: + sorted = map(lambda x: (x[1]['internal.date_access'], x[0]), self.datas.items()) + sorted.sort() + ids = map(lambda x: x[1], sorted[:len(self.datas)-self._max_count]) + self.unlink(ids) + return True + + def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): + if not fields: + fields = self._columns.keys() + result = [] + for id in ids: + r = {'id': id} + for f in fields: + r[f] = self.datas[id].get(f, False) + result.append(r) + self.datas[id]['internal.date_access'] = time.time() + fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) + for f in fields_post: + res2 = self._columns[f].get_memory(cr, self, ids, f, user, context=context, values=False) + for record in result: + record[f] = res2[record['id']] + return result + + def write(self, cr, user, ids, vals, context=None): + vals2 = {} + upd_todo = [] + for field in vals: + if self._columns[field]._classic_write: + vals2[field] = vals[field] + else: + upd_todo.append(field) + for id_new in ids: + self.datas[id_new].update(vals2) + self.datas[id_new]['internal.date_access'] = time.time() + for field in upd_todo: + self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context) + self._validate(cr, user, [id_new], context) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_write(user, self._name, id_new, cr) + self.clear() + return id_new + + def create(self, cr, user, vals, context=None): + self.next_id += 1 + id_new = self.next_id + default = [] + for f in self._columns.keys(): + if not f in vals: + default.append(f) + if len(default): + vals.update(self.default_get(cr, user, default, context)) + vals2 = {} + upd_todo = [] + for field in vals: + if self._columns[field]._classic_write: + vals2[field] = vals[field] + else: + upd_todo.append(field) + self.datas[id_new] = vals2 + self.datas[id_new]['internal.date_access'] = time.time() + for field in upd_todo: + self._columns[field].set_memory(cr, self, id_new, field, vals[field], user, context) + self._validate(cr, user, [id_new], context) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_create(user, self._name, id_new, cr) + self.clear() + return id_new + + def default_get(self, cr, uid, fields_list, context=None): + if not context: + context={} + value = {} + # get the default values for the inherited fields + for f in fields_list: + if f in self._defaults: + value[f] = self._defaults[f](self, cr, uid, context) + fld_def = ((f in self._columns) and self._columns[f]) \ + or ((f in self._inherit_fields ) and self._inherit_fields[f][2]) \ + or False + + # get the default values set by the user and override the default + # values defined in the object + ir_values_obj = self.pool.get('ir.values') + res = ir_values_obj.get(cr, uid, 'default', False, [self._name]) + for id, field, field_value in res: + if field in fields_list: + fld_def = (field in self._columns) + if fld_def._type in ('many2one', 'one2one'): + obj = self.pool.get(fld_def._obj) + if not obj.search(cr, uid, [('id', '=', field_value)]): + continue + if fld_def._type in ('many2many'): + obj = self.pool.get(fld_def._obj) + field_value2 = [] + for i in range(len(field_value)): + if not obj.search(cr, uid, [('id', '=', + field_value[i])]): + continue + field_value2.append(field_value[i]) + field_value = field_value2 + if fld_def._type in ('one2many'): + obj = self.pool.get(fld_def._obj) + field_value2 = [] + for i in range(len(field_value)): + field_value2.append({}) + for field2 in field_value[i]: + if obj._columns[field2]._type in ('many2one', 'one2one'): + obj2 = self.pool.get(obj._columns[field2]._obj) + if not obj2.search(cr, uid, + [('id', '=', field_value[i][field2])]): + continue + # TODO add test for many2many and one2many + field_value2[i][field2] = field_value[i][field2] + field_value = field_value2 + value[field] = field_value + return value + + def search(self, cr, user, args, offset=0, limit=None, order=None, + context=None, count=False): + return self.datas.keys() + + def unlink(self, cr, uid, ids, context=None): + for id in ids: + if id in self.datas: + del self.datas[id] + if len(ids): + cr.execute('delete from wkf_instance where res_type=%s and res_id in ('+','.join(map(str,ids))+')', (self._name, )) + return True + + def perm_read(self, cr, user, ids, context=None, details=True): + result = [] + for id in ids: + result.append({ + 'create_uid': (user, 'Root'), + 'create_date': time.strftime('%Y-%m-%d %H:%M:%S'), + 'write_uid': False, + 'write_date': False, + 'id': id + }) + return result class orm(orm_template): - _sql_constraints = [] - - _log_access = True - _table = None - _protected = ['read','write','create','default_get','perm_read','unlink','fields_get','fields_view_get','search','name_get','distinct_field_get','name_search','copy','import_data','search_count'] - def _auto_init(self, cr, context={}): - logger = netsvc.Logger() - create = False - self._field_create(cr, context=context) - if not hasattr(self, "_auto") or self._auto: - cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) - if not cr.rowcount: - cr.execute("CREATE TABLE \"%s\" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITH OIDS" % self._table) - create = True - cr.commit() - if self._log_access: - logs = { - 'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', - 'create_date': 'TIMESTAMP', - 'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', - 'write_date': 'TIMESTAMP' - } - for k in logs: - cr.execute( - """ - SELECT c.relname - FROM pg_class c, pg_attribute a - WHERE c.relname='%s' AND a.attname='%s' AND c.oid=a.attrelid - """ % (self._table, k)) - if not cr.rowcount: - cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s" % - (self._table, k, logs[k])) - cr.commit() - - # iterate on the database columns to drop the NOT NULL constraints - # of fields which were required but have been removed - cr.execute( - "SELECT a.attname, a.attnotnull "\ - "FROM pg_class c, pg_attribute a "\ - "WHERE c.oid=a.attrelid AND c.relname='%s'" % self._table) - db_columns = cr.dictfetchall() - for column in db_columns: - if column['attname'] not in ('id', 'oid', 'tableoid', 'ctid', 'xmin', 'xmax', 'cmin', 'cmax'): - if column['attnotnull'] and column['attname'] not in self._columns: - cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table, column['attname'])) - - # iterate on the "object columns" - for k in self._columns: - if k in ('id','write_uid','write_date','create_uid','create_date'): - continue - #raise _('Can not define a column %s. Reserved keyword !') % (k,) - f = self._columns[k] - - if isinstance(f, fields.one2many): - cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,)) - if cr.fetchone(): - cr.execute("SELECT count(*) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj,f._fields_id)) - res = cr.fetchone()[0] - if not res: - cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (%s) REFERENCES \"%s\" ON DELETE SET NULL" % (self._obj, f._fields_id, f._table)) - elif isinstance(f, fields.many2many): - cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (f._rel,)) - if not cr.dictfetchall(): - #FIXME: Remove this try/except - try: - ref = self.pool.get(f._obj)._table - except AttributeError: - ref = f._obj.replace('.','_') - cr.execute("CREATE TABLE \"%s\" (\"%s\" INTEGER NOT NULL REFERENCES \"%s\" ON DELETE CASCADE, \"%s\" INTEGER NOT NULL REFERENCES \"%s\" ON DELETE CASCADE) WITH OIDS"%(f._rel,f._id1,self._table,f._id2,ref)) - cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (f._rel,f._id1,f._rel,f._id1)) - cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (f._rel,f._id2,f._rel,f._id2)) - cr.commit() - else: - cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size FROM pg_class c,pg_attribute a,pg_type t WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid AND a.atttypid=t.oid", (self._table, k)) - res = cr.dictfetchall() - if not res: - if not isinstance(f,fields.function) or f.store: - - # add the missing field - cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s" % (self._table, k, get_pg_type(f)[1])) - - # initialize it - if not create and k in self._defaults: - default = self._defaults[k](self, cr, 1, {}) - if not default: - cr.execute("UPDATE \"%s\" SET \"%s\"=NULL" % (self._table, k)) - else: - cr.execute("UPDATE \"%s\" SET \"%s\"='%s'" % (self._table, k, default)) - if isinstance(f,fields.function): - cr.execute('select id from '+self._table) - ids_lst = map(lambda x: x[0], cr.fetchall()) - while ids_lst: - iids = ids_lst[:40] - ids_lst = ids_lst[40:] - res = f.get(cr, self, iids, k, 1, {}) - for r in res.items(): - cr.execute("UPDATE \"%s\" SET \"%s\"='%s' where id=%d"% (self._table, k, r[1],r[0])) - - # and add constraints if needed - if isinstance(f, fields.many2one): - #FIXME: Remove this try/except - try: - ref = self.pool.get(f._obj)._table - except AttributeError: - ref = f._obj.replace('.','_') - # ir_actions is inherited so foreign key doesn't work on it - if ref <> 'ir_actions': - cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (\"%s\") REFERENCES \"%s\" ON DELETE %s" % (self._table, k, ref, f.ondelete)) - if f.select: - cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) - if f.required: - cr.commit() - try: - cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" SET NOT NULL" % (self._table, k)) - except: - logger.notifyChannel('init', netsvc.LOG_WARNING, 'WARNING: unable to set column %s of table %s not null !\nTry to re-run: tinyerp-server.py --update=module\nIf it doesn\'t work, update records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) - cr.commit() - elif len(res)==1: - f_pg_def = res[0] - f_pg_type = f_pg_def['typname'] - f_pg_size = f_pg_def['size'] - f_pg_notnull = f_pg_def['attnotnull'] - if isinstance(f, fields.function) and not f.store: - logger.notifyChannel('init', netsvc.LOG_WARNING, 'column %s (%s) in table %s was converted to a function !\nYou should remove this column from your database.' % (k, f.string, self._table)) - f_obj_type = None - else: - f_obj_type = get_pg_type(f) and get_pg_type(f)[0] - - if f_obj_type: - if f_pg_type != f_obj_type: - logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed type (DB = %s, def = %s) !" % (k, self._table, f_pg_type, f._type)) - if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size != f.size: - # columns with the name 'type' cannot be changed for an unknown reason?! - if k != 'type': - if f_pg_size > f.size: - logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed size (DB = %d, def = %d), strings will be truncated !" % (k, self._table, f_pg_size, f.size)) + _sql_constraints = [] + + _log_access = True + _table = None + _protected = ['read','write','create','default_get','perm_read','unlink','fields_get','fields_view_get','search','name_get','distinct_field_get','name_search','copy','import_data','search_count'] + def _auto_init(self, cr, context={}): + logger = netsvc.Logger() + create = False + self._field_create(cr, context=context) + if not hasattr(self, "_auto") or self._auto: + cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) + if not cr.rowcount: + cr.execute("CREATE TABLE \"%s\" (id SERIAL NOT NULL, PRIMARY KEY(id)) WITH OIDS" % self._table) + create = True + cr.commit() + if self._log_access: + logs = { + 'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', + 'create_date': 'TIMESTAMP', + 'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', + 'write_date': 'TIMESTAMP' + } + for k in logs: + cr.execute( + """ + SELECT c.relname + FROM pg_class c, pg_attribute a + WHERE c.relname='%s' AND a.attname='%s' AND c.oid=a.attrelid + """ % (self._table, k)) + if not cr.rowcount: + cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s" % + (self._table, k, logs[k])) + cr.commit() + + # iterate on the database columns to drop the NOT NULL constraints + # of fields which were required but have been removed + cr.execute( + "SELECT a.attname, a.attnotnull "\ + "FROM pg_class c, pg_attribute a "\ + "WHERE c.oid=a.attrelid AND c.relname='%s'" % self._table) + db_columns = cr.dictfetchall() + for column in db_columns: + if column['attname'] not in ('id', 'oid', 'tableoid', 'ctid', 'xmin', 'xmax', 'cmin', 'cmax'): + if column['attnotnull'] and column['attname'] not in self._columns: + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table, column['attname'])) + + # iterate on the "object columns" + for k in self._columns: + if k in ('id','write_uid','write_date','create_uid','create_date'): + continue + #raise _('Can not define a column %s. Reserved keyword !') % (k,) + f = self._columns[k] + + if isinstance(f, fields.one2many): + cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,)) + if cr.fetchone(): + cr.execute("SELECT count(*) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj,f._fields_id)) + res = cr.fetchone()[0] + if not res: + cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (%s) REFERENCES \"%s\" ON DELETE SET NULL" % (self._obj, f._fields_id, f._table)) + elif isinstance(f, fields.many2many): + cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (f._rel,)) + if not cr.dictfetchall(): + #FIXME: Remove this try/except + try: + ref = self.pool.get(f._obj)._table + except AttributeError: + ref = f._obj.replace('.','_') + cr.execute("CREATE TABLE \"%s\" (\"%s\" INTEGER NOT NULL REFERENCES \"%s\" ON DELETE CASCADE, \"%s\" INTEGER NOT NULL REFERENCES \"%s\" ON DELETE CASCADE) WITH OIDS"%(f._rel,f._id1,self._table,f._id2,ref)) + cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (f._rel,f._id1,f._rel,f._id1)) + cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (f._rel,f._id2,f._rel,f._id2)) + cr.commit() + else: + cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size FROM pg_class c,pg_attribute a,pg_type t WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid AND a.atttypid=t.oid", (self._table, k)) + res = cr.dictfetchall() + if not res: + if not isinstance(f,fields.function) or f.store: + + # add the missing field + cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s" % (self._table, k, get_pg_type(f)[1])) + + # initialize it + if not create and k in self._defaults: + default = self._defaults[k](self, cr, 1, {}) + if not default: + cr.execute("UPDATE \"%s\" SET \"%s\"=NULL" % (self._table, k)) + else: + cr.execute("UPDATE \"%s\" SET \"%s\"='%s'" % (self._table, k, default)) + if isinstance(f,fields.function): + cr.execute('select id from '+self._table) + ids_lst = map(lambda x: x[0], cr.fetchall()) + while ids_lst: + iids = ids_lst[:40] + ids_lst = ids_lst[40:] + res = f.get(cr, self, iids, k, 1, {}) + for r in res.items(): + cr.execute("UPDATE \"%s\" SET \"%s\"='%s' where id=%d"% (self._table, k, r[1],r[0])) + + # and add constraints if needed + if isinstance(f, fields.many2one): + #FIXME: Remove this try/except + try: + ref = self.pool.get(f._obj)._table + except AttributeError: + ref = f._obj.replace('.','_') + # ir_actions is inherited so foreign key doesn't work on it + if ref <> 'ir_actions': + cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (\"%s\") REFERENCES \"%s\" ON DELETE %s" % (self._table, k, ref, f.ondelete)) + if f.select: + cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) + if f.required: + cr.commit() + try: + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" SET NOT NULL" % (self._table, k)) + except: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'WARNING: unable to set column %s of table %s not null !\nTry to re-run: tinyerp-server.py --update=module\nIf it doesn\'t work, update records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) + cr.commit() + elif len(res)==1: + f_pg_def = res[0] + f_pg_type = f_pg_def['typname'] + f_pg_size = f_pg_def['size'] + f_pg_notnull = f_pg_def['attnotnull'] + if isinstance(f, fields.function) and not f.store: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'column %s (%s) in table %s was converted to a function !\nYou should remove this column from your database.' % (k, f.string, self._table)) + f_obj_type = None + else: + f_obj_type = get_pg_type(f) and get_pg_type(f)[0] + + if f_obj_type: + if f_pg_type != f_obj_type: + logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed type (DB = %s, def = %s) !" % (k, self._table, f_pg_type, f._type)) + if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size != f.size: + # columns with the name 'type' cannot be changed for an unknown reason?! + if k != 'type': + if f_pg_size > f.size: + logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed size (DB = %d, def = %d), strings will be truncated !" % (k, self._table, f_pg_size, f.size)) #TODO: check si y a des donnees qui vont poser probleme (select char_length(...)) #TODO: issue a log message even if f_pg_size < f.size - cr.execute("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO temp_change_size" % (self._table,k)) - cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" VARCHAR(%d)" % (self._table,k,f.size)) - cr.execute("UPDATE \"%s\" SET \"%s\"=temp_change_size::VARCHAR(%d)" % (self._table,k,f.size)) - cr.execute("ALTER TABLE \"%s\" DROP COLUMN temp_change_size" % (self._table,)) - cr.commit() - # if the field is required and hasn't got a NOT NULL constraint - if f.required and f_pg_notnull == 0: - # set the field to the default value if any - if self._defaults.has_key(k): - default = self._defaults[k](self, cr, 1, {}) - if not (default is False): - cr.execute("UPDATE \"%s\" SET \"%s\"='%s' WHERE %s is NULL" % (self._table, k, default, k)) - cr.commit() - # add the NOT NULL constraint - try: - cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" SET NOT NULL" % (self._table, k)) - cr.commit() - except: - logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to set a NOT NULL constraint on column %s of the %s table !\nIf you want to have it, you should update the records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) - cr.commit() - elif not f.required and f_pg_notnull == 1: - cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table,k)) - cr.commit() - cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = '%s_%s_index' and tablename = '%s'" % (self._table, k, self._table)) - res = cr.dictfetchall() - if not res and f.select: - cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) - cr.commit() - if res and not f.select: - cr.execute("DROP INDEX \"%s_%s_index\"" % (self._table, k)) - cr.commit() - if isinstance(f, fields.many2one): - ref = self.pool.get(f._obj)._table - if ref != 'ir_actions': - cr.execute('SELECT confdeltype, conname FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, ' \ - 'pg_attribute as att1, pg_attribute as att2 ' \ - 'WHERE con.conrelid = cl1.oid ' \ - 'AND cl1.relname = %s ' \ - 'AND con.confrelid = cl2.oid ' \ - 'AND cl2.relname = %s ' \ - 'AND array_lower(con.conkey, 1) = 1 ' \ - 'AND con.conkey[1] = att1.attnum ' \ - 'AND att1.attrelid = cl1.oid ' \ - 'AND att1.attname = %s ' \ - 'AND array_lower(con.confkey, 1) = 1 ' \ - 'AND con.confkey[1] = att2.attnum ' \ - 'AND att2.attrelid = cl2.oid ' \ - 'AND att2.attname = %s ' \ - 'AND con.contype = \'f\'', (self._table, ref, k, 'id')) - res = cr.dictfetchall() - if res: - confdeltype = { - 'RESTRICT': 'r', - 'NO ACTION': 'a', - 'CASCADE': 'c', - 'SET NULL': 'n', - 'SET DEFAULT': 'd', - } - if res[0]['confdeltype'] != confdeltype.get(f.ondelete.upper(), 'a'): - cr.execute('ALTER TABLE "' + self._table + '" DROP CONSTRAINT "' + res[0]['conname'] + '"') - cr.execute('ALTER TABLE "' + self._table + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete) - cr.commit() - else: - print "ERROR" - else: - cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) - create = not bool(cr.fetchone()) - - for (key,con,_) in self._sql_constraints: - cr.execute("SELECT conname FROM pg_constraint where conname='%s_%s'" % (self._table, key)) - if not cr.dictfetchall(): - try: - cr.execute('alter table \"%s\" add constraint \"%s_%s\" %s' % (self._table,self._table,key, con,)) - cr.commit() - except: - logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table,self._table,key, con,)) - - if create: - if hasattr(self,"_sql"): - for line in self._sql.split(';'): - line2 = line.replace('\n','').strip() - if line2: - cr.execute(line2) - cr.commit() - - def __init__(self, cr): - super(orm, self).__init__(cr) - f=filter(lambda a: isinstance(self._columns[a], fields.function) and self._columns[a].store, self._columns) - if f: - list_store = [] - tuple_store = () - tuple_fn = () - for store_field in f: - if not self._columns[store_field].store == True: - dict_store = self._columns[store_field].store - key = dict_store.keys() - list_data=[] - for i in key: - tuple_store=self._name,store_field,self._columns[store_field]._fnct.__name__,tuple(dict_store[i][0]),dict_store[i][1],i - list_data.append(tuple_store) - #tuple_store=self._name,store_field,self._columns[store_field]._fnct.__name__,tuple(dict_store[key[0]][0]),dict_store[key[0]][1] - for l in list_data: - list_store=[] - if l[5] in self.pool._store_function.keys(): - self.pool._store_function[l[5]].append(l) - temp_list = list(set(self.pool._store_function[l[5]])) - self.pool._store_function[l[5]]=temp_list - else: - list_store.append(l) - self.pool._store_function[l[5]]=list_store - - for (key,_,msg) in self._sql_constraints: - self.pool._sql_error[self._table+'_'+key] = msg - - # Load manual fields - - cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state','ir.model.fields')) - if cr.fetchone(): - cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name,'manual')) - for field in cr.dictfetchall(): - if field['name'] in self._columns: - continue - attrs = { - 'string': field['field_description'], - 'required': bool(field['required']), - 'readonly': bool(field['readonly']), - 'domain': field['domain'] or None, - 'size': field['size'], - 'ondelete': field['on_delete'], - 'translate': (field['translate']), - #'select': int(field['select_level']) - } - #if field['relation']: - # attrs['relation'] = field['relation'] - if field['ttype'] == 'selection': - self._columns[field['name']] = getattr(fields, field['ttype'])(eval(field['selection']), **attrs) - elif field['ttype'] == 'many2one': - self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], **attrs) - else: - self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs) - - self._inherits_reload() - if not self._sequence: - self._sequence = self._table+'_id_seq' - for k in self._defaults: - assert (k in self._columns) or (k in self._inherit_fields), 'Default function defined in %s but field %s does not exist !' % (self._name, k,) - for f in self._columns: - self._columns[f].restart() - - def default_get(self, cr, uid, fields_list, context=None): - if not context: - context={} - value = {} - # get the default values for the inherited fields - for t in self._inherits.keys(): - value.update(self.pool.get(t).default_get(cr, uid, fields_list, - context)) - - # get the default values defined in the object - for f in fields_list: - if f in self._defaults: - value[f] = self._defaults[f](self, cr, uid, context) - fld_def = ((f in self._columns) and self._columns[f]) \ - or ((f in self._inherit_fields ) and self._inherit_fields[f][2]) \ - or False - if isinstance(fld_def, fields.property): - property_obj = self.pool.get('ir.property') - definition_id = fld_def._field_get(cr, uid, self._name, f) - nid = property_obj.search(cr, uid, [('fields_id', '=', - definition_id), ('res_id', '=', False)]) - if nid: - prop_value = property_obj.browse(cr, uid, nid[0], - context=context).value - value[f] = (prop_value and int(prop_value.split(',')[1])) \ - or False - - # get the default values set by the user and override the default - # values defined in the object - ir_values_obj = self.pool.get('ir.values') - res = ir_values_obj.get(cr, uid, 'default', False, [self._name]) - for id, field, field_value in res: - if field in fields_list: - fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2] - if fld_def._type in ('many2one', 'one2one'): - obj = self.pool.get(fld_def._obj) - if not obj.search(cr, uid, [('id', '=', field_value)]): - continue - if fld_def._type in ('many2many'): - obj = self.pool.get(fld_def._obj) - field_value2 = [] - for i in range(len(field_value)): - if not obj.search(cr, uid, [('id', '=', - field_value[i])]): - continue - field_value2.append(field_value[i]) - field_value = field_value2 - if fld_def._type in ('one2many'): - obj = self.pool.get(fld_def._obj) - field_value2 = [] - for i in range(len(field_value)): - field_value2.append({}) - for field2 in field_value[i]: - if obj._columns[field2]._type in ('many2one', 'one2one'): - obj2 = self.pool.get(obj._columns[field2]._obj) - if not obj2.search(cr, uid, - [('id', '=', field_value[i][field2])]): - continue - # TODO add test for many2many and one2many - field_value2[i][field2] = field_value[i][field2] - field_value = field_value2 - value[field] = field_value - return value - - - # - # Update objects that uses this one to update their _inherits fields - # - def _inherits_reload_src(self): - for obj in self.pool.obj_pool.values(): - if self._name in obj._inherits: - obj._inherits_reload() - - def _inherits_reload(self): - res = {} - for table in self._inherits: - res.update(self.pool.get(table)._inherit_fields) - for col in self.pool.get(table)._columns.keys(): - res[col]=(table, self._inherits[table], self.pool.get(table)._columns[col]) - for col in self.pool.get(table)._inherit_fields.keys(): - res[col]=(table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2]) - self._inherit_fields=res - self._inherits_reload_src() - - def fields_get(self, cr, user, fields=None, context=None): - read_access= self.pool.get('ir.model.access').check(cr, user, self._name, 'write', raise_exception=False) - return super(orm, self).fields_get(cr, user, fields, context, read_access) - - def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): - if not context: - context={} - self.pool.get('ir.model.access').check(cr, user, self._name, 'read') - if not fields: - fields = self._columns.keys() + self._inherit_fields.keys() - select = ids - if isinstance(ids, (int, long)): - select = [ids] - result = self._read_flat(cr, user, select, fields, context, load) - for r in result: - for key,v in r.items(): - if v == None: - r[key]=False - if isinstance(ids, (int, long)): - return result[0] - return result - - def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'): - if not context: - context={} - if not ids: - return [] - - if fields==None: - fields = self._columns.keys() - - # construct a clause for the rules : - d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) - - # all inherited fields + all non inherited fields for which the attribute whose name is in load is True - fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values() - - res = [] - if len(fields_pre) : - fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre) - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] - if d1: - cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) AND %s ORDER BY %s' % \ - (','.join(fields_pre2 + ['id']), self._table, - ','.join([str(x) for x in sub_ids]), d1, - self._order),d2) - if not cr.rowcount == len({}.fromkeys(sub_ids)): - raise except_orm(_('AccessError'), - _('You try to bypass an access rule (Document type: %s).') % self._description) - else: - cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) ORDER BY %s' % \ - (','.join(fields_pre2 + ['id']), self._table, - ','.join([str(x) for x in sub_ids]), - self._order)) - res.extend(cr.dictfetchall()) - else: - res = map(lambda x: {'id': x}, ids) - - for f in fields_pre: - if self._columns[f].translate: - ids = map(lambda x: x['id'], res) - res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids) - for r in res: - r[f] = res_trans.get(r['id'], False) or r[f] - - for table in self._inherits: - col = self._inherits[table] - cols = intersect(self._inherit_fields.keys(), fields) - if not cols: - continue - res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load) - - res3 = {} - for r in res2: - res3[r['id']] = r - del r['id'] - - for record in res: - record.update(res3[record[col]]) - if col not in fields: - del record[col] - - # all fields which need to be post-processed by a simple function (symbol_get) - fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields) - if fields_post: - # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need - # to get the _symbol_get in each occurence - for r in res: - for f in fields_post: - r[f] = self.columns[f]._symbol_get(r[f]) - ids = map(lambda x: x['id'], res) - - # all non inherited fields for which the attribute whose name is in load is False - fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) - for f in fields_post: - # get the value of that field for all records/ids - res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res) - for record in res: - record[f] = res2[record['id']] - - readonly = None - for vals in res: - for field in vals.copy(): - fobj = None - if field in self._columns: - fobj = self._columns[field] - - if not fobj: - continue - groups = fobj.read - if groups: - edit = False - for group in groups: - module = group.split(".")[0] - grp = group.split(".")[1] - cr.execute("select count(*) from res_groups_users_rel where gid in (select res_id from ir_model_data where name='%s' and module='%s' and model='%s') and uid=%s" % \ - (grp, module, 'res.groups', user)) - readonly = cr.fetchall() - if readonly[0][0] >= 1: - edit = True - break - elif readonly[0][0] == 0: - edit = False - else: - edit = False - - if not edit: - if type(vals[field]) == type([]): - vals[field] = [] - elif type(vals[field]) == type(0.0): - vals[field] = 0 - elif type(vals[field]) == type(''): - vals[field] = '=No Permission=' - else: - vals[field] = False - return res - - def perm_read(self, cr, user, ids, context=None, details=True): - if not context: - context={} - if not ids: - return [] - fields = '' - if self._log_access: - fields =', u.create_uid, u.create_date, u.write_uid, u.write_date' - if isinstance(ids, (int, long)): - ids_str = str(ids) - else: - ids_str = string.join(map(lambda x:str(x), ids),',') - cr.execute('select u.id'+fields+' from "'+self._table+'" u where u.id in ('+ids_str+')') - res = cr.dictfetchall() - for r in res: - for key in r: - r[key] = r[key] or False - if key in ('write_uid','create_uid','uid') and details: - if r[key]: - r[key] = self.pool.get('res.users').name_get(cr, user, [r[key]])[0] - if isinstance(ids, (int, long)): - return res[ids] - return res - - def unlink(self, cr, uid, ids, context=None): - if not context: - context={} - if not ids: - return True - if isinstance(ids, (int, long)): - ids = [ids] - - fn_list = [] - if self._name in self.pool._store_function.keys(): - list_store = self.pool._store_function[self._name] - fn_data = () - id_change = [] - for tuple_fn in list_store: - for id in ids: - id_change.append(self._store_get_ids(cr, uid, id,tuple_fn, context)[0]) - fn_data = id_change,tuple_fn - fn_list.append(fn_data) - - delta= context.get('read_delta',False) - if delta and self._log_access: - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] - cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ - "from \"%s\" where id in (%s)" % - (delta, self._table, ",".join(map(str, sub_ids))) ) - res= cr.fetchone() - if res and res[0]: - raise except_orm(_('ConcurrencyException'), - _('This record was modified in the meanwhile')) - - self.pool.get('ir.model.access').check(cr, uid, self._name, 'unlink') - - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_delete(uid, self._name, id, cr) - - #cr.execute('select * from '+self._table+' where id in ('+str_d+')', ids) - #res = cr.dictfetchall() - #for key in self._inherits: - # ids2 = [x[self._inherits[key]] for x in res] - # self.pool.get(key).unlink(cr, uid, ids2) - - d1, d2 = self.pool.get('ir.rule').domain_get(cr, uid, self._name) - if d1: - d1 = ' AND '+d1 - - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] - str_d = string.join(('%d',)*len(sub_ids),',') - if d1: - cr.execute('SELECT id FROM "'+self._table+'" ' \ - 'WHERE id IN ('+str_d+')'+d1, sub_ids+d2) - if not cr.rowcount == len({}.fromkeys(ids)): - raise except_orm(_('AccessError'), - _('You try to bypass an access rule (Document type: %s).') % \ - self._description) - - if d1: - cr.execute('delete from "'+self._table+'" ' \ - 'where id in ('+str_d+')'+d1, sub_ids+d2) - else: - cr.execute('delete from "'+self._table+'" ' \ - 'where id in ('+str_d+')', sub_ids) - if fn_list: - for ids,tuple_fn in fn_list: - self._store_set_values(cr, uid, ids,tuple_fn,id_change, context) - - return True - - # - # TODO: Validate - # - def write(self, cr, user, ids, vals, context=None): - readonly = None - for field in vals.copy(): - fobj = None - if field in self._columns: - fobj = self._columns[field] - else: - fobj = self._inherit_fields[field][2] - if not fobj: - continue - groups = fobj.write - - if groups: - edit = False - for group in groups: - module = group.split(".")[0] - grp = group.split(".")[1] - cr.execute("select count(*) from res_groups_users_rel where gid in (select res_id from ir_model_data where name='%s' and module='%s' and model='%s') and uid=%s" % \ - (grp, module, 'res.groups', user)) - readonly = cr.fetchall() - if readonly[0][0] >= 1: - edit = True - break - elif readonly[0][0] == 0: - edit = False - else: - edit = False - - if not edit: - vals.pop(field) - - if not context: - context={} - if not ids: - return True - if isinstance(ids, (int, long)): - ids = [ids] - delta= context.get('read_delta',False) - if delta and self._log_access: - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] - cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ - "from %s where id in (%s)" % - (delta,self._table, ",".join(map(str, sub_ids)))) - res= cr.fetchone() - if res and res[0]: - for field in vals: - if field in self._columns and self._columns[field]._classic_write: - raise except_orm(_('ConcurrencyException'), - _('This record was modified in the meanwhile')) - - self.pool.get('ir.model.access').check(cr, user, self._name, 'write') - - #for v in self._inherits.values(): - # assert v not in vals, (v, vals) - upd0=[] - upd1=[] - upd_todo=[] - updend=[] - direct = [] - totranslate = context.get('lang', False) and (context['lang'] != 'en_US') - for field in vals: - if field in self._columns: - if self._columns[field]._classic_write: - if (not totranslate) or not self._columns[field].translate: - upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0]) - upd1.append(self._columns[field]._symbol_set[1](vals[field])) - direct.append(field) - else: - upd_todo.append(field) - else: - updend.append(field) - 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)) - - if self._log_access: - upd0.append('write_uid=%d') - upd0.append('write_date=now()') - upd1.append(user) - - if len(upd0): - - d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) - if d1: - d1 = ' and '+d1 - - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] - ids_str = string.join(map(str, sub_ids),',') - if d1: - cr.execute('SELECT id FROM "'+self._table+'" ' \ - 'WHERE id IN ('+ids_str+')'+d1, d2) - if not cr.rowcount == len({}.fromkeys(sub_ids)): - raise except_orm(_('AccessError'), - _('You try to bypass an access rule (Document type: %s).') % \ - self._description) - else: - cr.execute('SELECT id FROM "'+self._table+'" WHERE id IN ('+ids_str+')') - if not cr.rowcount == len({}.fromkeys(sub_ids)): - raise except_orm(_('AccessError'), - _('You try to write on an record that doesn\'t exist ' \ - '(Document type: %s).') % self._description) - if d1: - cr.execute('update "'+self._table+'" set '+string.join(upd0,',')+' ' \ - 'where id in ('+ids_str+')'+d1, upd1+ d2) - else: - cr.execute('update "'+self._table+'" set '+string.join(upd0,',')+' ' \ - 'where id in ('+ids_str+')', upd1) - - if totranslate: - for f in direct: - if self._columns[f].translate: - self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f,'model', context['lang'], ids,vals[f]) - - # call the 'set' method of fields which are not classic_write - upd_todo.sort(lambda x,y: self._columns[x].priority-self._columns[y].priority) - for field in upd_todo: - for id in ids: - self._columns[field].set(cr, self, id, field, vals[field], user, context=context) - - for table in self._inherits: - col = self._inherits[table] - nids = [] - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] - ids_str = string.join(map(str, sub_ids),',') - cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \ - 'where id in ('+ids_str+')', upd1) - nids.extend([x[0] for x in cr.fetchall()]) - - v = {} - for val in updend: - if self._inherit_fields[val][0]==table: - v[val]=vals[val] - self.pool.get(table).write(cr, user, nids, v, context) - - self._validate(cr, user, ids, context) - - if context.has_key('read_delta'): - del context['read_delta'] - - wf_service = netsvc.LocalService("workflow") - for id in ids: - wf_service.trg_write(user, self._name, id, cr) - self._update_function_stored(cr, user, ids, context=context) - - if self._name in self.pool._store_function.keys(): - list_store = self.pool._store_function[self._name] - for tuple_fn in list_store: - flag = False - if not tuple_fn[3]: - flag = True - for field in tuple_fn[3]: - if field in vals.keys(): - flag = True - break - if flag: - id_change = self._store_get_ids(cr, user, ids[0],tuple_fn, context) - self._store_set_values(cr, user, ids[0],tuple_fn,id_change, context) - - return True - - # - # TODO: Should set perm to user.xxx - # - def create(self, cr, user, vals, context=None): - """ create(cr, user, vals, context) -> int - cr = database cursor - user = user id - vals = dictionary of the form {'field_name':field_value, ...} - """ - if not context: - context={} - self.pool.get('ir.model.access').check(cr, user, self._name, 'create') - - default = [] - - avoid_table = [] - for (t,c) in self._inherits.items(): - if c in vals: - avoid_table.append(t) - for f in self._columns.keys(): # + self._inherit_fields.keys(): - if not f in vals: - default.append(f) - for f in self._inherit_fields.keys(): - if (not f in vals) and (not self._inherit_fields[f][0] in avoid_table): - default.append(f) - - if len(default): - vals.update(self.default_get(cr, user, default, context)) - - tocreate = {} - for v in self._inherits: - if self._inherits[v] not in vals: - tocreate[v] = {} - - (upd0, upd1, upd2) = ('', '', []) - upd_todo = [] - - for v in vals.keys(): - if v in self._inherit_fields: - (table,col,col_detail) = self._inherit_fields[v] - tocreate[table][v] = vals[v] - del vals[v] - - cr.execute("SELECT nextval('"+self._sequence+"')") - id_new = cr.fetchone()[0] - for table in tocreate: - id = self.pool.get(table).create(cr, user, tocreate[table]) - upd0 += ','+self._inherits[table] - upd1 += ',%d' - upd2.append(id) - - for field in vals: - if self._columns[field]._classic_write: - upd0=upd0+',"'+field+'"' - upd1=upd1+','+self._columns[field]._symbol_set[0] - upd2.append(self._columns[field]._symbol_set[1](vals[field])) - else: - upd_todo.append(field) - 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)) - if self._log_access: - upd0 += ',create_uid,create_date' - upd1 += ',%d,now()' - upd2.append(user) - cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2)) - upd_todo.sort(lambda x,y: self._columns[x].priority-self._columns[y].priority) - for field in upd_todo: - self._columns[field].set(cr, self, id_new, field, vals[field], user, context) - - self._validate(cr, user, [id_new], context) - - wf_service = netsvc.LocalService("workflow") - wf_service.trg_create(user, self._name, id_new, cr) - self._update_function_stored(cr, user, [id_new], context=context) - if self._name in self.pool._store_function.keys(): - list_store = self.pool._store_function[self._name] - for tuple_fn in list_store: - id_change = self._store_get_ids(cr, user, id_new,tuple_fn, context) - self._store_set_values(cr, user, id_new,tuple_fn,id_change, context) - - return id_new - - def _store_get_ids(self,cr, uid, ids,tuple_fn, context): - parent_id=getattr(self.pool.get(tuple_fn[0]),tuple_fn[4].func_name)(cr,uid,[ids]) - return parent_id - - def _store_set_values(self,cr,uid,ids,tuple_fn,parent_id,context): - name = tuple_fn[1] - table = tuple_fn[0] - args={} - vals_tot=getattr(self.pool.get(table),tuple_fn[2])(cr, uid, parent_id ,name, args, context) - write_dict = {} - for id in vals_tot.keys(): - write_dict[name]=vals_tot[id] - self.pool.get(table).write(cr,uid,[id],write_dict) - return True - - def _update_function_stored(self, cr, user, ids, context=None): - if not context: - context = {} - f = filter(lambda a: isinstance(self._columns[a], fields.function) \ - and self._columns[a].store, self._columns) - if f: - result=self.read(cr, user, ids, fields=f, context=context) - for res in result: - upd0=[] - upd1=[] - for field in res: - if field not in f: - continue - value = res[field] - if self._columns[field]._type in ('many2one', 'one2one'): - value = res[field][0] - upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0]) - upd1.append(self._columns[field]._symbol_set[1](value)) - upd1.append(res['id']) - cr.execute('update "' + self._table + '" set ' + \ - string.join(upd0, ',') + ' where id = %d', upd1) - return True - - # - # TODO: Validate - # - def perm_write(self, cr, user, ids, fields, context=None): - raise _('This method does not exist anymore') - - # TODO: ameliorer avec NULL - def _where_calc(self, cr, user, args, active_test=True, context=None): - if not context: - context={} - args = args[:] - # if the object has a field named 'active', filter out all inactive - # records unless they were explicitely asked for - if 'active' in self._columns and (active_test and context.get('active_test', True)): - i = 0 - active_found = False - while i<len(args): - if args[i][0]=='active': - active_found = True - i += 1 - if not active_found: - args.append(('active', '=', 1)) - - i = 0 - tables=['"'+self._table+'"'] - joins=[] - while i<len(args): - table=self - assert args[i][1] in ('like','!=','ilike','=like', 'not like', 'not ilike', 'not in','inselect','child_of','in','=','<>','<','>','>=','<='), _('Error ! Bad clause operand "%s".') % (args[i][1],) - if args[i][1] == 'inselect': - raise except_orm(_('ValidateError'), - _("The clause 'inselect' can not be used outside the orm!")) - if args[i][0] in self._inherit_fields: - table=self.pool.get(self._inherit_fields[args[i][0]][0]) - if ('"'+table._table+'"' not in tables): - tables.append('"'+table._table+'"') - joins.append(('id', 'join', '%s.%s' % (self._table, self._inherits[table._name]), table)) - fargs = args[i][0].split('.', 1) - field = table._columns.get(fargs[0],False) - if not field: - if args[i][0] == 'id' and args[i][1] == 'child_of': - ids2 = args[i][2] - def _rec_get(ids, table, parent): - if not ids: - return [] - ids2 = table.search(cr, user, [(parent, 'in', ids)], context=context) - return ids + _rec_get(ids2, table, parent) - args[i] = (args[i][0], 'in', ids2 + _rec_get(ids2, table, table._parent_name), table) - i+=1 - continue - if len(fargs) > 1: - if field._type == 'many2one': - args[i] = (fargs[0], 'in', self.pool.get(field._obj).search(cr, user, [(fargs[1], args[i][1], args[i][2])], context=context)) - i+=1 - continue - else: - i+=1 - continue - if field._properties: - arg = [args.pop(i)] - j = i - while j<len(args): - if args[j][0]==arg[0][0]: - arg.append(args.pop(j)) - else: - j+=1 - if field._fnct_search: - args.extend(field.search(cr, user, self, arg[0][0], arg)) - if field.store: - args.extend(arg) - i+=1 - elif field._type=='one2many': - field_obj = self.pool.get(field._obj) - - if isinstance(args[i][2], basestring): - # get the ids of the records of the "distant" resource - ids2 = [x[0] for x in field_obj.name_search(cr, user, args[i][2], [], args[i][1])] - else: - ids2 = args[i][2] - if not ids2: - args[i] = ('id','=','0') - else: - ids3 = [] - for j in range((len(ids2) / ID_MAX) + (len(ids2) % ID_MAX)): - sub_ids2 = ids2[ID_MAX * j:ID_MAX * (j + 1)] - if sub_ids2: - cr.execute('SELECT "'+field._fields_id+'" ' \ - 'FROM "'+field_obj._table+'" ' \ - 'WHERE id in ('+','.join(map(str,sub_ids2))+')') - ids3.extend([x[0] for x in cr.fetchall()]) - - args[i] = ('id', 'in', ids3) - i+=1 - - elif field._type=='many2many': - #FIXME - if args[i][1]=='child_of': - if isinstance(args[i][2], basestring): - ids2 = [x[0] for x in self.pool.get(field._obj).name_search(cr, user, args[i][2], [], 'like')] - else: - ids2 = args[i][2] - def _rec_get(ids, table, parent): - if not ids: - return [] - ids2 = table.search(cr, user, [(parent, 'in', ids)], context=context) - return ids + _rec_get(ids2, table, parent) - def _rec_convert(ids): - if self.pool.get(field._obj)==self: - return ids - if not len(ids): return [] - cr.execute('select "'+field._id1+'" from "'+field._rel+'" where "'+field._id2+'" in ('+','.join(map(str,ids))+')') - ids = [x[0] for x in cr.fetchall()] - return ids - args[i] = ('id','in',_rec_convert(ids2+_rec_get(ids2, self.pool.get(field._obj), table._parent_name))) - else: - if isinstance(args[i][2], basestring): - res_ids = [x[0] for x in self.pool.get(field._obj).name_search(cr, user, args[i][2], [], args[i][1])] - else: - res_ids = args[i][2] - if not len(res_ids): - args[i] = ('id', 'in', [0]) - else: - cr.execute('select "'+field._id1+'" from "'+field._rel+'" where "'+field._id2+'" in ('+','.join(map(str, res_ids))+')') - args[i] = ('id', 'in', map(lambda x: x[0], cr.fetchall())) - i+=1 - - elif field._type=='many2one': - if args[i][1]=='child_of': - if isinstance(args[i][2], basestring): - ids2 = [x[0] for x in self.pool.get(field._obj).name_search(cr, user, args[i][2], [], 'like')] - else: - ids2 = args[i][2] - def _rec_get(ids, table, parent): - if not ids: - return [] - ids2 = table.search(cr, user, [(parent, 'in', ids)], context=context) - return ids + _rec_get(ids2, table, parent) - if field._obj <> table._name: - args[i] = (args[i][0],'in',ids2+_rec_get(ids2, self.pool.get(field._obj), table._parent_name), table) - else: - args[i] = ('id','in',ids2+_rec_get(ids2, table, args[i][0]), table) - else: - if isinstance(args[i][2], basestring): - res_ids = self.pool.get(field._obj).name_search(cr, user, args[i][2], [], args[i][1]) - args[i] = (args[i][0],'in',map(lambda x: x[0], res_ids), table) - else: - args[i] += (table,) - i+=1 - else: - if field.translate: - if args[i][1] in ('like', 'ilike', 'not like', 'not ilike'): - args[i] = (args[i][0], args[i][1], '%%%s%%' % args[i][2]) - query1 = '(SELECT res_id FROM ir_translation ' \ - 'WHERE name = %s AND lang = %s ' \ - 'AND type = %s ' \ - 'AND VALUE ' + args[i][1] + ' %s)' - query2 = [table._name + ',' + args[i][0], - context.get('lang', False) or 'en_US', - 'model', - args[i][2]] - query1 += ' UNION ' - query1 += '(SELECT id FROM "' + table._table + '" ' \ - 'WHERE "' + args[i][0] + '" ' + args[i][1] + ' %s)' - query2 += [args[i][2]] - args[i] = ('id', 'inselect', (query1, query2), table) - else: - args[i] += (table,) - i+=1 - args.extend(joins) - - qu1, qu2 = [], [] - for x in args: - table=self - if len(x) > 3: - table=x[3] - if x[1] == 'inselect': - qu1.append('(%s.%s in (%s))' % (table._table, x[0], x[2][0])) - qu2 += x[2][1] - elif x[1]=='not in': - if len(x[2])>0: - todel = [] - for xitem in range(len(x[2])): - if x[2][xitem]==False and isinstance(x[2][xitem],bool): - todel.append(xitem) - for xitem in todel[::-1]: - del x[2][xitem] - if x[0]=='id': - qu1.append('(%s.id not in (%s))' % (table._table, ','.join(['%d'] * len(x[2])),)) - else: - qu1.append('(%s.%s not in (%s))' % (table._table, x[0], ','.join([table._columns[x[0]]._symbol_set[0]]*len(x[2])))) - if todel: - qu1[-1] = '('+qu1[-1]+' or '+x[0]+' is null)' - qu2+=x[2] - else: - qu1.append(' (1=0)') - elif x[1] != 'in': + cr.execute("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO temp_change_size" % (self._table,k)) + cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" VARCHAR(%d)" % (self._table,k,f.size)) + cr.execute("UPDATE \"%s\" SET \"%s\"=temp_change_size::VARCHAR(%d)" % (self._table,k,f.size)) + cr.execute("ALTER TABLE \"%s\" DROP COLUMN temp_change_size" % (self._table,)) + cr.commit() + # if the field is required and hasn't got a NOT NULL constraint + if f.required and f_pg_notnull == 0: + # set the field to the default value if any + if self._defaults.has_key(k): + default = self._defaults[k](self, cr, 1, {}) + if not (default is False): + cr.execute("UPDATE \"%s\" SET \"%s\"='%s' WHERE %s is NULL" % (self._table, k, default, k)) + cr.commit() + # add the NOT NULL constraint + try: + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" SET NOT NULL" % (self._table, k)) + cr.commit() + except: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to set a NOT NULL constraint on column %s of the %s table !\nIf you want to have it, you should update the records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) + cr.commit() + elif not f.required and f_pg_notnull == 1: + cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table,k)) + cr.commit() + cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = '%s_%s_index' and tablename = '%s'" % (self._table, k, self._table)) + res = cr.dictfetchall() + if not res and f.select: + cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) + cr.commit() + if res and not f.select: + cr.execute("DROP INDEX \"%s_%s_index\"" % (self._table, k)) + cr.commit() + if isinstance(f, fields.many2one): + ref = self.pool.get(f._obj)._table + if ref != 'ir_actions': + cr.execute('SELECT confdeltype, conname FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, ' \ + 'pg_attribute as att1, pg_attribute as att2 ' \ + 'WHERE con.conrelid = cl1.oid ' \ + 'AND cl1.relname = %s ' \ + 'AND con.confrelid = cl2.oid ' \ + 'AND cl2.relname = %s ' \ + 'AND array_lower(con.conkey, 1) = 1 ' \ + 'AND con.conkey[1] = att1.attnum ' \ + 'AND att1.attrelid = cl1.oid ' \ + 'AND att1.attname = %s ' \ + 'AND array_lower(con.confkey, 1) = 1 ' \ + 'AND con.confkey[1] = att2.attnum ' \ + 'AND att2.attrelid = cl2.oid ' \ + 'AND att2.attname = %s ' \ + 'AND con.contype = \'f\'', (self._table, ref, k, 'id')) + res = cr.dictfetchall() + if res: + confdeltype = { + 'RESTRICT': 'r', + 'NO ACTION': 'a', + 'CASCADE': 'c', + 'SET NULL': 'n', + 'SET DEFAULT': 'd', + } + if res[0]['confdeltype'] != confdeltype.get(f.ondelete.upper(), 'a'): + cr.execute('ALTER TABLE "' + self._table + '" DROP CONSTRAINT "' + res[0]['conname'] + '"') + cr.execute('ALTER TABLE "' + self._table + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete) + cr.commit() + else: + print "ERROR" + else: + cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) + create = not bool(cr.fetchone()) + + for (key,con,_) in self._sql_constraints: + cr.execute("SELECT conname FROM pg_constraint where conname='%s_%s'" % (self._table, key)) + if not cr.dictfetchall(): + try: + cr.execute('alter table \"%s\" add constraint \"%s_%s\" %s' % (self._table,self._table,key, con,)) + cr.commit() + except: + logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table,self._table,key, con,)) + + if create: + if hasattr(self,"_sql"): + for line in self._sql.split(';'): + line2 = line.replace('\n','').strip() + if line2: + cr.execute(line2) + cr.commit() + + def __init__(self, cr): + super(orm, self).__init__(cr) + f=filter(lambda a: isinstance(self._columns[a], fields.function) and self._columns[a].store, self._columns) + if f: + list_store = [] + tuple_store = () + tuple_fn = () + for store_field in f: + if not self._columns[store_field].store == True: + dict_store = self._columns[store_field].store + key = dict_store.keys() + list_data=[] + for i in key: + tuple_store=self._name,store_field,self._columns[store_field]._fnct.__name__,tuple(dict_store[i][0]),dict_store[i][1],i + list_data.append(tuple_store) + #tuple_store=self._name,store_field,self._columns[store_field]._fnct.__name__,tuple(dict_store[key[0]][0]),dict_store[key[0]][1] + for l in list_data: + list_store=[] + if l[5] in self.pool._store_function.keys(): + self.pool._store_function[l[5]].append(l) + temp_list = list(set(self.pool._store_function[l[5]])) + self.pool._store_function[l[5]]=temp_list + else: + list_store.append(l) + self.pool._store_function[l[5]]=list_store + + for (key,_,msg) in self._sql_constraints: + self.pool._sql_error[self._table+'_'+key] = msg + + # Load manual fields + + cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", ('state','ir.model.fields')) + if cr.fetchone(): + cr.execute('SELECT * FROM ir_model_fields WHERE model=%s AND state=%s', (self._name,'manual')) + for field in cr.dictfetchall(): + if field['name'] in self._columns: + continue + attrs = { + 'string': field['field_description'], + 'required': bool(field['required']), + 'readonly': bool(field['readonly']), + 'domain': field['domain'] or None, + 'size': field['size'], + 'ondelete': field['on_delete'], + 'translate': (field['translate']), + #'select': int(field['select_level']) + } + #if field['relation']: + # attrs['relation'] = field['relation'] + if field['ttype'] == 'selection': + self._columns[field['name']] = getattr(fields, field['ttype'])(eval(field['selection']), **attrs) + elif field['ttype'] == 'many2one': + self._columns[field['name']] = getattr(fields, field['ttype'])(field['relation'], **attrs) + else: + self._columns[field['name']] = getattr(fields, field['ttype'])(**attrs) + + self._inherits_reload() + if not self._sequence: + self._sequence = self._table+'_id_seq' + for k in self._defaults: + assert (k in self._columns) or (k in self._inherit_fields), 'Default function defined in %s but field %s does not exist !' % (self._name, k,) + for f in self._columns: + self._columns[f].restart() + + def default_get(self, cr, uid, fields_list, context=None): + if not context: + context={} + value = {} + # get the default values for the inherited fields + for t in self._inherits.keys(): + value.update(self.pool.get(t).default_get(cr, uid, fields_list, + context)) + + # get the default values defined in the object + for f in fields_list: + if f in self._defaults: + value[f] = self._defaults[f](self, cr, uid, context) + fld_def = ((f in self._columns) and self._columns[f]) \ + or ((f in self._inherit_fields ) and self._inherit_fields[f][2]) \ + or False + if isinstance(fld_def, fields.property): + property_obj = self.pool.get('ir.property') + definition_id = fld_def._field_get(cr, uid, self._name, f) + nid = property_obj.search(cr, uid, [('fields_id', '=', + definition_id), ('res_id', '=', False)]) + if nid: + prop_value = property_obj.browse(cr, uid, nid[0], + context=context).value + value[f] = (prop_value and int(prop_value.split(',')[1])) \ + or False + + # get the default values set by the user and override the default + # values defined in the object + ir_values_obj = self.pool.get('ir.values') + res = ir_values_obj.get(cr, uid, 'default', False, [self._name]) + for id, field, field_value in res: + if field in fields_list: + fld_def = (field in self._columns) and self._columns[field] or self._inherit_fields[field][2] + if fld_def._type in ('many2one', 'one2one'): + obj = self.pool.get(fld_def._obj) + if not obj.search(cr, uid, [('id', '=', field_value)]): + continue + if fld_def._type in ('many2many'): + obj = self.pool.get(fld_def._obj) + field_value2 = [] + for i in range(len(field_value)): + if not obj.search(cr, uid, [('id', '=', + field_value[i])]): + continue + field_value2.append(field_value[i]) + field_value = field_value2 + if fld_def._type in ('one2many'): + obj = self.pool.get(fld_def._obj) + field_value2 = [] + for i in range(len(field_value)): + field_value2.append({}) + for field2 in field_value[i]: + if obj._columns[field2]._type in ('many2one', 'one2one'): + obj2 = self.pool.get(obj._columns[field2]._obj) + if not obj2.search(cr, uid, + [('id', '=', field_value[i][field2])]): + continue + # TODO add test for many2many and one2many + field_value2[i][field2] = field_value[i][field2] + field_value = field_value2 + value[field] = field_value + return value + + + # + # Update objects that uses this one to update their _inherits fields + # + def _inherits_reload_src(self): + for obj in self.pool.obj_pool.values(): + if self._name in obj._inherits: + obj._inherits_reload() + + def _inherits_reload(self): + res = {} + for table in self._inherits: + res.update(self.pool.get(table)._inherit_fields) + for col in self.pool.get(table)._columns.keys(): + res[col]=(table, self._inherits[table], self.pool.get(table)._columns[col]) + for col in self.pool.get(table)._inherit_fields.keys(): + res[col]=(table, self._inherits[table], self.pool.get(table)._inherit_fields[col][2]) + self._inherit_fields=res + self._inherits_reload_src() + + def fields_get(self, cr, user, fields=None, context=None): + read_access= self.pool.get('ir.model.access').check(cr, user, self._name, 'write', raise_exception=False) + return super(orm, self).fields_get(cr, user, fields, context, read_access) + + def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): + if not context: + context={} + self.pool.get('ir.model.access').check(cr, user, self._name, 'read') + if not fields: + fields = self._columns.keys() + self._inherit_fields.keys() + select = ids + if isinstance(ids, (int, long)): + select = [ids] + result = self._read_flat(cr, user, select, fields, context, load) + for r in result: + for key,v in r.items(): + if v == None: + r[key]=False + if isinstance(ids, (int, long)): + return result[0] + return result + + def _read_flat(self, cr, user, ids, fields, context=None, load='_classic_read'): + if not context: + context={} + if not ids: + return [] + + if fields==None: + fields = self._columns.keys() + + # construct a clause for the rules : + d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) + + # all inherited fields + all non inherited fields for which the attribute whose name is in load is True + fields_pre = filter(lambda x: x in self._columns and getattr(self._columns[x],'_classic_write'), fields) + self._inherits.values() + + res = [] + if len(fields_pre) : + fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre) + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] + if d1: + cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) AND %s ORDER BY %s' % \ + (','.join(fields_pre2 + ['id']), self._table, + ','.join([str(x) for x in sub_ids]), d1, + self._order),d2) + if not cr.rowcount == len({}.fromkeys(sub_ids)): + raise except_orm(_('AccessError'), + _('You try to bypass an access rule (Document type: %s).') % self._description) + else: + cr.execute('SELECT %s FROM \"%s\" WHERE id IN (%s) ORDER BY %s' % \ + (','.join(fields_pre2 + ['id']), self._table, + ','.join([str(x) for x in sub_ids]), + self._order)) + res.extend(cr.dictfetchall()) + else: + res = map(lambda x: {'id': x}, ids) + + for f in fields_pre: + if self._columns[f].translate: + ids = map(lambda x: x['id'], res) + res_trans = self.pool.get('ir.translation')._get_ids(cr, user, self._name+','+f, 'model', context.get('lang', False) or 'en_US', ids) + for r in res: + r[f] = res_trans.get(r['id'], False) or r[f] + + for table in self._inherits: + col = self._inherits[table] + cols = intersect(self._inherit_fields.keys(), fields) + if not cols: + continue + res2 = self.pool.get(table).read(cr, user, [x[col] for x in res], cols, context, load) + + res3 = {} + for r in res2: + res3[r['id']] = r + del r['id'] + + for record in res: + record.update(res3[record[col]]) + if col not in fields: + del record[col] + + # all fields which need to be post-processed by a simple function (symbol_get) + fields_post = filter(lambda x: x in self._columns and self._columns[x]._symbol_get, fields) + if fields_post: + # maybe it would be faster to iterate on the fields then on res, so that we wouldn't need + # to get the _symbol_get in each occurence + for r in res: + for f in fields_post: + r[f] = self.columns[f]._symbol_get(r[f]) + ids = map(lambda x: x['id'], res) + + # all non inherited fields for which the attribute whose name is in load is False + fields_post = filter(lambda x: x in self._columns and not getattr(self._columns[x], load), fields) + for f in fields_post: + # get the value of that field for all records/ids + res2 = self._columns[f].get(cr, self, ids, f, user, context=context, values=res) + for record in res: + record[f] = res2[record['id']] + + readonly = None + for vals in res: + for field in vals.copy(): + fobj = None + if field in self._columns: + fobj = self._columns[field] + + if not fobj: + continue + groups = fobj.read + if groups: + edit = False + for group in groups: + module = group.split(".")[0] + grp = group.split(".")[1] + cr.execute("select count(*) from res_groups_users_rel where gid in (select res_id from ir_model_data where name='%s' and module='%s' and model='%s') and uid=%s" % \ + (grp, module, 'res.groups', user)) + readonly = cr.fetchall() + if readonly[0][0] >= 1: + edit = True + break + elif readonly[0][0] == 0: + edit = False + else: + edit = False + + if not edit: + if type(vals[field]) == type([]): + vals[field] = [] + elif type(vals[field]) == type(0.0): + vals[field] = 0 + elif type(vals[field]) == type(''): + vals[field] = '=No Permission=' + else: + vals[field] = False + return res + + def perm_read(self, cr, user, ids, context=None, details=True): + if not context: + context={} + if not ids: + return [] + fields = '' + if self._log_access: + fields =', u.create_uid, u.create_date, u.write_uid, u.write_date' + if isinstance(ids, (int, long)): + ids_str = str(ids) + else: + ids_str = string.join(map(lambda x:str(x), ids),',') + cr.execute('select u.id'+fields+' from "'+self._table+'" u where u.id in ('+ids_str+')') + res = cr.dictfetchall() + for r in res: + for key in r: + r[key] = r[key] or False + if key in ('write_uid','create_uid','uid') and details: + if r[key]: + r[key] = self.pool.get('res.users').name_get(cr, user, [r[key]])[0] + if isinstance(ids, (int, long)): + return res[ids] + return res + + def unlink(self, cr, uid, ids, context=None): + if not context: + context={} + if not ids: + return True + if isinstance(ids, (int, long)): + ids = [ids] + + fn_list = [] + if self._name in self.pool._store_function.keys(): + list_store = self.pool._store_function[self._name] + fn_data = () + id_change = [] + for tuple_fn in list_store: + for id in ids: + id_change.append(self._store_get_ids(cr, uid, id,tuple_fn, context)[0]) + fn_data = id_change,tuple_fn + fn_list.append(fn_data) + + delta= context.get('read_delta',False) + if delta and self._log_access: + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] + cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ + "from \"%s\" where id in (%s)" % + (delta, self._table, ",".join(map(str, sub_ids))) ) + res= cr.fetchone() + if res and res[0]: + raise except_orm(_('ConcurrencyException'), + _('This record was modified in the meanwhile')) + + self.pool.get('ir.model.access').check(cr, uid, self._name, 'unlink') + + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_delete(uid, self._name, id, cr) + + #cr.execute('select * from '+self._table+' where id in ('+str_d+')', ids) + #res = cr.dictfetchall() + #for key in self._inherits: + # ids2 = [x[self._inherits[key]] for x in res] + # self.pool.get(key).unlink(cr, uid, ids2) + + d1, d2 = self.pool.get('ir.rule').domain_get(cr, uid, self._name) + if d1: + d1 = ' AND '+d1 + + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] + str_d = string.join(('%d',)*len(sub_ids),',') + if d1: + cr.execute('SELECT id FROM "'+self._table+'" ' \ + 'WHERE id IN ('+str_d+')'+d1, sub_ids+d2) + if not cr.rowcount == len({}.fromkeys(ids)): + raise except_orm(_('AccessError'), + _('You try to bypass an access rule (Document type: %s).') % \ + self._description) + + if d1: + cr.execute('delete from "'+self._table+'" ' \ + 'where id in ('+str_d+')'+d1, sub_ids+d2) + else: + cr.execute('delete from "'+self._table+'" ' \ + 'where id in ('+str_d+')', sub_ids) + if fn_list: + for ids,tuple_fn in fn_list: + self._store_set_values(cr, uid, ids,tuple_fn,id_change, context) + + return True + + # + # TODO: Validate + # + def write(self, cr, user, ids, vals, context=None): + readonly = None + for field in vals.copy(): + fobj = None + if field in self._columns: + fobj = self._columns[field] + else: + fobj = self._inherit_fields[field][2] + if not fobj: + continue + groups = fobj.write + + if groups: + edit = False + for group in groups: + module = group.split(".")[0] + grp = group.split(".")[1] + cr.execute("select count(*) from res_groups_users_rel where gid in (select res_id from ir_model_data where name='%s' and module='%s' and model='%s') and uid=%s" % \ + (grp, module, 'res.groups', user)) + readonly = cr.fetchall() + if readonly[0][0] >= 1: + edit = True + break + elif readonly[0][0] == 0: + edit = False + else: + edit = False + + if not edit: + vals.pop(field) + + if not context: + context={} + if not ids: + return True + if isinstance(ids, (int, long)): + ids = [ids] + delta= context.get('read_delta',False) + if delta and self._log_access: + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] + cr.execute("select (now() - min(write_date)) <= '%s'::interval " \ + "from %s where id in (%s)" % + (delta,self._table, ",".join(map(str, sub_ids)))) + res= cr.fetchone() + if res and res[0]: + for field in vals: + if field in self._columns and self._columns[field]._classic_write: + raise except_orm(_('ConcurrencyException'), + _('This record was modified in the meanwhile')) + + self.pool.get('ir.model.access').check(cr, user, self._name, 'write') + + #for v in self._inherits.values(): + # assert v not in vals, (v, vals) + upd0=[] + upd1=[] + upd_todo=[] + updend=[] + direct = [] + totranslate = context.get('lang', False) and (context['lang'] != 'en_US') + for field in vals: + if field in self._columns: + if self._columns[field]._classic_write: + if (not totranslate) or not self._columns[field].translate: + upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0]) + upd1.append(self._columns[field]._symbol_set[1](vals[field])) + direct.append(field) + else: + upd_todo.append(field) + else: + updend.append(field) + 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)) + + if self._log_access: + upd0.append('write_uid=%d') + upd0.append('write_date=now()') + upd1.append(user) + + if len(upd0): + + d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) + if d1: + d1 = ' and '+d1 + + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] + ids_str = string.join(map(str, sub_ids),',') + if d1: + cr.execute('SELECT id FROM "'+self._table+'" ' \ + 'WHERE id IN ('+ids_str+')'+d1, d2) + if not cr.rowcount == len({}.fromkeys(sub_ids)): + raise except_orm(_('AccessError'), + _('You try to bypass an access rule (Document type: %s).') % \ + self._description) + else: + cr.execute('SELECT id FROM "'+self._table+'" WHERE id IN ('+ids_str+')') + if not cr.rowcount == len({}.fromkeys(sub_ids)): + raise except_orm(_('AccessError'), + _('You try to write on an record that doesn\'t exist ' \ + '(Document type: %s).') % self._description) + if d1: + cr.execute('update "'+self._table+'" set '+string.join(upd0,',')+' ' \ + 'where id in ('+ids_str+')'+d1, upd1+ d2) + else: + cr.execute('update "'+self._table+'" set '+string.join(upd0,',')+' ' \ + 'where id in ('+ids_str+')', upd1) + + if totranslate: + for f in direct: + if self._columns[f].translate: + self.pool.get('ir.translation')._set_ids(cr, user, self._name+','+f,'model', context['lang'], ids,vals[f]) + + # call the 'set' method of fields which are not classic_write + upd_todo.sort(lambda x,y: self._columns[x].priority-self._columns[y].priority) + for field in upd_todo: + for id in ids: + self._columns[field].set(cr, self, id, field, vals[field], user, context=context) + + for table in self._inherits: + col = self._inherits[table] + nids = [] + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)] + ids_str = string.join(map(str, sub_ids),',') + cr.execute('select distinct "'+col+'" from "'+self._table+'" ' \ + 'where id in ('+ids_str+')', upd1) + nids.extend([x[0] for x in cr.fetchall()]) + + v = {} + for val in updend: + if self._inherit_fields[val][0]==table: + v[val]=vals[val] + self.pool.get(table).write(cr, user, nids, v, context) + + self._validate(cr, user, ids, context) + + if context.has_key('read_delta'): + del context['read_delta'] + + wf_service = netsvc.LocalService("workflow") + for id in ids: + wf_service.trg_write(user, self._name, id, cr) + self._update_function_stored(cr, user, ids, context=context) + + if self._name in self.pool._store_function.keys(): + list_store = self.pool._store_function[self._name] + for tuple_fn in list_store: + flag = False + if not tuple_fn[3]: + flag = True + for field in tuple_fn[3]: + if field in vals.keys(): + flag = True + break + if flag: + id_change = self._store_get_ids(cr, user, ids[0],tuple_fn, context) + self._store_set_values(cr, user, ids[0],tuple_fn,id_change, context) + + return True + + # + # TODO: Should set perm to user.xxx + # + def create(self, cr, user, vals, context=None): + """ create(cr, user, vals, context) -> int + cr = database cursor + user = user id + vals = dictionary of the form {'field_name':field_value, ...} + """ + if not context: + context={} + self.pool.get('ir.model.access').check(cr, user, self._name, 'create') + + default = [] + + avoid_table = [] + for (t,c) in self._inherits.items(): + if c in vals: + avoid_table.append(t) + for f in self._columns.keys(): # + self._inherit_fields.keys(): + if not f in vals: + default.append(f) + for f in self._inherit_fields.keys(): + if (not f in vals) and (not self._inherit_fields[f][0] in avoid_table): + default.append(f) + + if len(default): + vals.update(self.default_get(cr, user, default, context)) + + tocreate = {} + for v in self._inherits: + if self._inherits[v] not in vals: + tocreate[v] = {} + + (upd0, upd1, upd2) = ('', '', []) + upd_todo = [] + + for v in vals.keys(): + if v in self._inherit_fields: + (table,col,col_detail) = self._inherit_fields[v] + tocreate[table][v] = vals[v] + del vals[v] + + cr.execute("SELECT nextval('"+self._sequence+"')") + id_new = cr.fetchone()[0] + for table in tocreate: + id = self.pool.get(table).create(cr, user, tocreate[table]) + upd0 += ','+self._inherits[table] + upd1 += ',%d' + upd2.append(id) + + for field in vals: + if self._columns[field]._classic_write: + upd0=upd0+',"'+field+'"' + upd1=upd1+','+self._columns[field]._symbol_set[0] + upd2.append(self._columns[field]._symbol_set[1](vals[field])) + else: + upd_todo.append(field) + 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)) + if self._log_access: + upd0 += ',create_uid,create_date' + upd1 += ',%d,now()' + upd2.append(user) + cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2)) + upd_todo.sort(lambda x,y: self._columns[x].priority-self._columns[y].priority) + for field in upd_todo: + self._columns[field].set(cr, self, id_new, field, vals[field], user, context) + + self._validate(cr, user, [id_new], context) + + wf_service = netsvc.LocalService("workflow") + wf_service.trg_create(user, self._name, id_new, cr) + self._update_function_stored(cr, user, [id_new], context=context) + if self._name in self.pool._store_function.keys(): + list_store = self.pool._store_function[self._name] + for tuple_fn in list_store: + id_change = self._store_get_ids(cr, user, id_new,tuple_fn, context) + self._store_set_values(cr, user, id_new,tuple_fn,id_change, context) + + return id_new + + def _store_get_ids(self,cr, uid, ids,tuple_fn, context): + parent_id=getattr(self.pool.get(tuple_fn[0]),tuple_fn[4].func_name)(cr,uid,[ids]) + return parent_id + + def _store_set_values(self,cr,uid,ids,tuple_fn,parent_id,context): + name = tuple_fn[1] + table = tuple_fn[0] + args={} + vals_tot=getattr(self.pool.get(table),tuple_fn[2])(cr, uid, parent_id ,name, args, context) + write_dict = {} + for id in vals_tot.keys(): + write_dict[name]=vals_tot[id] + self.pool.get(table).write(cr,uid,[id],write_dict) + return True + + def _update_function_stored(self, cr, user, ids, context=None): + if not context: + context = {} + f = filter(lambda a: isinstance(self._columns[a], fields.function) \ + and self._columns[a].store, self._columns) + if f: + result=self.read(cr, user, ids, fields=f, context=context) + for res in result: + upd0=[] + upd1=[] + for field in res: + if field not in f: + continue + value = res[field] + if self._columns[field]._type in ('many2one', 'one2one'): + value = res[field][0] + upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0]) + upd1.append(self._columns[field]._symbol_set[1](value)) + upd1.append(res['id']) + cr.execute('update "' + self._table + '" set ' + \ + string.join(upd0, ',') + ' where id = %d', upd1) + return True + + # + # TODO: Validate + # + def perm_write(self, cr, user, ids, fields, context=None): + raise _('This method does not exist anymore') + + # TODO: ameliorer avec NULL + def _where_calc(self, cr, user, args, active_test=True, context=None): + if not context: + context={} + args = args[:] + # if the object has a field named 'active', filter out all inactive + # records unless they were explicitely asked for + if 'active' in self._columns and (active_test and context.get('active_test', True)): + i = 0 + active_found = False + while i<len(args): + if args[i][0]=='active': + active_found = True + i += 1 + if not active_found: + args.append(('active', '=', 1)) + + i = 0 + tables=['"'+self._table+'"'] + joins=[] + while i<len(args): + table=self + assert args[i][1] in ('like','!=','ilike','=like', 'not like', 'not ilike', 'not in','inselect','child_of','in','=','<>','<','>','>=','<='), _('Error ! Bad clause operand "%s".') % (args[i][1],) + if args[i][1] == 'inselect': + raise except_orm(_('ValidateError'), + _("The clause 'inselect' can not be used outside the orm!")) + if args[i][0] in self._inherit_fields: + table=self.pool.get(self._inherit_fields[args[i][0]][0]) + if ('"'+table._table+'"' not in tables): + tables.append('"'+table._table+'"') + joins.append(('id', 'join', '%s.%s' % (self._table, self._inherits[table._name]), table)) + fargs = args[i][0].split('.', 1) + field = table._columns.get(fargs[0],False) + if not field: + if args[i][0] == 'id' and args[i][1] == 'child_of': + ids2 = args[i][2] + def _rec_get(ids, table, parent): + if not ids: + return [] + ids2 = table.search(cr, user, [(parent, 'in', ids)], context=context) + return ids + _rec_get(ids2, table, parent) + args[i] = (args[i][0], 'in', ids2 + _rec_get(ids2, table, table._parent_name), table) + i+=1 + continue + if len(fargs) > 1: + if field._type == 'many2one': + args[i] = (fargs[0], 'in', self.pool.get(field._obj).search(cr, user, [(fargs[1], args[i][1], args[i][2])], context=context)) + i+=1 + continue + else: + i+=1 + continue + if field._properties: + arg = [args.pop(i)] + j = i + while j<len(args): + if args[j][0]==arg[0][0]: + arg.append(args.pop(j)) + else: + j+=1 + if field._fnct_search: + args.extend(field.search(cr, user, self, arg[0][0], arg)) + if field.store: + args.extend(arg) + i+=1 + elif field._type=='one2many': + field_obj = self.pool.get(field._obj) + + if isinstance(args[i][2], basestring): + # get the ids of the records of the "distant" resource + ids2 = [x[0] for x in field_obj.name_search(cr, user, args[i][2], [], args[i][1])] + else: + ids2 = args[i][2] + if not ids2: + args[i] = ('id','=','0') + else: + ids3 = [] + for j in range((len(ids2) / ID_MAX) + (len(ids2) % ID_MAX)): + sub_ids2 = ids2[ID_MAX * j:ID_MAX * (j + 1)] + if sub_ids2: + cr.execute('SELECT "'+field._fields_id+'" ' \ + 'FROM "'+field_obj._table+'" ' \ + 'WHERE id in ('+','.join(map(str,sub_ids2))+')') + ids3.extend([x[0] for x in cr.fetchall()]) + + args[i] = ('id', 'in', ids3) + i+=1 + + elif field._type=='many2many': + #FIXME + if args[i][1]=='child_of': + if isinstance(args[i][2], basestring): + ids2 = [x[0] for x in self.pool.get(field._obj).name_search(cr, user, args[i][2], [], 'like')] + else: + ids2 = args[i][2] + def _rec_get(ids, table, parent): + if not ids: + return [] + ids2 = table.search(cr, user, [(parent, 'in', ids)], context=context) + return ids + _rec_get(ids2, table, parent) + def _rec_convert(ids): + if self.pool.get(field._obj)==self: + return ids + if not len(ids): return [] + cr.execute('select "'+field._id1+'" from "'+field._rel+'" where "'+field._id2+'" in ('+','.join(map(str,ids))+')') + ids = [x[0] for x in cr.fetchall()] + return ids + args[i] = ('id','in',_rec_convert(ids2+_rec_get(ids2, self.pool.get(field._obj), table._parent_name))) + else: + if isinstance(args[i][2], basestring): + res_ids = [x[0] for x in self.pool.get(field._obj).name_search(cr, user, args[i][2], [], args[i][1])] + else: + res_ids = args[i][2] + if not len(res_ids): + args[i] = ('id', 'in', [0]) + else: + cr.execute('select "'+field._id1+'" from "'+field._rel+'" where "'+field._id2+'" in ('+','.join(map(str, res_ids))+')') + args[i] = ('id', 'in', map(lambda x: x[0], cr.fetchall())) + i+=1 + + elif field._type=='many2one': + if args[i][1]=='child_of': + if isinstance(args[i][2], basestring): + ids2 = [x[0] for x in self.pool.get(field._obj).name_search(cr, user, args[i][2], [], 'like')] + else: + ids2 = args[i][2] + def _rec_get(ids, table, parent): + if not ids: + return [] + ids2 = table.search(cr, user, [(parent, 'in', ids)], context=context) + return ids + _rec_get(ids2, table, parent) + if field._obj <> table._name: + args[i] = (args[i][0],'in',ids2+_rec_get(ids2, self.pool.get(field._obj), table._parent_name), table) + else: + args[i] = ('id','in',ids2+_rec_get(ids2, table, args[i][0]), table) + else: + if isinstance(args[i][2], basestring): + res_ids = self.pool.get(field._obj).name_search(cr, user, args[i][2], [], args[i][1]) + args[i] = (args[i][0],'in',map(lambda x: x[0], res_ids), table) + else: + args[i] += (table,) + i+=1 + else: + if field.translate: + if args[i][1] in ('like', 'ilike', 'not like', 'not ilike'): + args[i] = (args[i][0], args[i][1], '%%%s%%' % args[i][2]) + query1 = '(SELECT res_id FROM ir_translation ' \ + 'WHERE name = %s AND lang = %s ' \ + 'AND type = %s ' \ + 'AND VALUE ' + args[i][1] + ' %s)' + query2 = [table._name + ',' + args[i][0], + context.get('lang', False) or 'en_US', + 'model', + args[i][2]] + query1 += ' UNION ' + query1 += '(SELECT id FROM "' + table._table + '" ' \ + 'WHERE "' + args[i][0] + '" ' + args[i][1] + ' %s)' + query2 += [args[i][2]] + args[i] = ('id', 'inselect', (query1, query2), table) + else: + args[i] += (table,) + i+=1 + args.extend(joins) + + qu1, qu2 = [], [] + for x in args: + table=self + if len(x) > 3: + table=x[3] + if x[1] == 'inselect': + qu1.append('(%s.%s in (%s))' % (table._table, x[0], x[2][0])) + qu2 += x[2][1] + elif x[1]=='not in': + if len(x[2])>0: + todel = [] + for xitem in range(len(x[2])): + if x[2][xitem]==False and isinstance(x[2][xitem],bool): + todel.append(xitem) + for xitem in todel[::-1]: + del x[2][xitem] + if x[0]=='id': + qu1.append('(%s.id not in (%s))' % (table._table, ','.join(['%d'] * len(x[2])),)) + else: + qu1.append('(%s.%s not in (%s))' % (table._table, x[0], ','.join([table._columns[x[0]]._symbol_set[0]]*len(x[2])))) + if todel: + qu1[-1] = '('+qu1[-1]+' or '+x[0]+' is null)' + qu2+=x[2] + else: + qu1.append(' (1=0)') + elif x[1] != 'in': #FIXME: this replace all (..., '=', False) values with 'is null' and this is # not what we want for real boolean fields. The problem is, we can't change it # easily because we use False everywhere instead of None # NOTE FAB: we can't use None becStay tunes ! ause it is not accepted by XML-RPC, that's why # boolean (0-1), None -> False # Ged> boolean fields are not always = 0 or 1 - if (x[2] is False) and (x[1]=='='): - qu1.append(x[0]+' is null') - elif (x[2] is False) and (x[1]=='<>' or x[1]=='!='): - qu1.append(x[0]+' is not null') - else: - if x[0]=='id': - if x[1]=='join': - qu1.append('(%s.%s = %s)' % (table._table, x[0], x[2])) - else: - qu1.append('(%s.%s %s %%s)' % (table._table, x[0], x[1])) - qu2.append(x[2]) - else: - add_null = False - if x[1] in ('like', 'ilike', 'not like', 'not ilike'): - if isinstance(x[2], str): - str_utf8 = x[2] - elif isinstance(x[2], unicode): - str_utf8 = x[2].encode('utf-8') - else: - str_utf8 = str(x[2]) - qu2.append('%%%s%%' % str_utf8) - if not str_utf8: - add_null = True - else: - if x[0] in table._columns: - qu2.append(table._columns[x[0]]._symbol_set[1](x[2])) - if x[1]=='=like': - x1 = 'like' - else: - x1 = x[1] - if x[0] in table._columns: - if x[1] in ('like', 'ilike', 'not like', 'not ilike'): - - qu1.append('(%s.%s %s %s)' % (table._table, - x[0], x1, '%s')) - else: - qu1.append('(%s.%s %s %s)' % (table._table, - x[0], x1, - table._columns[x[0]]._symbol_set[0])) - else: - qu1.append('(%s.%s %s \'%s\')' % (table._table, x[0], x1, x[2])) - - if add_null: - qu1[-1] = '('+qu1[-1]+' or '+x[0]+' is null)' - elif x[1]=='in': - if len(x[2])>0: - todel = [] - for xitem in range(len(x[2])): - if x[2][xitem]==False and isinstance(x[2][xitem],bool): - todel.append(xitem) - for xitem in todel[::-1]: - del x[2][xitem] - #TODO fix max_stack_depth - if x[0]=='id': - qu1.append('(%s.id in (%s))' % (table._table, ','.join(['%d'] * len(x[2])),)) - else: - qu1.append('(%s.%s in (%s))' % (table._table, x[0], ','.join([table._columns[x[0]]._symbol_set[0]]*len(x[2])))) - if todel: - qu1[-1] = '('+qu1[-1]+' or '+x[0]+' is null)' - qu2+=x[2] - else: - qu1.append(' (1=0)') - return (qu1,qu2,tables) - - def _check_qorder(self, word): - if not regex_order.match(word): - raise except_orm(_('AccessError'), _('Bad query.')) - return True - - def search(self, cr, user, args, offset=0, limit=None, order=None, - context=None, count=False): - if not context: - context = {} - # compute the where, order by, limit and offset clauses - (qu1,qu2,tables) = self._where_calc(cr, user, args, context=context) - - if len(qu1): - qu1 = ' where '+string.join(qu1,' and ') - else: - qu1 = '' - - if order: - self._check_qorder(order) - order_by = order or self._order - - limit_str = limit and ' limit %d' % limit or '' - offset_str = offset and ' offset %d' % offset or '' - - - # construct a clause for the rules : - d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) - if d1: - qu1 = qu1 and qu1+' and '+d1 or ' where '+d1 - qu2 += d2 - - if count: - cr.execute('select count(%s.id) from ' % self._table + - ','.join(tables) +qu1 + limit_str + offset_str, qu2) - res = cr.fetchall() - return res[0][0] - # execute the "main" query to fetch the ids we were searching for - cr.execute('select %s.id from ' % self._table + ','.join(tables) +qu1+' order by '+order_by+limit_str+offset_str, qu2) - res = cr.fetchall() - return [x[0] for x in res] - - # returns the different values ever entered for one field - # this is used, for example, in the client when the user hits enter on - # a char field - def distinct_field_get(self, cr, uid, field, value, args=None, offset=0, limit=None): - if not args: - args=[] - if field in self._inherit_fields: - return self.pool.get(self._inherit_fields[field][0]).distinct_field_get(cr, uid,field,value,args,offset,limit) - else: - return self._columns[field].search(cr, self, args, field, value, offset, limit, uid) - - def name_get(self, cr, user, ids, context=None): - if not context: - context={} - if not ids: - return [] - if isinstance(ids, (int, long)): - ids = [ids] - return [(r['id'], str(r[self._rec_name])) for r in self.read(cr, user, ids, - [self._rec_name], context, load='_classic_write')] - - def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): - if not args: - args=[] - if not context: - context={} - args=args[:] - if name: - args += [(self._rec_name,operator,name)] - ids = self.search(cr, user, args, limit=limit, context=context) - res = self.name_get(cr, user, ids, context) - return res - - def copy(self, cr, uid, id, default=None, context=None): - if not context: - context={} - if not default: - default = {} - if 'state' not in default: - if 'state' in self._defaults: - default['state'] = self._defaults['state'](self, cr, uid, context) - data = self.read(cr, uid, [id], context=context)[0] - fields = self.fields_get(cr, uid) - for f in fields: - ftype = fields[f]['type'] - - if self._log_access and f in ('create_date', 'create_uid', 'write_date', 'write_uid'): - del data[f] - - if f in default: - data[f] = default[f] - elif ftype == 'function': - del data[f] - elif ftype == 'many2one': - try: - data[f] = data[f] and data[f][0] - except: - pass - elif ftype in ('one2many', 'one2one'): - res = [] - rel = self.pool.get(fields[f]['relation']) - for rel_id in data[f]: - # the lines are first duplicated using the wrong (old) - # parent but then are reassigned to the correct one thanks - # to the (4, ...) - res.append((4, rel.copy(cr, uid, rel_id, context=context))) - data[f] = res - elif ftype == 'many2many': - data[f] = [(6, 0, data[f])] - del data['id'] - for v in self._inherits: - del data[self._inherits[v]] - return self.create(cr, uid, data) - - def read_string(self, cr, uid, id, langs, fields=None, context=None): - if not context: - context={} - res = {} - res2 = {} - self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'read') - if not fields: - fields = self._columns.keys() + self._inherit_fields.keys() - for lang in langs: - res[lang] = {'code': lang} - for f in fields: - if f in self._columns: - res_trans = self.pool.get('ir.translation')._get_source(cr, uid, self._name+','+f, 'field', lang) - if res_trans: - res[lang][f]=res_trans - else: - res[lang][f]=self._columns[f].string - for table in self._inherits: - cols = intersect(self._inherit_fields.keys(), fields) - res2 = self.pool.get(table).read_string(cr, uid, id, langs, cols, context) - for lang in res2: - if lang in res: - res[lang]={'code': lang} - for f in res2[lang]: - res[lang][f]=res2[lang][f] - return res - - def write_string(self, cr, uid, id, langs, vals, context=None): - if not context: - context={} - self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'write') - for lang in langs: - for field in vals: - if field in self._columns: - self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field]) - for table in self._inherits: - cols = intersect(self._inherit_fields.keys(), vals) - if cols: - self.pool.get(table).write_string(cr, uid, id, langs, vals, context) - return True - - def check_recursion(self, cr, uid, ids, parent=None): - if not parent: - parent = self._parent_name - ids_parent = ids[:] - while len(ids_parent): - ids_parent2 = [] - for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): - sub_ids_parent = ids_parent[ID_MAX * i:ID_MAX * (i + 1)] - cr.execute('SELECT distinct "'+parent+'"'+ - ' FROM "'+self._table+'" ' \ - 'WHERE id in ('+','.join(map(str, sub_ids_parent))+')') - ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall()))) - ids_parent = ids_parent2 - for i in ids_parent: - if i in ids: - return False - return True + if (x[2] is False) and (x[1]=='='): + qu1.append(x[0]+' is null') + elif (x[2] is False) and (x[1]=='<>' or x[1]=='!='): + qu1.append(x[0]+' is not null') + else: + if x[0]=='id': + if x[1]=='join': + qu1.append('(%s.%s = %s)' % (table._table, x[0], x[2])) + else: + qu1.append('(%s.%s %s %%s)' % (table._table, x[0], x[1])) + qu2.append(x[2]) + else: + add_null = False + if x[1] in ('like', 'ilike', 'not like', 'not ilike'): + if isinstance(x[2], str): + str_utf8 = x[2] + elif isinstance(x[2], unicode): + str_utf8 = x[2].encode('utf-8') + else: + str_utf8 = str(x[2]) + qu2.append('%%%s%%' % str_utf8) + if not str_utf8: + add_null = True + else: + if x[0] in table._columns: + qu2.append(table._columns[x[0]]._symbol_set[1](x[2])) + if x[1]=='=like': + x1 = 'like' + else: + x1 = x[1] + if x[0] in table._columns: + if x[1] in ('like', 'ilike', 'not like', 'not ilike'): + + qu1.append('(%s.%s %s %s)' % (table._table, + x[0], x1, '%s')) + else: + qu1.append('(%s.%s %s %s)' % (table._table, + x[0], x1, + table._columns[x[0]]._symbol_set[0])) + else: + qu1.append('(%s.%s %s \'%s\')' % (table._table, x[0], x1, x[2])) + + if add_null: + qu1[-1] = '('+qu1[-1]+' or '+x[0]+' is null)' + elif x[1]=='in': + if len(x[2])>0: + todel = [] + for xitem in range(len(x[2])): + if x[2][xitem]==False and isinstance(x[2][xitem],bool): + todel.append(xitem) + for xitem in todel[::-1]: + del x[2][xitem] + #TODO fix max_stack_depth + if x[0]=='id': + qu1.append('(%s.id in (%s))' % (table._table, ','.join(['%d'] * len(x[2])),)) + else: + qu1.append('(%s.%s in (%s))' % (table._table, x[0], ','.join([table._columns[x[0]]._symbol_set[0]]*len(x[2])))) + if todel: + qu1[-1] = '('+qu1[-1]+' or '+x[0]+' is null)' + qu2+=x[2] + else: + qu1.append(' (1=0)') + return (qu1,qu2,tables) + + def _check_qorder(self, word): + if not regex_order.match(word): + raise except_orm(_('AccessError'), _('Bad query.')) + return True + + def search(self, cr, user, args, offset=0, limit=None, order=None, + context=None, count=False): + if not context: + context = {} + # compute the where, order by, limit and offset clauses + (qu1,qu2,tables) = self._where_calc(cr, user, args, context=context) + + if len(qu1): + qu1 = ' where '+string.join(qu1,' and ') + else: + qu1 = '' + + if order: + self._check_qorder(order) + order_by = order or self._order + + limit_str = limit and ' limit %d' % limit or '' + offset_str = offset and ' offset %d' % offset or '' + + + # construct a clause for the rules : + d1, d2 = self.pool.get('ir.rule').domain_get(cr, user, self._name) + if d1: + qu1 = qu1 and qu1+' and '+d1 or ' where '+d1 + qu2 += d2 + + if count: + cr.execute('select count(%s.id) from ' % self._table + + ','.join(tables) +qu1 + limit_str + offset_str, qu2) + res = cr.fetchall() + return res[0][0] + # execute the "main" query to fetch the ids we were searching for + cr.execute('select %s.id from ' % self._table + ','.join(tables) +qu1+' order by '+order_by+limit_str+offset_str, qu2) + res = cr.fetchall() + return [x[0] for x in res] + + # returns the different values ever entered for one field + # this is used, for example, in the client when the user hits enter on + # a char field + def distinct_field_get(self, cr, uid, field, value, args=None, offset=0, limit=None): + if not args: + args=[] + if field in self._inherit_fields: + return self.pool.get(self._inherit_fields[field][0]).distinct_field_get(cr, uid,field,value,args,offset,limit) + else: + return self._columns[field].search(cr, self, args, field, value, offset, limit, uid) + + def name_get(self, cr, user, ids, context=None): + if not context: + context={} + if not ids: + return [] + if isinstance(ids, (int, long)): + ids = [ids] + return [(r['id'], str(r[self._rec_name])) for r in self.read(cr, user, ids, + [self._rec_name], context, load='_classic_write')] + + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=80): + if not args: + args=[] + if not context: + context={} + args=args[:] + if name: + args += [(self._rec_name,operator,name)] + ids = self.search(cr, user, args, limit=limit, context=context) + res = self.name_get(cr, user, ids, context) + return res + + def copy(self, cr, uid, id, default=None, context=None): + if not context: + context={} + if not default: + default = {} + if 'state' not in default: + if 'state' in self._defaults: + default['state'] = self._defaults['state'](self, cr, uid, context) + data = self.read(cr, uid, [id], context=context)[0] + fields = self.fields_get(cr, uid) + for f in fields: + ftype = fields[f]['type'] + + if self._log_access and f in ('create_date', 'create_uid', 'write_date', 'write_uid'): + del data[f] + + if f in default: + data[f] = default[f] + elif ftype == 'function': + del data[f] + elif ftype == 'many2one': + try: + data[f] = data[f] and data[f][0] + except: + pass + elif ftype in ('one2many', 'one2one'): + res = [] + rel = self.pool.get(fields[f]['relation']) + for rel_id in data[f]: + # the lines are first duplicated using the wrong (old) + # parent but then are reassigned to the correct one thanks + # to the (4, ...) + res.append((4, rel.copy(cr, uid, rel_id, context=context))) + data[f] = res + elif ftype == 'many2many': + data[f] = [(6, 0, data[f])] + del data['id'] + for v in self._inherits: + del data[self._inherits[v]] + return self.create(cr, uid, data) + + def read_string(self, cr, uid, id, langs, fields=None, context=None): + if not context: + context={} + res = {} + res2 = {} + self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'read') + if not fields: + fields = self._columns.keys() + self._inherit_fields.keys() + for lang in langs: + res[lang] = {'code': lang} + for f in fields: + if f in self._columns: + res_trans = self.pool.get('ir.translation')._get_source(cr, uid, self._name+','+f, 'field', lang) + if res_trans: + res[lang][f]=res_trans + else: + res[lang][f]=self._columns[f].string + for table in self._inherits: + cols = intersect(self._inherit_fields.keys(), fields) + res2 = self.pool.get(table).read_string(cr, uid, id, langs, cols, context) + for lang in res2: + if lang in res: + res[lang]={'code': lang} + for f in res2[lang]: + res[lang][f]=res2[lang][f] + return res + + def write_string(self, cr, uid, id, langs, vals, context=None): + if not context: + context={} + self.pool.get('ir.model.access').check(cr, uid, 'ir.translation', 'write') + for lang in langs: + for field in vals: + if field in self._columns: + self.pool.get('ir.translation')._set_ids(cr, uid, self._name+','+field, 'field', lang, [0], vals[field]) + for table in self._inherits: + cols = intersect(self._inherit_fields.keys(), vals) + if cols: + self.pool.get(table).write_string(cr, uid, id, langs, vals, context) + return True + + def check_recursion(self, cr, uid, ids, parent=None): + if not parent: + parent = self._parent_name + ids_parent = ids[:] + while len(ids_parent): + ids_parent2 = [] + for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)): + sub_ids_parent = ids_parent[ID_MAX * i:ID_MAX * (i + 1)] + cr.execute('SELECT distinct "'+parent+'"'+ + ' FROM "'+self._table+'" ' \ + 'WHERE id in ('+','.join(map(str, sub_ids_parent))+')') + ids_parent2.extend(filter(None, map(lambda x: x[0], cr.fetchall()))) + ids_parent = ids_parent2 + for i in ids_parent: + if i in ids: + return False + return True # vim:noexpandtab:ts=4 diff --git a/bin/osv/osv.py b/bin/osv/osv.py index 3b948aec5d3..63cc52408b8 100644 --- a/bin/osv/osv.py +++ b/bin/osv/osv.py @@ -45,315 +45,315 @@ module_class_list = {} class_pool = {} class except_osv(Exception): - def __init__(self, name, value, exc_type='warning'): - self.name = name - self.exc_type = exc_type - self.value = value - self.args = (exc_type,name) + def __init__(self, name, value, exc_type='warning'): + self.name = name + self.exc_type = exc_type + self.value = value + self.args = (exc_type,name) class osv_pool(netsvc.Service): - def __init__(self): - self.obj_pool = {} - self.module_object_list = {} - self.created = [] - self._sql_error = {} - self._store_function = {} - netsvc.Service.__init__(self, 'object_proxy', audience='') - self.joinGroup('web-services') - self.exportMethod(self.exportedMethods) - self.exportMethod(self.obj_list) - self.exportMethod(self.exec_workflow) - self.exportMethod(self.execute) - self.exportMethod(self.execute_cr) - - def execute_cr(self, cr, uid, obj, method, *args, **kw): - try: - object = pooler.get_pool(cr.dbname).get(obj) - if not object: - self.abortResponse(1, 'Object Error', 'warning', - 'Object %s doesn\'t exist' % str(obj)) - return getattr(object,method)(cr, uid, *args, **kw) - except orm.except_orm, inst: - self.abortResponse(1, inst.name, 'warning', inst.value) - except except_osv, inst: - self.abortResponse(1, inst.name, inst.exc_type, inst.value) - except psycopg.IntegrityError, inst: - for key in self._sql_error.keys(): - if key in inst[0]: - self.abortResponse(1, 'Constraint Error', 'warning', - self._sql_error[key]) - self.abortResponse(1, 'Integrity Error', 'warning', inst[0]) - except Exception, e: - import traceback - tb_s = reduce(lambda x, y: x+y, traceback.format_exception( - sys.exc_type, sys.exc_value, sys.exc_traceback)) - logger = Logger() - logger.notifyChannel("web-services", LOG_ERROR, - 'Exception in call: ' + tb_s) - raise - - def execute(self, db, uid, obj, method, *args, **kw): - db, pool = pooler.get_db_and_pool(db) - cr = db.cursor() - try: - try: - res = pool.execute_cr(cr, uid, obj, method, *args, **kw) - cr.commit() - except Exception: - cr.rollback() - raise - finally: - cr.close() - return res - - def exec_workflow_cr(self, cr, uid, obj, method, *args): - wf_service = netsvc.LocalService("workflow") - return wf_service.trg_validate(uid, obj, args[0], method, cr) - - def exec_workflow(self, db, uid, obj, method, *args): - cr = pooler.get_db(db).cursor() - try: - try: - res = self.exec_workflow_cr(cr, uid, obj, method, *args) - cr.commit() - except orm.except_orm, inst: - cr.rollback() - self.abortResponse(1, inst.name, 'warning', inst.value) - except except_osv, inst: - cr.rollback() - self.abortResponse(1, inst.name, inst[0], inst.value) - finally: - cr.close() - return res - - def obj_list(self): - return self.obj_pool.keys() - - # adds a new object instance to the object pool. - # if it already existed, the instance is replaced - def add(self, name, obj_inst): - if self.obj_pool.has_key(name): - del self.obj_pool[name] - self.obj_pool[name] = obj_inst - - module = str(obj_inst.__class__)[6:] - module = module[:len(module)-1] - module = module.split('.')[0][2:] - self.module_object_list.setdefault(module, []).append(obj_inst) - - def get(self, name): - obj = self.obj_pool.get(name, None) + def __init__(self): + self.obj_pool = {} + self.module_object_list = {} + self.created = [] + self._sql_error = {} + self._store_function = {} + netsvc.Service.__init__(self, 'object_proxy', audience='') + self.joinGroup('web-services') + self.exportMethod(self.exportedMethods) + self.exportMethod(self.obj_list) + self.exportMethod(self.exec_workflow) + self.exportMethod(self.execute) + self.exportMethod(self.execute_cr) + + def execute_cr(self, cr, uid, obj, method, *args, **kw): + try: + object = pooler.get_pool(cr.dbname).get(obj) + if not object: + self.abortResponse(1, 'Object Error', 'warning', + 'Object %s doesn\'t exist' % str(obj)) + return getattr(object,method)(cr, uid, *args, **kw) + except orm.except_orm, inst: + self.abortResponse(1, inst.name, 'warning', inst.value) + except except_osv, inst: + self.abortResponse(1, inst.name, inst.exc_type, inst.value) + except psycopg.IntegrityError, inst: + for key in self._sql_error.keys(): + if key in inst[0]: + self.abortResponse(1, 'Constraint Error', 'warning', + self._sql_error[key]) + self.abortResponse(1, 'Integrity Error', 'warning', inst[0]) + except Exception, e: + import traceback + tb_s = reduce(lambda x, y: x+y, traceback.format_exception( + sys.exc_type, sys.exc_value, sys.exc_traceback)) + logger = Logger() + logger.notifyChannel("web-services", LOG_ERROR, + 'Exception in call: ' + tb_s) + raise + + def execute(self, db, uid, obj, method, *args, **kw): + db, pool = pooler.get_db_and_pool(db) + cr = db.cursor() + try: + try: + res = pool.execute_cr(cr, uid, obj, method, *args, **kw) + cr.commit() + except Exception: + cr.rollback() + raise + finally: + cr.close() + return res + + def exec_workflow_cr(self, cr, uid, obj, method, *args): + wf_service = netsvc.LocalService("workflow") + return wf_service.trg_validate(uid, obj, args[0], method, cr) + + def exec_workflow(self, db, uid, obj, method, *args): + cr = pooler.get_db(db).cursor() + try: + try: + res = self.exec_workflow_cr(cr, uid, obj, method, *args) + cr.commit() + except orm.except_orm, inst: + cr.rollback() + self.abortResponse(1, inst.name, 'warning', inst.value) + except except_osv, inst: + cr.rollback() + self.abortResponse(1, inst.name, inst[0], inst.value) + finally: + cr.close() + return res + + def obj_list(self): + return self.obj_pool.keys() + + # adds a new object instance to the object pool. + # if it already existed, the instance is replaced + def add(self, name, obj_inst): + if self.obj_pool.has_key(name): + del self.obj_pool[name] + self.obj_pool[name] = obj_inst + + module = str(obj_inst.__class__)[6:] + module = module[:len(module)-1] + module = module.split('.')[0][2:] + self.module_object_list.setdefault(module, []).append(obj_inst) + + def get(self, name): + obj = self.obj_pool.get(name, None) # We cannot uncomment this line because it breaks initialisation since objects do not initialize # in the correct order and the ORM doesnt support correctly when some objets do not exist yet -# assert obj, "object %s does not exist !" % name - return obj - - #TODO: pass a list of modules to load - def instanciate(self, module, cr): -# print "module list:", module_list -# for module in module_list: - res = [] - class_list = module_class_list.get(module, []) -# if module not in self.module_object_list: -# print "%s class_list:" % module, class_list - for klass in class_list: - res.append(klass.createInstance(self, module, cr)) - return res -# else: -# print "skipping module", module +# assert obj, "object %s does not exist !" % name + return obj + + #TODO: pass a list of modules to load + def instanciate(self, module, cr): +# print "module list:", module_list +# for module in module_list: + res = [] + class_list = module_class_list.get(module, []) +# if module not in self.module_object_list: +# print "%s class_list:" % module, class_list + for klass in class_list: + res.append(klass.createInstance(self, module, cr)) + return res +# else: +# print "skipping module", module #pooler.get_pool(cr.dbname) = osv_pool() class osv_memory(orm.orm_memory): - #__metaclass__ = inheritor - def __new__(cls): - module = str(cls)[6:] - module = module[:len(module)-1] - module = module.split('.')[0][2:] - if not hasattr(cls, '_module'): - cls._module = module - module_class_list.setdefault(cls._module, []).append(cls) - class_pool[cls._name] = cls - if module not in module_list: - module_list.append(cls._module) - return None - - # - # Goal: try to apply inheritancy at the instanciation level and - # put objects in the pool var - # - def createInstance(cls, pool, module, cr): - name = hasattr(cls,'_name') and cls._name or cls._inherit - parent_name = hasattr(cls, '_inherit') and cls._inherit - if parent_name: - print 'Inherit not supported in osv_memory object !' - obj = object.__new__(cls) - obj.__init__(pool, cr) - return obj - createInstance = classmethod(createInstance) - - def __init__(self, pool, cr): - pool.add(self._name, self) - self.pool = pool - orm.orm_memory.__init__(self, cr) + #__metaclass__ = inheritor + def __new__(cls): + module = str(cls)[6:] + module = module[:len(module)-1] + module = module.split('.')[0][2:] + if not hasattr(cls, '_module'): + cls._module = module + module_class_list.setdefault(cls._module, []).append(cls) + class_pool[cls._name] = cls + if module not in module_list: + module_list.append(cls._module) + return None + + # + # Goal: try to apply inheritancy at the instanciation level and + # put objects in the pool var + # + def createInstance(cls, pool, module, cr): + name = hasattr(cls,'_name') and cls._name or cls._inherit + parent_name = hasattr(cls, '_inherit') and cls._inherit + if parent_name: + print 'Inherit not supported in osv_memory object !' + obj = object.__new__(cls) + obj.__init__(pool, cr) + return obj + createInstance = classmethod(createInstance) + + def __init__(self, pool, cr): + pool.add(self._name, self) + self.pool = pool + orm.orm_memory.__init__(self, cr) class osv(orm.orm): - #__metaclass__ = inheritor - def __new__(cls): - module = str(cls)[6:] - module = module[:len(module)-1] - module = module.split('.')[0][2:] - if not hasattr(cls, '_module'): - cls._module = module - module_class_list.setdefault(cls._module, []).append(cls) - class_pool[cls._name] = cls - if module not in module_list: - module_list.append(cls._module) - return None - - # - # Goal: try to apply inheritancy at the instanciation level and - # put objects in the pool var - # - def createInstance(cls, pool, module, cr): - parent_name = hasattr(cls, '_inherit') and cls._inherit - if parent_name: - parent_class = pool.get(parent_name).__class__ - assert parent_class, "parent class %s does not exist !" % parent_name - nattr = {} - for s in ('_columns', '_defaults', '_inherits', '_constraints', '_sql_constraints'): - new = copy.copy(getattr(pool.get(parent_name), s)) - if hasattr(new, 'update'): - new.update(cls.__dict__.get(s, {})) - else: - new.extend(cls.__dict__.get(s, [])) - nattr[s] = new - name = hasattr(cls,'_name') and cls._name or cls._inherit - cls = type(name, (cls, parent_class), nattr) - obj = object.__new__(cls) - obj.__init__(pool, cr) - return obj - createInstance = classmethod(createInstance) - - def __init__(self, pool, cr): - pool.add(self._name, self) - self.pool = pool - orm.orm.__init__(self, cr) + #__metaclass__ = inheritor + def __new__(cls): + module = str(cls)[6:] + module = module[:len(module)-1] + module = module.split('.')[0][2:] + if not hasattr(cls, '_module'): + cls._module = module + module_class_list.setdefault(cls._module, []).append(cls) + class_pool[cls._name] = cls + if module not in module_list: + module_list.append(cls._module) + return None + + # + # Goal: try to apply inheritancy at the instanciation level and + # put objects in the pool var + # + def createInstance(cls, pool, module, cr): + parent_name = hasattr(cls, '_inherit') and cls._inherit + if parent_name: + parent_class = pool.get(parent_name).__class__ + assert parent_class, "parent class %s does not exist !" % parent_name + nattr = {} + for s in ('_columns', '_defaults', '_inherits', '_constraints', '_sql_constraints'): + new = copy.copy(getattr(pool.get(parent_name), s)) + if hasattr(new, 'update'): + new.update(cls.__dict__.get(s, {})) + else: + new.extend(cls.__dict__.get(s, [])) + nattr[s] = new + name = hasattr(cls,'_name') and cls._name or cls._inherit + cls = type(name, (cls, parent_class), nattr) + obj = object.__new__(cls) + obj.__init__(pool, cr) + return obj + createInstance = classmethod(createInstance) + + def __init__(self, pool, cr): + pool.add(self._name, self) + self.pool = pool + orm.orm.__init__(self, cr) class Cacheable(object): - _cache = UpdateableDict() + _cache = UpdateableDict() - def add(self, key, value): - self._cache[key] = value + def add(self, key, value): + self._cache[key] = value - def invalidate(self, key): - del self._cache[key] + def invalidate(self, key): + del self._cache[key] - def get(self, key): - try: - w = self._cache[key] - return w - except KeyError: - return None + def get(self, key): + try: + w = self._cache[key] + return w + except KeyError: + return None - def clear(self): - self._cache.clear() - self._items = [] + def clear(self): + self._cache.clear() + self._items = [] def filter_dict(d, fields): - res = {} - for f in fields + ['id']: - if f in d: - res[f] = d[f] - return res + res = {} + for f in fields + ['id']: + if f in d: + res[f] = d[f] + return res class cacheable_osv(osv, Cacheable): - _relevant = ['lang'] - - def __init__(self): - super(cacheable_osv, self).__init__() - - def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): - if not fields: - fields=[] - if not context: - context={} - fields = fields or self._columns.keys() - ctx = [context.get(x, False) for x in self._relevant] - result, tofetch = [], [] - for id in ids: - res = self.get(self._name, id, ctx) - if not res: - tofetch.append(id) - else: - result.append(filter_dict(res, fields)) - - # gen the list of "local" (ie not inherited) fields which are classic or many2one - nfields = filter(lambda x: x[1]._classic_write, self._columns.items()) - # gen the list of inherited fields - inherits = map(lambda x: (x[0], x[1][2]), self._inherit_fields.items()) - # complete the field list with the inherited fields which are classic or many2one - nfields += filter(lambda x: x[1]._classic_write, inherits) - nfields = [x[0] for x in nfields] - - res = super(cacheable_osv, self).read(cr, user, tofetch, nfields, context, load) - for r in res: - self.add((self._name, r['id'], ctx), r) - result.append(filter_dict(r, fields)) - - # Appel de fonction si necessaire - tofetch = [] - for f in fields: - if f not in nfields: - tofetch.append(f) - for f in tofetch: - fvals = self._columns[f].get(cr, self, ids, f, user, context=context) - for r in result: - r[f] = fvals[r['id']] - - # TODO: tri par self._order !! - return result - - def invalidate(self, key): - del self._cache[key[0]][key[1]] - - def write(self, cr, user, ids, values, context=None): - if not context: - context={} - for id in ids: - self.invalidate((self._name, id)) - return super(cacheable_osv, self).write(cr, user, ids, values, context) - - def unlink(self, cr, user, ids): - self.clear() - return super(cacheable_osv, self).unlink(cr, user, ids) + _relevant = ['lang'] + + def __init__(self): + super(cacheable_osv, self).__init__() + + def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'): + if not fields: + fields=[] + if not context: + context={} + fields = fields or self._columns.keys() + ctx = [context.get(x, False) for x in self._relevant] + result, tofetch = [], [] + for id in ids: + res = self.get(self._name, id, ctx) + if not res: + tofetch.append(id) + else: + result.append(filter_dict(res, fields)) + + # gen the list of "local" (ie not inherited) fields which are classic or many2one + nfields = filter(lambda x: x[1]._classic_write, self._columns.items()) + # gen the list of inherited fields + inherits = map(lambda x: (x[0], x[1][2]), self._inherit_fields.items()) + # complete the field list with the inherited fields which are classic or many2one + nfields += filter(lambda x: x[1]._classic_write, inherits) + nfields = [x[0] for x in nfields] + + res = super(cacheable_osv, self).read(cr, user, tofetch, nfields, context, load) + for r in res: + self.add((self._name, r['id'], ctx), r) + result.append(filter_dict(r, fields)) + + # Appel de fonction si necessaire + tofetch = [] + for f in fields: + if f not in nfields: + tofetch.append(f) + for f in tofetch: + fvals = self._columns[f].get(cr, self, ids, f, user, context=context) + for r in result: + r[f] = fvals[r['id']] + + # TODO: tri par self._order !! + return result + + def invalidate(self, key): + del self._cache[key[0]][key[1]] + + def write(self, cr, user, ids, values, context=None): + if not context: + context={} + for id in ids: + self.invalidate((self._name, id)) + return super(cacheable_osv, self).write(cr, user, ids, values, context) + + def unlink(self, cr, user, ids): + self.clear() + return super(cacheable_osv, self).unlink(cr, user, ids) #cacheable_osv = osv # vim:noexpandtab: #class FakePool(object): -# def __init__(self, module): -# self.preferred_module = module - -# def get(self, name): -# localpool = module_objects_dict.get(self.preferred_module, {'dict': {}})['dict'] -# if name in localpool: -# obj = localpool[name] -# else: -# obj = pooler.get_pool(cr.dbname).get(name) -# return obj - -# fake_pool = self -# class fake_class(obj.__class__): -# def __init__(self): -# super(fake_class, self).__init__() -# self.pool = fake_pool - -# return fake_class() +# def __init__(self, module): +# self.preferred_module = module + +# def get(self, name): +# localpool = module_objects_dict.get(self.preferred_module, {'dict': {}})['dict'] +# if name in localpool: +# obj = localpool[name] +# else: +# obj = pooler.get_pool(cr.dbname).get(name) +# return obj + +# fake_pool = self +# class fake_class(obj.__class__): +# def __init__(self): +# super(fake_class, self).__init__() +# self.pool = fake_pool + +# return fake_class() diff --git a/bin/pooler.py b/bin/pooler.py index b775934730f..52b9305b801 100644 --- a/bin/pooler.py +++ b/bin/pooler.py @@ -36,65 +36,65 @@ db_dic = {} pool_dic = {} def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False): - if not status: - status={} - if db_name in db_dic: - db = db_dic[db_name] - else: - logger = netsvc.Logger() - logger.notifyChannel('pooler', netsvc.LOG_INFO, 'Connecting to %s' % (db_name)) - db = sql_db.db_connect(db_name) - db_dic[db_name] = db - - if db_name in pool_dic: - pool = pool_dic[db_name] - else: - pool = osv.osv.osv_pool() - pool_dic[db_name] = pool - addons.load_modules(db, force_demo, status, update_module) + if not status: + status={} + if db_name in db_dic: + db = db_dic[db_name] + else: + logger = netsvc.Logger() + logger.notifyChannel('pooler', netsvc.LOG_INFO, 'Connecting to %s' % (db_name)) + db = sql_db.db_connect(db_name) + db_dic[db_name] = db + + if db_name in pool_dic: + pool = pool_dic[db_name] + else: + pool = osv.osv.osv_pool() + pool_dic[db_name] = pool + addons.load_modules(db, force_demo, status, update_module) - if not update_module: - import report - report.interface.register_all(db) - pool.get('ir.cron')._poolJobs(db.dbname) - return db, pool + if not update_module: + import report + report.interface.register_all(db) + pool.get('ir.cron')._poolJobs(db.dbname) + return db, pool def restart_pool(db_name, force_demo=False, update_module=False): -# del db_dic[db_name] - del pool_dic[db_name] - return get_db_and_pool(db_name, force_demo, update_module=update_module) +# del db_dic[db_name] + del pool_dic[db_name] + return get_db_and_pool(db_name, force_demo, update_module=update_module) def close_db(db_name): - if db_name in db_dic: - db_dic[db_name].truedb.close() - del db_dic[db_name] - if db_name in pool_dic: - del pool_dic[db_name] + if db_name in db_dic: + db_dic[db_name].truedb.close() + del db_dic[db_name] + if db_name in pool_dic: + del pool_dic[db_name] def get_db_only(db_name): - if db_name in db_dic: - db = db_dic[db_name] - else: - db = sql_db.db_connect(db_name) - db_dic[db_name] = db - return db + if db_name in db_dic: + db = db_dic[db_name] + else: + db = sql_db.db_connect(db_name) + db_dic[db_name] = db + return db def get_db(db_name): -# print "get_db", db_name - return get_db_and_pool(db_name)[0] +# print "get_db", db_name + return get_db_and_pool(db_name)[0] def get_pool(db_name, force_demo=False, status=None, update_module=False): -# print "get_pool", db_name - pool = get_db_and_pool(db_name, force_demo, status, update_module)[1] -# addons.load_modules(db_name, False) -# if not pool.obj_list(): -# pool.instanciate() -# print "pool", pool - return pool -# return get_db_and_pool(db_name)[1] +# print "get_pool", db_name + pool = get_db_and_pool(db_name, force_demo, status, update_module)[1] +# addons.load_modules(db_name, False) +# if not pool.obj_list(): +# pool.instanciate() +# print "pool", pool + return pool +# return get_db_and_pool(db_name)[1] def init(): - global db -# db = get_db_only(tools.config['db_name']) - sql_db.init() + global db +# db = get_db_only(tools.config['db_name']) + sql_db.init() diff --git a/bin/report/common.py b/bin/report/common.py index 218ed8d210d..b8cc3593761 100644 --- a/bin/report/common.py +++ b/bin/report/common.py @@ -28,6 +28,6 @@ ############################################################################## pageSize = { - 'A4': (210,297), - 'A5': (148.5,105) + 'A4': (210,297), + 'A5': (148.5,105) } diff --git a/bin/report/custom.py b/bin/report/custom.py index 77fb343b47c..ed072e330d2 100644 --- a/bin/report/custom.py +++ b/bin/report/custom.py @@ -46,12 +46,12 @@ import misc import StringIO class external_pdf(render.render): - def __init__(self, pdf): - render.render.__init__(self) - self.pdf = pdf - self.output_type='pdf' - def _render(self): - return self.pdf + def __init__(self, pdf): + render.render.__init__(self) + self.pdf = pdf + self.output_type='pdf' + def _render(self): + return self.pdf theme.use_color = 1 @@ -61,603 +61,603 @@ theme.use_color = 1 # heuu, ca marche pas ds tous les cas car graphs sont generes en pdf directment # par pychart, et on passe donc pas par du rml class report_custom(report_int): - def __init__(self, name): - report_int.__init__(self, name) - # - # PRE: - # fields = [['address','city'],['name'], ['zip']] - # conditions = [[('zip','==','3'),(,)],(,),(,)] #same structure as fields - # row_canvas = ['Rue', None, None] - # POST: - # [ ['ville','name','zip'] ] - # - def _row_get(self, cr, uid, objs, fields, conditions, row_canvas=None, group_by=None): - result = [] - tmp = [] - for obj in objs: - tobreak = False - for cond in conditions: - if cond and cond[0]: - c = cond[0] - temp = c[0](eval('obj.'+c[1])) - if not eval('\''+temp+'\''+' '+c[2]+' '+'\''+str(c[3])+'\''): - tobreak = True - if tobreak: - break - levels = {} - row = [] - for i in range(len(fields)): - if not fields[i]: - row.append(row_canvas and row_canvas[i]) - if row_canvas[i]: - row_canvas[i]=False - elif len(fields[i])==1: - if not isinstance(obj, browse_null): - row.append(str(eval('obj.'+fields[i][0]))) - else: - row.append(None) - else: - row.append(None) - levels[fields[i][0]]=True - if not levels: - result.append(row) - else: - # Process group_by data first - key = [] - if group_by != None and fields[group_by] != None: - if fields[group_by][0] in levels.keys(): - key.append(fields[group_by][0]) - for l in levels.keys(): - if l != fields[group_by][0]: - key.append(l) - else: - key = levels.keys() - for l in key: - objs = eval('obj.'+l) - if not isinstance(objs, browse_record_list) and type(objs) <> type([]): - objs = [objs] - field_new = [] - cond_new = [] - for f in range(len(fields)): - if (fields[f] and fields[f][0])==l: - field_new.append(fields[f][1:]) - cond_new.append(conditions[f][1:]) - else: - field_new.append(None) - cond_new.append(None) - if len(objs): - result += self._row_get(cr, uid, objs, field_new, cond_new, row, group_by) - else: - result.append(row) - return result - - - def create(self, cr, uid, ids, datas, context=None): - if not context: - context={} - self.pool = pooler.get_pool(cr.dbname) - report = self.pool.get('ir.report.custom').browse(cr, uid, [datas['report_id']])[0] - datas['model'] = report.model_id.model - if report.menu_id: - ids = self.pool.get(report.model_id.model).search(cr, uid, []) - datas['ids'] = ids - - service = netsvc.LocalService("object_proxy") - report_id = datas['report_id'] - report = service.execute(cr.dbname, uid, 'ir.report.custom', 'read', [report_id], context=context)[0] - fields = service.execute(cr.dbname, uid, 'ir.report.custom.fields', 'read', report['fields_child0'], context=context) - - fields.sort(lambda x,y : x['sequence'] - y['sequence']) - - if report['field_parent']: - parent_field = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [report['field_parent'][0]],['model']) - model_name = service.execute(cr.dbname, uid, 'ir.model', 'read', [report['model_id'][0]], ['model'],context=context)[0]['model'] - - fct = {} - fct['id'] = lambda x : x - fct['gety'] = lambda x: x.split('-')[0] - fct['in'] = lambda x: x.split(',') - new_fields = [] - new_cond = [] - for f in fields: - row = [] - cond = [] - for i in range(4): - if f['field_child'+str(i)]: - row.append(f['field_child'+str(i)][1]) - if f['fc'+str(i)+'_operande']: - fct_name = 'id' - cond_op = f['fc'+str(i)+'_op'] - if len(f['fc'+str(i)+'_op'].split(',')) == 2: - cond_op = f['fc'+str(i)+'_op'].split(',')[1] - fct_name = f['fc'+str(i)+'_op'].split(',')[0] - cond.append((fct[fct_name], f['fc'+str(i)+'_operande'][1], cond_op, f['fc'+str(i)+'_condition'])) - else: - cond.append(None) - new_fields.append(row) - new_cond.append(cond) - objs = self.pool.get(model_name).browse(cr, uid, ids) - - # Group by - groupby = None - idx = 0 - for f in fields: - if f['groupby']: - groupby = idx - idx += 1 - - - results = [] - if report['field_parent']: - level = [] - def build_tree(obj, level, depth): - res = self._row_get(cr, uid,[obj], new_fields, new_cond) - level.append(depth) - new_obj = eval('obj.'+report['field_parent'][1]) - if not isinstance(new_obj, list) : - new_obj = [new_obj] - for o in new_obj: - if not isinstance(o, browse_null): - res += build_tree(o, level, depth+1) - return res - - for obj in objs: - results += build_tree(obj, level, 0) - else: - results = self._row_get(cr, uid,objs, new_fields, new_cond, group_by=groupby) - - fct = { - 'calc_sum': lambda l: reduce(lambda x,y: float(x)+float(y), filter(None, l), 0), - 'calc_avg': lambda l: reduce(lambda x,y: float(x)+float(y), filter(None, l), 0) / (len(filter(None, l)) or 1.0), - 'calc_max': lambda l: reduce(lambda x,y: max(x,y), [(i or 0.0) for i in l], 0), - 'calc_min': lambda l: reduce(lambda x,y: min(x,y), [(i or 0.0) for i in l], 0), - 'calc_count': lambda l: len(filter(None, l)), - 'False': lambda l: '\r\n'.join(filter(None, l)), - 'groupby': lambda l: reduce(lambda x,y: x or y, l) - } - new_res = [] - - prev = None - if groupby != None: - res_dic = {} - for line in results: - if not line[groupby] and prev in res_dic: - res_dic[prev].append(line) - else: - prev = line[groupby] - if res_dic.has_key(line[groupby]): - res_dic[line[groupby]].append(line) - else: - res_dic[line[groupby]] = [] - res_dic[line[groupby]].append(line) - #we use the keys in results since they are ordered, whereas in res_dic.heys() they aren't - for key in filter(None, [x[groupby] for x in results]): - row = [] - for col in range(len(fields)): - if col == groupby: - row.append(fct['groupby'](map(lambda x: x[col], res_dic[key]))) - else: - row.append(fct[str(fields[col]['operation'])](map(lambda x: x[col], res_dic[key]))) - new_res.append(row) - results = new_res - - if report['type']=='table': - if report['field_parent']: - res = self._create_tree(uid, ids, report, fields, level, results, context) - else: - sort_idx = 0 - for idx in range(len(fields)): - if fields[idx]['name'] == report['sortby']: - sort_idx = idx - break - try : - results.sort(lambda x,y : cmp(float(x[sort_idx]),float(y[sort_idx]))) - except : - results.sort(lambda x,y : cmp(x[sort_idx],y[sort_idx])) - if report['limitt']: - results = results[:int(report['limitt'])] - res = self._create_table(uid, ids, report, fields, None, results, context) - elif report['type'] in ('pie','bar', 'line'): - results2 = [] - prev = False - for r in results: - row = [] - for j in range(len(r)): - if j == 0 and not r[j]: - row.append(prev) - elif j == 0 and r[j]: - prev = r[j] - row.append(r[j]) - else: - try: - row.append(float(r[j])) - except: - row.append(r[j]) - results2.append(row) - if report['type']=='pie': - res = self._create_pie(cr,uid, ids, report, fields, results2, context) - elif report['type']=='bar': - res = self._create_bars(cr,uid, ids, report, fields, results2, context) - elif report['type']=='line': - res = self._create_lines(cr,uid, ids, report, fields, results2, context) - return (self.obj.get(), 'pdf') - - def _create_tree(self, uid, ids, report, fields, level, results, context): - pageSize=common.pageSize.get(report['print_format'], [210.0,297.0]) - if report['print_orientation']=='landscape': - pageSize=[pageSize[1],pageSize[0]] - - impl = minidom.getDOMImplementation() - new_doc = impl.createDocument(None, "report", None) - - # build header - config = new_doc.createElement("config") - - def _append_node(name, text): - n = new_doc.createElement(name) - t = new_doc.createTextNode(text) - n.appendChild(t) - config.appendChild(n) - - _append_node('date', time.strftime('%d/%m/%Y')) - _append_node('PageFormat', '%s' % report['print_format']) - _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) - _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) - _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) - - length = pageSize[0]-30-reduce(lambda x,y:x+(y['width'] or 0), fields, 0) - count = 0 - for f in fields: - if not f['width']: count+=1 - for f in fields: - if not f['width']: - f['width']=round((float(length)/count)-0.5) - - _append_node('tableSize', '%s' % ','.join(map(lambda x: '%.2fmm' % (x['width'],), fields))) - _append_node('report-header', '%s' % (report['title'],)) - _append_node('report-footer', '%s' % (report['footer'],)) - - new_doc.childNodes[0].appendChild(config) - header = new_doc.createElement("header") - - for f in fields: - field = new_doc.createElement("field") - field_txt = new_doc.createTextNode('%s' % (f['name'],)) - field.appendChild(field_txt) - header.appendChild(field) - - new_doc.childNodes[0].appendChild(header) - - lines = new_doc.createElement("lines") - level.reverse() - for line in results: - shift = level.pop() - node_line = new_doc.createElement("row") - prefix = '+' - for f in range(len(fields)): - col = new_doc.createElement("col") - if f == 0: - col.setAttribute('para','yes') - col.setAttribute('tree','yes') - col.setAttribute('space',str(3*shift)+'mm') - if line[f] != None: - txt = new_doc.createTextNode(prefix+str(line[f]) or '') - else: - txt = new_doc.createTextNode('/') - col.appendChild(txt) - node_line.appendChild(col) - prefix = '' - lines.appendChild(node_line) - - new_doc.childNodes[0].appendChild(lines) - - styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl')) - style = libxslt.parseStylesheetDoc(styledoc) - doc = libxml2.parseDoc(new_doc.toxml()) - rml_obj = style.applyStylesheet(doc, None) - rml = style.saveResultToString(rml_obj) - - self.obj = render.rml(rml) - self.obj.render() - return True - - - def _create_lines(self, cr, uid, ids, report, fields, results, context): - service = netsvc.LocalService("object_proxy") - pdf_string = StringIO.StringIO() - can = canvas.init(fname=pdf_string, format='pdf') - - can.show(80,380,'/16/H'+report['title']) - - ar = area.T(size=(350,350), - #x_coord = category_coord.T(['2005-09-01','2005-10-22'],0), - x_axis = axis.X(label = fields[0]['name'], format="/a-30{}%s"), - y_axis = axis.Y(label = ', '.join(map(lambda x : x['name'], fields[1:])))) - - process_date = {} - process_date['D'] = lambda x : reduce(lambda xx,yy : xx+'-'+yy,x.split('-')[1:3]) - process_date['M'] = lambda x : x.split('-')[1] - process_date['Y'] = lambda x : x.split('-')[0] - - order_date = {} - order_date['D'] = lambda x : time.mktime((2005,int(x.split('-')[0]), int(x.split('-')[1]),0,0,0,0,0,0)) - order_date['M'] = lambda x : x - order_date['Y'] = lambda x : x - - abscissa = [] - tmp = {} - - idx = 0 - date_idx = None - fct = {} - for f in fields: - field_id = (f['field_child3'] and f['field_child3'][0]) or (f['field_child2'] and f['field_child2'][0]) or (f['field_child1'] and f['field_child1'][0]) or (f['field_child0'] and f['field_child0'][0]) - if field_id: - type = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [field_id],['ttype']) - if type[0]['ttype'] == 'date': - date_idx = idx - fct[idx] = process_date[report['frequency']] - else: - fct[idx] = lambda x : x - else: - fct[idx] = lambda x : x - idx+=1 - - # plots are usually displayed year by year - # so we do so if the first field is a date - data_by_year = {} - if date_idx != None: - for r in results: - key = process_date['Y'](r[date_idx]) - if not data_by_year.has_key(key): - data_by_year[key] = [] - for i in range(len(r)): - r[i] = fct[i](r[i]) - data_by_year[key].append(r) - else: - data_by_year[''] = results - - idx0 = 0 - nb_bar = len(data_by_year)*(len(fields)-1) - colors = map(lambda x:line_style.T(color=x), misc.choice_colors(nb_bar)) - abscissa = {} - for line in data_by_year.keys(): - fields_bar = [] - # sum data and save it in a list. An item for a fields - for d in data_by_year[line]: - for idx in range(len(fields)-1): - fields_bar.append({}) - if fields_bar[idx].has_key(d[0]): - fields_bar[idx][d[0]] += d[idx+1] - else: - fields_bar[idx][d[0]] = d[idx+1] - for idx in range(len(fields)-1): - data = {} - for k in fields_bar[idx].keys(): - if data.has_key(k): - data[k] += fields_bar[idx][k] - else: - data[k] = fields_bar[idx][k] - data_cum = [] - prev = 0.0 - keys = data.keys() - keys.sort() - # cumulate if necessary - for k in keys: - data_cum.append([k, float(data[k])+float(prev)]) - if fields[idx+1]['cumulate']: - prev += data[k] - idx0 = 0 - plot = line_plot.T(label=fields[idx+1]['name']+' '+str(line), data = data_cum, line_style=colors[idx0*(len(fields)-1)+idx]) - ar.add_plot(plot) - abscissa.update(fields_bar[idx]) - idx0 += 1 - - abscissa = map(lambda x : [x, None], abscissa) - ar.x_coord = category_coord.T(abscissa,0) - ar.draw(can) - - can.close() - self.obj = external_pdf(pdf_string.getvalue()) - self.obj.render() - pdf_string.close() - return True - - - - def _create_bars(self, cr, uid, ids, report, fields, results, context): - service = netsvc.LocalService("object_proxy") - pdf_string = StringIO.StringIO() - can = canvas.init(fname=pdf_string, format='pdf') - - can.show(80,380,'/16/H'+report['title']) - - process_date = {} - process_date['D'] = lambda x : reduce(lambda xx,yy : xx+'-'+yy,x.split('-')[1:3]) - process_date['M'] = lambda x : x.split('-')[1] - process_date['Y'] = lambda x : x.split('-')[0] - - order_date = {} - order_date['D'] = lambda x : time.mktime((2005,int(x.split('-')[0]), int(x.split('-')[1]),0,0,0,0,0,0)) - order_date['M'] = lambda x : x - order_date['Y'] = lambda x : x - - ar = area.T(size=(350,350), - x_axis = axis.X(label = fields[0]['name'], format="/a-30{}%s"), - y_axis = axis.Y(label = ', '.join(map(lambda x : x['name'], fields[1:])))) - - idx = 0 - date_idx = None - fct = {} - for f in fields: - field_id = (f['field_child3'] and f['field_child3'][0]) or (f['field_child2'] and f['field_child2'][0]) or (f['field_child1'] and f['field_child1'][0]) or (f['field_child0'] and f['field_child0'][0]) - if field_id: - type = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [field_id],['ttype']) - if type[0]['ttype'] == 'date': - date_idx = idx - fct[idx] = process_date[report['frequency']] - else: - fct[idx] = lambda x : x - else: - fct[idx] = lambda x : x - idx+=1 - - # plot are usually displayed year by year - # so we do so if the first field is a date - data_by_year = {} - if date_idx != None: - for r in results: - key = process_date['Y'](r[date_idx]) - if not data_by_year.has_key(key): - data_by_year[key] = [] - for i in range(len(r)): - r[i] = fct[i](r[i]) - data_by_year[key].append(r) - else: - data_by_year[''] = results - - - nb_bar = len(data_by_year)*(len(fields)-1) - colors = map(lambda x:fill_style.Plain(bgcolor=x), misc.choice_colors(nb_bar)) - - abscissa = {} - for line in data_by_year.keys(): - fields_bar = [] - # sum data and save it in a list. An item for a fields - for d in data_by_year[line]: - for idx in range(len(fields)-1): - fields_bar.append({}) - if fields_bar[idx].has_key(d[0]): - fields_bar[idx][d[0]] += d[idx+1] - else: - fields_bar[idx][d[0]] = d[idx+1] - for idx in range(len(fields)-1): - data = {} - for k in fields_bar[idx].keys(): - if data.has_key(k): - data[k] += fields_bar[idx][k] - else: - data[k] = fields_bar[idx][k] - data_cum = [] - prev = 0.0 - keys = data.keys() - keys.sort() - # cumulate if necessary - for k in keys: - data_cum.append([k, float(data[k])+float(prev)]) - if fields[idx+1]['cumulate']: - prev += data[k] - - idx0 = 0 - plot = bar_plot.T(label=fields[idx+1]['name']+' '+str(line), data = data_cum, cluster=(idx0*(len(fields)-1)+idx,nb_bar), fill_style=colors[idx0*(len(fields)-1)+idx]) - ar.add_plot(plot) - abscissa.update(fields_bar[idx]) - idx0 += 1 - abscissa = map(lambda x : [x, None], abscissa) - ar.x_coord = category_coord.T(abscissa,0) - ar.draw(can) - - can.close() - self.obj = external_pdf(pdf_string.getvalue()) - self.obj.render() - pdf_string.close() - return True - - def _create_pie(self, cr, uid, ids, report, fields, results, context): - pdf_string = StringIO.StringIO() - can = canvas.init(fname=pdf_string, format='pdf') - ar = area.T(size=(350,350), legend=legend.T(), - x_grid_style = None, y_grid_style = None) - colors = map(lambda x:fill_style.Plain(bgcolor=x), misc.choice_colors(len(results))) - - if reduce(lambda x,y : x+y, map(lambda x : x[1],results)) == 0.0: - raise(_('The sum of the data (2nd field) is null.\nWe can draw a pie chart !')) - - plot = pie_plot.T(data=results, arc_offsets=[0,10,0,10], - shadow = (2, -2, fill_style.gray50), - label_offset = 25, - arrow_style = arrow.a3, - fill_styles=colors) - ar.add_plot(plot) - ar.draw(can) - can.close() - self.obj = external_pdf(pdf_string.getvalue()) - self.obj.render() - pdf_string.close() - return True - - def _create_table(self, uid, ids, report, fields, tree, results, context): - pageSize=common.pageSize.get(report['print_format'], [210.0,297.0]) - if report['print_orientation']=='landscape': - pageSize=[pageSize[1],pageSize[0]] - - impl = minidom.getDOMImplementation() - new_doc = impl.createDocument(None, "report", None) - - # build header - config = new_doc.createElement("config") - - def _append_node(name, text): - n = new_doc.createElement(name) - t = new_doc.createTextNode(text) - n.appendChild(t) - config.appendChild(n) - - _append_node('date', time.strftime('%d/%m/%Y')) - _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) - _append_node('PageFormat', '%s' % report['print_format']) - _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) - _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) - - length = pageSize[0]-30-reduce(lambda x,y:x+(y['width'] or 0), fields, 0) - count = 0 - for f in fields: - if not f['width']: count+=1 - for f in fields: - if not f['width']: - f['width']=round((float(length)/count)-0.5) - - _append_node('tableSize', '%s' % ','.join(map(lambda x: '%.2fmm' % (x['width'],), fields))) - _append_node('report-header', '%s' % (report['title'],)) - _append_node('report-footer', '%s' % (report['footer'],)) - - new_doc.childNodes[0].appendChild(config) - header = new_doc.createElement("header") - - for f in fields: - field = new_doc.createElement("field") - field_txt = new_doc.createTextNode('%s' % (f['name'],)) - field.appendChild(field_txt) - header.appendChild(field) - - new_doc.childNodes[0].appendChild(header) - - lines = new_doc.createElement("lines") - for line in results: - node_line = new_doc.createElement("row") - for f in range(len(fields)): - col = new_doc.createElement("col") - col.setAttribute('tree','no') - if line[f] != None: - txt = new_doc.createTextNode(str(line[f] or '')) - else: - txt = new_doc.createTextNode('/') - col.appendChild(txt) - node_line.appendChild(col) - lines.appendChild(node_line) - - new_doc.childNodes[0].appendChild(lines) - -# file('/tmp/terp.xml','w+').write(new_doc.toxml()) - - styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl')) - style = libxslt.parseStylesheetDoc(styledoc) - doc = libxml2.parseDoc(new_doc.toxml()) - rml_obj = style.applyStylesheet(doc, None) - rml = style.saveResultToString(rml_obj) - - self.obj = render.rml(rml) - self.obj.render() - return True + def __init__(self, name): + report_int.__init__(self, name) + # + # PRE: + # fields = [['address','city'],['name'], ['zip']] + # conditions = [[('zip','==','3'),(,)],(,),(,)] #same structure as fields + # row_canvas = ['Rue', None, None] + # POST: + # [ ['ville','name','zip'] ] + # + def _row_get(self, cr, uid, objs, fields, conditions, row_canvas=None, group_by=None): + result = [] + tmp = [] + for obj in objs: + tobreak = False + for cond in conditions: + if cond and cond[0]: + c = cond[0] + temp = c[0](eval('obj.'+c[1])) + if not eval('\''+temp+'\''+' '+c[2]+' '+'\''+str(c[3])+'\''): + tobreak = True + if tobreak: + break + levels = {} + row = [] + for i in range(len(fields)): + if not fields[i]: + row.append(row_canvas and row_canvas[i]) + if row_canvas[i]: + row_canvas[i]=False + elif len(fields[i])==1: + if not isinstance(obj, browse_null): + row.append(str(eval('obj.'+fields[i][0]))) + else: + row.append(None) + else: + row.append(None) + levels[fields[i][0]]=True + if not levels: + result.append(row) + else: + # Process group_by data first + key = [] + if group_by != None and fields[group_by] != None: + if fields[group_by][0] in levels.keys(): + key.append(fields[group_by][0]) + for l in levels.keys(): + if l != fields[group_by][0]: + key.append(l) + else: + key = levels.keys() + for l in key: + objs = eval('obj.'+l) + if not isinstance(objs, browse_record_list) and type(objs) <> type([]): + objs = [objs] + field_new = [] + cond_new = [] + for f in range(len(fields)): + if (fields[f] and fields[f][0])==l: + field_new.append(fields[f][1:]) + cond_new.append(conditions[f][1:]) + else: + field_new.append(None) + cond_new.append(None) + if len(objs): + result += self._row_get(cr, uid, objs, field_new, cond_new, row, group_by) + else: + result.append(row) + return result + + + def create(self, cr, uid, ids, datas, context=None): + if not context: + context={} + self.pool = pooler.get_pool(cr.dbname) + report = self.pool.get('ir.report.custom').browse(cr, uid, [datas['report_id']])[0] + datas['model'] = report.model_id.model + if report.menu_id: + ids = self.pool.get(report.model_id.model).search(cr, uid, []) + datas['ids'] = ids + + service = netsvc.LocalService("object_proxy") + report_id = datas['report_id'] + report = service.execute(cr.dbname, uid, 'ir.report.custom', 'read', [report_id], context=context)[0] + fields = service.execute(cr.dbname, uid, 'ir.report.custom.fields', 'read', report['fields_child0'], context=context) + + fields.sort(lambda x,y : x['sequence'] - y['sequence']) + + if report['field_parent']: + parent_field = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [report['field_parent'][0]],['model']) + model_name = service.execute(cr.dbname, uid, 'ir.model', 'read', [report['model_id'][0]], ['model'],context=context)[0]['model'] + + fct = {} + fct['id'] = lambda x : x + fct['gety'] = lambda x: x.split('-')[0] + fct['in'] = lambda x: x.split(',') + new_fields = [] + new_cond = [] + for f in fields: + row = [] + cond = [] + for i in range(4): + if f['field_child'+str(i)]: + row.append(f['field_child'+str(i)][1]) + if f['fc'+str(i)+'_operande']: + fct_name = 'id' + cond_op = f['fc'+str(i)+'_op'] + if len(f['fc'+str(i)+'_op'].split(',')) == 2: + cond_op = f['fc'+str(i)+'_op'].split(',')[1] + fct_name = f['fc'+str(i)+'_op'].split(',')[0] + cond.append((fct[fct_name], f['fc'+str(i)+'_operande'][1], cond_op, f['fc'+str(i)+'_condition'])) + else: + cond.append(None) + new_fields.append(row) + new_cond.append(cond) + objs = self.pool.get(model_name).browse(cr, uid, ids) + + # Group by + groupby = None + idx = 0 + for f in fields: + if f['groupby']: + groupby = idx + idx += 1 + + + results = [] + if report['field_parent']: + level = [] + def build_tree(obj, level, depth): + res = self._row_get(cr, uid,[obj], new_fields, new_cond) + level.append(depth) + new_obj = eval('obj.'+report['field_parent'][1]) + if not isinstance(new_obj, list) : + new_obj = [new_obj] + for o in new_obj: + if not isinstance(o, browse_null): + res += build_tree(o, level, depth+1) + return res + + for obj in objs: + results += build_tree(obj, level, 0) + else: + results = self._row_get(cr, uid,objs, new_fields, new_cond, group_by=groupby) + + fct = { + 'calc_sum': lambda l: reduce(lambda x,y: float(x)+float(y), filter(None, l), 0), + 'calc_avg': lambda l: reduce(lambda x,y: float(x)+float(y), filter(None, l), 0) / (len(filter(None, l)) or 1.0), + 'calc_max': lambda l: reduce(lambda x,y: max(x,y), [(i or 0.0) for i in l], 0), + 'calc_min': lambda l: reduce(lambda x,y: min(x,y), [(i or 0.0) for i in l], 0), + 'calc_count': lambda l: len(filter(None, l)), + 'False': lambda l: '\r\n'.join(filter(None, l)), + 'groupby': lambda l: reduce(lambda x,y: x or y, l) + } + new_res = [] + + prev = None + if groupby != None: + res_dic = {} + for line in results: + if not line[groupby] and prev in res_dic: + res_dic[prev].append(line) + else: + prev = line[groupby] + if res_dic.has_key(line[groupby]): + res_dic[line[groupby]].append(line) + else: + res_dic[line[groupby]] = [] + res_dic[line[groupby]].append(line) + #we use the keys in results since they are ordered, whereas in res_dic.heys() they aren't + for key in filter(None, [x[groupby] for x in results]): + row = [] + for col in range(len(fields)): + if col == groupby: + row.append(fct['groupby'](map(lambda x: x[col], res_dic[key]))) + else: + row.append(fct[str(fields[col]['operation'])](map(lambda x: x[col], res_dic[key]))) + new_res.append(row) + results = new_res + + if report['type']=='table': + if report['field_parent']: + res = self._create_tree(uid, ids, report, fields, level, results, context) + else: + sort_idx = 0 + for idx in range(len(fields)): + if fields[idx]['name'] == report['sortby']: + sort_idx = idx + break + try : + results.sort(lambda x,y : cmp(float(x[sort_idx]),float(y[sort_idx]))) + except : + results.sort(lambda x,y : cmp(x[sort_idx],y[sort_idx])) + if report['limitt']: + results = results[:int(report['limitt'])] + res = self._create_table(uid, ids, report, fields, None, results, context) + elif report['type'] in ('pie','bar', 'line'): + results2 = [] + prev = False + for r in results: + row = [] + for j in range(len(r)): + if j == 0 and not r[j]: + row.append(prev) + elif j == 0 and r[j]: + prev = r[j] + row.append(r[j]) + else: + try: + row.append(float(r[j])) + except: + row.append(r[j]) + results2.append(row) + if report['type']=='pie': + res = self._create_pie(cr,uid, ids, report, fields, results2, context) + elif report['type']=='bar': + res = self._create_bars(cr,uid, ids, report, fields, results2, context) + elif report['type']=='line': + res = self._create_lines(cr,uid, ids, report, fields, results2, context) + return (self.obj.get(), 'pdf') + + def _create_tree(self, uid, ids, report, fields, level, results, context): + pageSize=common.pageSize.get(report['print_format'], [210.0,297.0]) + if report['print_orientation']=='landscape': + pageSize=[pageSize[1],pageSize[0]] + + impl = minidom.getDOMImplementation() + new_doc = impl.createDocument(None, "report", None) + + # build header + config = new_doc.createElement("config") + + def _append_node(name, text): + n = new_doc.createElement(name) + t = new_doc.createTextNode(text) + n.appendChild(t) + config.appendChild(n) + + _append_node('date', time.strftime('%d/%m/%Y')) + _append_node('PageFormat', '%s' % report['print_format']) + _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) + _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) + _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) + + length = pageSize[0]-30-reduce(lambda x,y:x+(y['width'] or 0), fields, 0) + count = 0 + for f in fields: + if not f['width']: count+=1 + for f in fields: + if not f['width']: + f['width']=round((float(length)/count)-0.5) + + _append_node('tableSize', '%s' % ','.join(map(lambda x: '%.2fmm' % (x['width'],), fields))) + _append_node('report-header', '%s' % (report['title'],)) + _append_node('report-footer', '%s' % (report['footer'],)) + + new_doc.childNodes[0].appendChild(config) + header = new_doc.createElement("header") + + for f in fields: + field = new_doc.createElement("field") + field_txt = new_doc.createTextNode('%s' % (f['name'],)) + field.appendChild(field_txt) + header.appendChild(field) + + new_doc.childNodes[0].appendChild(header) + + lines = new_doc.createElement("lines") + level.reverse() + for line in results: + shift = level.pop() + node_line = new_doc.createElement("row") + prefix = '+' + for f in range(len(fields)): + col = new_doc.createElement("col") + if f == 0: + col.setAttribute('para','yes') + col.setAttribute('tree','yes') + col.setAttribute('space',str(3*shift)+'mm') + if line[f] != None: + txt = new_doc.createTextNode(prefix+str(line[f]) or '') + else: + txt = new_doc.createTextNode('/') + col.appendChild(txt) + node_line.appendChild(col) + prefix = '' + lines.appendChild(node_line) + + new_doc.childNodes[0].appendChild(lines) + + styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl')) + style = libxslt.parseStylesheetDoc(styledoc) + doc = libxml2.parseDoc(new_doc.toxml()) + rml_obj = style.applyStylesheet(doc, None) + rml = style.saveResultToString(rml_obj) + + self.obj = render.rml(rml) + self.obj.render() + return True + + + def _create_lines(self, cr, uid, ids, report, fields, results, context): + service = netsvc.LocalService("object_proxy") + pdf_string = StringIO.StringIO() + can = canvas.init(fname=pdf_string, format='pdf') + + can.show(80,380,'/16/H'+report['title']) + + ar = area.T(size=(350,350), + #x_coord = category_coord.T(['2005-09-01','2005-10-22'],0), + x_axis = axis.X(label = fields[0]['name'], format="/a-30{}%s"), + y_axis = axis.Y(label = ', '.join(map(lambda x : x['name'], fields[1:])))) + + process_date = {} + process_date['D'] = lambda x : reduce(lambda xx,yy : xx+'-'+yy,x.split('-')[1:3]) + process_date['M'] = lambda x : x.split('-')[1] + process_date['Y'] = lambda x : x.split('-')[0] + + order_date = {} + order_date['D'] = lambda x : time.mktime((2005,int(x.split('-')[0]), int(x.split('-')[1]),0,0,0,0,0,0)) + order_date['M'] = lambda x : x + order_date['Y'] = lambda x : x + + abscissa = [] + tmp = {} + + idx = 0 + date_idx = None + fct = {} + for f in fields: + field_id = (f['field_child3'] and f['field_child3'][0]) or (f['field_child2'] and f['field_child2'][0]) or (f['field_child1'] and f['field_child1'][0]) or (f['field_child0'] and f['field_child0'][0]) + if field_id: + type = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [field_id],['ttype']) + if type[0]['ttype'] == 'date': + date_idx = idx + fct[idx] = process_date[report['frequency']] + else: + fct[idx] = lambda x : x + else: + fct[idx] = lambda x : x + idx+=1 + + # plots are usually displayed year by year + # so we do so if the first field is a date + data_by_year = {} + if date_idx != None: + for r in results: + key = process_date['Y'](r[date_idx]) + if not data_by_year.has_key(key): + data_by_year[key] = [] + for i in range(len(r)): + r[i] = fct[i](r[i]) + data_by_year[key].append(r) + else: + data_by_year[''] = results + + idx0 = 0 + nb_bar = len(data_by_year)*(len(fields)-1) + colors = map(lambda x:line_style.T(color=x), misc.choice_colors(nb_bar)) + abscissa = {} + for line in data_by_year.keys(): + fields_bar = [] + # sum data and save it in a list. An item for a fields + for d in data_by_year[line]: + for idx in range(len(fields)-1): + fields_bar.append({}) + if fields_bar[idx].has_key(d[0]): + fields_bar[idx][d[0]] += d[idx+1] + else: + fields_bar[idx][d[0]] = d[idx+1] + for idx in range(len(fields)-1): + data = {} + for k in fields_bar[idx].keys(): + if data.has_key(k): + data[k] += fields_bar[idx][k] + else: + data[k] = fields_bar[idx][k] + data_cum = [] + prev = 0.0 + keys = data.keys() + keys.sort() + # cumulate if necessary + for k in keys: + data_cum.append([k, float(data[k])+float(prev)]) + if fields[idx+1]['cumulate']: + prev += data[k] + idx0 = 0 + plot = line_plot.T(label=fields[idx+1]['name']+' '+str(line), data = data_cum, line_style=colors[idx0*(len(fields)-1)+idx]) + ar.add_plot(plot) + abscissa.update(fields_bar[idx]) + idx0 += 1 + + abscissa = map(lambda x : [x, None], abscissa) + ar.x_coord = category_coord.T(abscissa,0) + ar.draw(can) + + can.close() + self.obj = external_pdf(pdf_string.getvalue()) + self.obj.render() + pdf_string.close() + return True + + + + def _create_bars(self, cr, uid, ids, report, fields, results, context): + service = netsvc.LocalService("object_proxy") + pdf_string = StringIO.StringIO() + can = canvas.init(fname=pdf_string, format='pdf') + + can.show(80,380,'/16/H'+report['title']) + + process_date = {} + process_date['D'] = lambda x : reduce(lambda xx,yy : xx+'-'+yy,x.split('-')[1:3]) + process_date['M'] = lambda x : x.split('-')[1] + process_date['Y'] = lambda x : x.split('-')[0] + + order_date = {} + order_date['D'] = lambda x : time.mktime((2005,int(x.split('-')[0]), int(x.split('-')[1]),0,0,0,0,0,0)) + order_date['M'] = lambda x : x + order_date['Y'] = lambda x : x + + ar = area.T(size=(350,350), + x_axis = axis.X(label = fields[0]['name'], format="/a-30{}%s"), + y_axis = axis.Y(label = ', '.join(map(lambda x : x['name'], fields[1:])))) + + idx = 0 + date_idx = None + fct = {} + for f in fields: + field_id = (f['field_child3'] and f['field_child3'][0]) or (f['field_child2'] and f['field_child2'][0]) or (f['field_child1'] and f['field_child1'][0]) or (f['field_child0'] and f['field_child0'][0]) + if field_id: + type = service.execute(cr.dbname, uid, 'ir.model.fields', 'read', [field_id],['ttype']) + if type[0]['ttype'] == 'date': + date_idx = idx + fct[idx] = process_date[report['frequency']] + else: + fct[idx] = lambda x : x + else: + fct[idx] = lambda x : x + idx+=1 + + # plot are usually displayed year by year + # so we do so if the first field is a date + data_by_year = {} + if date_idx != None: + for r in results: + key = process_date['Y'](r[date_idx]) + if not data_by_year.has_key(key): + data_by_year[key] = [] + for i in range(len(r)): + r[i] = fct[i](r[i]) + data_by_year[key].append(r) + else: + data_by_year[''] = results + + + nb_bar = len(data_by_year)*(len(fields)-1) + colors = map(lambda x:fill_style.Plain(bgcolor=x), misc.choice_colors(nb_bar)) + + abscissa = {} + for line in data_by_year.keys(): + fields_bar = [] + # sum data and save it in a list. An item for a fields + for d in data_by_year[line]: + for idx in range(len(fields)-1): + fields_bar.append({}) + if fields_bar[idx].has_key(d[0]): + fields_bar[idx][d[0]] += d[idx+1] + else: + fields_bar[idx][d[0]] = d[idx+1] + for idx in range(len(fields)-1): + data = {} + for k in fields_bar[idx].keys(): + if data.has_key(k): + data[k] += fields_bar[idx][k] + else: + data[k] = fields_bar[idx][k] + data_cum = [] + prev = 0.0 + keys = data.keys() + keys.sort() + # cumulate if necessary + for k in keys: + data_cum.append([k, float(data[k])+float(prev)]) + if fields[idx+1]['cumulate']: + prev += data[k] + + idx0 = 0 + plot = bar_plot.T(label=fields[idx+1]['name']+' '+str(line), data = data_cum, cluster=(idx0*(len(fields)-1)+idx,nb_bar), fill_style=colors[idx0*(len(fields)-1)+idx]) + ar.add_plot(plot) + abscissa.update(fields_bar[idx]) + idx0 += 1 + abscissa = map(lambda x : [x, None], abscissa) + ar.x_coord = category_coord.T(abscissa,0) + ar.draw(can) + + can.close() + self.obj = external_pdf(pdf_string.getvalue()) + self.obj.render() + pdf_string.close() + return True + + def _create_pie(self, cr, uid, ids, report, fields, results, context): + pdf_string = StringIO.StringIO() + can = canvas.init(fname=pdf_string, format='pdf') + ar = area.T(size=(350,350), legend=legend.T(), + x_grid_style = None, y_grid_style = None) + colors = map(lambda x:fill_style.Plain(bgcolor=x), misc.choice_colors(len(results))) + + if reduce(lambda x,y : x+y, map(lambda x : x[1],results)) == 0.0: + raise(_('The sum of the data (2nd field) is null.\nWe can draw a pie chart !')) + + plot = pie_plot.T(data=results, arc_offsets=[0,10,0,10], + shadow = (2, -2, fill_style.gray50), + label_offset = 25, + arrow_style = arrow.a3, + fill_styles=colors) + ar.add_plot(plot) + ar.draw(can) + can.close() + self.obj = external_pdf(pdf_string.getvalue()) + self.obj.render() + pdf_string.close() + return True + + def _create_table(self, uid, ids, report, fields, tree, results, context): + pageSize=common.pageSize.get(report['print_format'], [210.0,297.0]) + if report['print_orientation']=='landscape': + pageSize=[pageSize[1],pageSize[0]] + + impl = minidom.getDOMImplementation() + new_doc = impl.createDocument(None, "report", None) + + # build header + config = new_doc.createElement("config") + + def _append_node(name, text): + n = new_doc.createElement(name) + t = new_doc.createTextNode(text) + n.appendChild(t) + config.appendChild(n) + + _append_node('date', time.strftime('%d/%m/%Y')) + _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) + _append_node('PageFormat', '%s' % report['print_format']) + _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) + _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) + + length = pageSize[0]-30-reduce(lambda x,y:x+(y['width'] or 0), fields, 0) + count = 0 + for f in fields: + if not f['width']: count+=1 + for f in fields: + if not f['width']: + f['width']=round((float(length)/count)-0.5) + + _append_node('tableSize', '%s' % ','.join(map(lambda x: '%.2fmm' % (x['width'],), fields))) + _append_node('report-header', '%s' % (report['title'],)) + _append_node('report-footer', '%s' % (report['footer'],)) + + new_doc.childNodes[0].appendChild(config) + header = new_doc.createElement("header") + + for f in fields: + field = new_doc.createElement("field") + field_txt = new_doc.createTextNode('%s' % (f['name'],)) + field.appendChild(field_txt) + header.appendChild(field) + + new_doc.childNodes[0].appendChild(header) + + lines = new_doc.createElement("lines") + for line in results: + node_line = new_doc.createElement("row") + for f in range(len(fields)): + col = new_doc.createElement("col") + col.setAttribute('tree','no') + if line[f] != None: + txt = new_doc.createTextNode(str(line[f] or '')) + else: + txt = new_doc.createTextNode('/') + col.appendChild(txt) + node_line.appendChild(col) + lines.appendChild(node_line) + + new_doc.childNodes[0].appendChild(lines) + +# file('/tmp/terp.xml','w+').write(new_doc.toxml()) + + styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl')) + style = libxslt.parseStylesheetDoc(styledoc) + doc = libxml2.parseDoc(new_doc.toxml()) + rml_obj = style.applyStylesheet(doc, None) + rml = style.saveResultToString(rml_obj) + + self.obj = render.rml(rml) + self.obj.render() + return True report_custom('report.custom') diff --git a/bin/report/int_to_text.py b/bin/report/int_to_text.py index 552922f0973..e867ed6085d 100644 --- a/bin/report/int_to_text.py +++ b/bin/report/int_to_text.py @@ -28,55 +28,55 @@ ############################################################################## unites = { - 0: '', 1:'un', 2:'deux', 3:'trois', 4:'quatre', 5:'cinq', 6:'six', 7:'sept', 8:'huit', 9:'neuf', - 10:'dix', 11:'onze', 12:'douze', 13:'treize', 14:'quatorze', 15:'quinze', 16:'seize', - 21:'vingt et un', 31:'trente et un', 41:'quarante et un', 51:'cinquante et un', 61:'soixante et un', - 71:'septante et un', 91:'nonante et un', 80:'quatre-vingts' + 0: '', 1:'un', 2:'deux', 3:'trois', 4:'quatre', 5:'cinq', 6:'six', 7:'sept', 8:'huit', 9:'neuf', + 10:'dix', 11:'onze', 12:'douze', 13:'treize', 14:'quatorze', 15:'quinze', 16:'seize', + 21:'vingt et un', 31:'trente et un', 41:'quarante et un', 51:'cinquante et un', 61:'soixante et un', + 71:'septante et un', 91:'nonante et un', 80:'quatre-vingts' } dizaine = { - 1: 'dix', 2:'vingt', 3:'trente',4:'quarante', 5:'cinquante', 6:'soixante', 7:'septante', 8:'quatre-vingt', 9:'nonante' + 1: 'dix', 2:'vingt', 3:'trente',4:'quarante', 5:'cinquante', 6:'soixante', 7:'septante', 8:'quatre-vingt', 9:'nonante' } centaine = { - 0:'', 1: 'cent', 2:'deux cent', 3:'trois cent',4:'quatre cent', 5:'cinq cent', 6:'six cent', 7:'sept cent', 8:'huit cent', 9:'neuf cent' + 0:'', 1: 'cent', 2:'deux cent', 3:'trois cent',4:'quatre cent', 5:'cinq cent', 6:'six cent', 7:'sept cent', 8:'huit cent', 9:'neuf cent' } mille = { - 0:'', 1:'mille' + 0:'', 1:'mille' } def _100_to_text(chiffre): - if chiffre in unites: - return unites[chiffre] - else: - if chiffre%10>0: - return dizaine[chiffre / 10]+'-'+unites[chiffre % 10] - else: - return dizaine[chiffre / 10] + if chiffre in unites: + return unites[chiffre] + else: + if chiffre%10>0: + return dizaine[chiffre / 10]+'-'+unites[chiffre % 10] + else: + return dizaine[chiffre / 10] def _1000_to_text(chiffre): - d = _100_to_text(chiffre % 100) - d2 = chiffre/100 - if d2>0 and d: - return centaine[d2]+' '+d - elif d2>1 and not(d): - return centaine[d2]+'s' - else: - return centaine[d2] or d + d = _100_to_text(chiffre % 100) + d2 = chiffre/100 + if d2>0 and d: + return centaine[d2]+' '+d + elif d2>1 and not(d): + return centaine[d2]+'s' + else: + return centaine[d2] or d def _10000_to_text(chiffre): - if chiffre==0: - return 'zero' - part1 = _1000_to_text(chiffre % 1000) - part2 = mille.get(chiffre / 1000, _1000_to_text(chiffre / 1000)+' mille') - if part2 and part1: - part1 = ' '+part1 - return part2+part1 + if chiffre==0: + return 'zero' + part1 = _1000_to_text(chiffre % 1000) + part2 = mille.get(chiffre / 1000, _1000_to_text(chiffre / 1000)+' mille') + if part2 and part1: + part1 = ' '+part1 + return part2+part1 def int_to_text(i): - return _10000_to_text(i) + return _10000_to_text(i) if __name__=='__main__': - for i in range(1,999999,139): - print int_to_text(i) + for i in range(1,999999,139): + print int_to_text(i) diff --git a/bin/report/interface.py b/bin/report/interface.py index 63757ad6151..0ed5b73a07c 100644 --- a/bin/report/interface.py +++ b/bin/report/interface.py @@ -46,187 +46,187 @@ import urllib # encode a value to a string in utf8 and converts XML entities # def toxml(val): - if isinstance(val, str): - str_utf8 = val - elif isinstance(val, unicode): - str_utf8 = val.encode('utf-8') - else: - str_utf8 = str(val) - return str_utf8.replace('&', '&').replace('<','<').replace('>','>') + if isinstance(val, str): + str_utf8 = val + elif isinstance(val, unicode): + str_utf8 = val.encode('utf-8') + else: + str_utf8 = str(val) + return str_utf8.replace('&', '&').replace('<','<').replace('>','>') class report_int(netsvc.Service): - def __init__(self, name, audience='*'): - assert not netsvc.service_exist(name), 'The report "%s" already exist!' % name - super(report_int, self).__init__(name, audience) - if name[0:7]<>'report.': - raise Exception, 'ConceptionError, bad report name, should start with "report."' - self.name = name - self.id = 0 - self.name2 = '.'.join(name.split('.')[1:]) - self.joinGroup('report') - self.exportMethod(self.create) - - def create(self, cr, uid, ids, datas, context=None): - return False + def __init__(self, name, audience='*'): + assert not netsvc.service_exist(name), 'The report "%s" already exist!' % name + super(report_int, self).__init__(name, audience) + if name[0:7]<>'report.': + raise Exception, 'ConceptionError, bad report name, should start with "report."' + self.name = name + self.id = 0 + self.name2 = '.'.join(name.split('.')[1:]) + self.joinGroup('report') + self.exportMethod(self.create) + + def create(self, cr, uid, ids, datas, context=None): + return False """ - Class to automatically build a document using the transformation process: - XML -> DATAS -> RML -> PDF - -> HTML - using a XSL:RML transformation + Class to automatically build a document using the transformation process: + XML -> DATAS -> RML -> PDF + -> HTML + using a XSL:RML transformation """ class report_rml(report_int): - def __init__(self, name, table, tmpl, xsl): - super(report_rml, self).__init__(name) - self.table = table - self.tmpl = tmpl - self.xsl = xsl - self.bin_datas = {} - self.generators = { - 'pdf': self.create_pdf, - 'html': self.create_html, - 'raw': self.create_raw, - 'sxw': self.create_sxw, - } - - def create(self, cr, uid, ids, datas, context): - xml = self.create_xml(cr, uid, ids, datas, context) -# file('/tmp/terp.xml','wb+').write(xml) - if datas.get('report_type', 'pdf') == 'raw': - return xml - rml = self.create_rml(cr, xml, uid, context) -# file('/tmp/terp.rml','wb+').write(rml) - report_type = datas.get('report_type', 'pdf') - create_doc = self.generators[report_type] - pdf = create_doc(rml) - return (pdf, report_type) - - def create_xml(self, cr, uid, ids, datas, context=None): - if not context: - context={} - doc = print_xml.document(cr, uid, datas, {}) - self.bin_datas.update( doc.bin_datas or {}) - doc.parse(self.tmpl, ids, self.table, context) - xml = doc.xml_get() - doc.close() - return self.post_process_xml_data(cr, uid, xml, context) - - def post_process_xml_data(self, cr, uid, xml, context=None): - if not context: - context={} - # find the position of the 3rd tag - # (skip the <?xml ...?> and the "root" tag) - iter = re.finditer('<[^>]*>', xml) - i = iter.next() - i = iter.next() - pos_xml = i.end() - - doc = print_xml.document(cr, uid, {}, {}) - tmpl_path = addons.get_module_resource('custom', 'corporate_defaults.xml') - doc.parse(tmpl_path, [uid], 'res.users', context) - corporate_header = doc.xml_get() - doc.close() - - # find the position of the tag after the <?xml ...?> tag - iter = re.finditer('<[^>]*>', corporate_header) - i = iter.next() - pos_header = i.end() - - return xml[:pos_xml] + corporate_header[pos_header:] + xml[pos_xml:] - - # - # TODO: The translation doesn't work for "<tag t="1">textext<tag> tex</tag>text</tag>" - # - def create_rml(self, cr, xml, uid, context=None): - if not context: - context={} - service = netsvc.LocalService("object_proxy") - - # In some case we might not use xsl ... - if not self.xsl: - return xml - - # load XSL (parse it to the XML level) - styledoc = libxml2.parseDoc(tools.file_open(self.xsl).read()) - xsl_path, tail = os.path.split(self.xsl) - for child in styledoc.children: - if child.name == 'import': - if child.hasProp('href'): - imp_file = child.prop('href') - _x, imp_file = tools.file_open(imp_file, subdir=xsl_path, pathinfo=True) - child.setProp('href', urllib.quote(str(imp_file))) - - #TODO: get all the translation in one query. That means we have to: - # * build a list of items to translate, - # * issue the query to translate them, - # * (re)build/update the stylesheet with the translated items - - # translate the XSL stylesheet - def look_down(child, lang): - while child is not None: - if (child.type == "element") and child.hasProp('t'): - #FIXME: use cursor - res = service.execute(cr.dbname, uid, 'ir.translation', - '_get_source', self.name2, 'xsl', lang, child.content) - if res: - child.setContent(res) - look_down(child.children, lang) - child = child.next - - if context.get('lang', False): - look_down(styledoc.children, context['lang']) - - # parse XSL - style = libxslt.parseStylesheetDoc(styledoc) - # load XML (data) - doc = libxml2.parseMemory(xml,len(xml)) - # create RML (apply XSL to XML data) - result = style.applyStylesheet(doc, None) - # save result to string - xml = style.saveResultToString(result) - - style.freeStylesheet() - doc.freeDoc() - result.freeDoc() - return xml - - def create_pdf(self, xml, logo=None): - if logo: - self.bin_datas['logo'] = logo - else: - if 'logo' in self.bin_datas: - del self.bin_datas['logo'] - obj = render.rml(xml, self.bin_datas, tools.config['root_path']) - obj.render() - return obj.get() - - def create_html(self, xml, logo=None): - obj = render.rml2html(xml, self.bin_datas) - obj.render() - return obj.get() - - def create_raw(self, xml, logo=None): - return xml - - def create_sxw(self, path, logo=None): - return path + def __init__(self, name, table, tmpl, xsl): + super(report_rml, self).__init__(name) + self.table = table + self.tmpl = tmpl + self.xsl = xsl + self.bin_datas = {} + self.generators = { + 'pdf': self.create_pdf, + 'html': self.create_html, + 'raw': self.create_raw, + 'sxw': self.create_sxw, + } + + def create(self, cr, uid, ids, datas, context): + xml = self.create_xml(cr, uid, ids, datas, context) +# file('/tmp/terp.xml','wb+').write(xml) + if datas.get('report_type', 'pdf') == 'raw': + return xml + rml = self.create_rml(cr, xml, uid, context) +# file('/tmp/terp.rml','wb+').write(rml) + report_type = datas.get('report_type', 'pdf') + create_doc = self.generators[report_type] + pdf = create_doc(rml) + return (pdf, report_type) + + def create_xml(self, cr, uid, ids, datas, context=None): + if not context: + context={} + doc = print_xml.document(cr, uid, datas, {}) + self.bin_datas.update( doc.bin_datas or {}) + doc.parse(self.tmpl, ids, self.table, context) + xml = doc.xml_get() + doc.close() + return self.post_process_xml_data(cr, uid, xml, context) + + def post_process_xml_data(self, cr, uid, xml, context=None): + if not context: + context={} + # find the position of the 3rd tag + # (skip the <?xml ...?> and the "root" tag) + iter = re.finditer('<[^>]*>', xml) + i = iter.next() + i = iter.next() + pos_xml = i.end() + + doc = print_xml.document(cr, uid, {}, {}) + tmpl_path = addons.get_module_resource('custom', 'corporate_defaults.xml') + doc.parse(tmpl_path, [uid], 'res.users', context) + corporate_header = doc.xml_get() + doc.close() + + # find the position of the tag after the <?xml ...?> tag + iter = re.finditer('<[^>]*>', corporate_header) + i = iter.next() + pos_header = i.end() + + return xml[:pos_xml] + corporate_header[pos_header:] + xml[pos_xml:] + + # + # TODO: The translation doesn't work for "<tag t="1">textext<tag> tex</tag>text</tag>" + # + def create_rml(self, cr, xml, uid, context=None): + if not context: + context={} + service = netsvc.LocalService("object_proxy") + + # In some case we might not use xsl ... + if not self.xsl: + return xml + + # load XSL (parse it to the XML level) + styledoc = libxml2.parseDoc(tools.file_open(self.xsl).read()) + xsl_path, tail = os.path.split(self.xsl) + for child in styledoc.children: + if child.name == 'import': + if child.hasProp('href'): + imp_file = child.prop('href') + _x, imp_file = tools.file_open(imp_file, subdir=xsl_path, pathinfo=True) + child.setProp('href', urllib.quote(str(imp_file))) + + #TODO: get all the translation in one query. That means we have to: + # * build a list of items to translate, + # * issue the query to translate them, + # * (re)build/update the stylesheet with the translated items + + # translate the XSL stylesheet + def look_down(child, lang): + while child is not None: + if (child.type == "element") and child.hasProp('t'): + #FIXME: use cursor + res = service.execute(cr.dbname, uid, 'ir.translation', + '_get_source', self.name2, 'xsl', lang, child.content) + if res: + child.setContent(res) + look_down(child.children, lang) + child = child.next + + if context.get('lang', False): + look_down(styledoc.children, context['lang']) + + # parse XSL + style = libxslt.parseStylesheetDoc(styledoc) + # load XML (data) + doc = libxml2.parseMemory(xml,len(xml)) + # create RML (apply XSL to XML data) + result = style.applyStylesheet(doc, None) + # save result to string + xml = style.saveResultToString(result) + + style.freeStylesheet() + doc.freeDoc() + result.freeDoc() + return xml + + def create_pdf(self, xml, logo=None): + if logo: + self.bin_datas['logo'] = logo + else: + if 'logo' in self.bin_datas: + del self.bin_datas['logo'] + obj = render.rml(xml, self.bin_datas, tools.config['root_path']) + obj.render() + return obj.get() + + def create_html(self, xml, logo=None): + obj = render.rml2html(xml, self.bin_datas) + obj.render() + return obj.get() + + def create_raw(self, xml, logo=None): + return xml + + def create_sxw(self, path, logo=None): + return path from report_sxw import report_sxw def register_all(db): - opj = os.path.join - cr = db.cursor() - cr.execute("SELECT * FROM ir_act_report_xml WHERE auto ORDER BY id") - result = cr.dictfetchall() - cr.close() - for r in result: - if netsvc.service_exist('report.'+r['report_name']): - continue - if r['report_rml'] or r['report_rml_content_data']: - report_sxw('report.'+r['report_name'], r['model'], - opj('addons',r['report_rml'] or '/'), header=r['header']) - if r['report_xsl']: - report_rml('report.'+r['report_name'], r['model'], - opj('addons',r['report_xml']), - r['report_xsl'] and opj('addons',r['report_xsl'])) + opj = os.path.join + cr = db.cursor() + cr.execute("SELECT * FROM ir_act_report_xml WHERE auto ORDER BY id") + result = cr.dictfetchall() + cr.close() + for r in result: + if netsvc.service_exist('report.'+r['report_name']): + continue + if r['report_rml'] or r['report_rml_content_data']: + report_sxw('report.'+r['report_name'], r['model'], + opj('addons',r['report_rml'] or '/'), header=r['header']) + if r['report_xsl']: + report_rml('report.'+r['report_name'], r['model'], + opj('addons',r['report_xml']), + r['report_xsl'] and opj('addons',r['report_xsl'])) diff --git a/bin/report/misc.py b/bin/report/misc.py index 03df9d98c1b..a5926d65e28 100644 --- a/bin/report/misc.py +++ b/bin/report/misc.py @@ -28,14 +28,14 @@ from pychart import * colorline = [color.T(r=((r+3) % 11)/10.0, - g=((g+6) % 11)/10.0, - b=((b+9) % 11)/10.0) - for r in range(11) for g in range(11) for b in range(11)] + g=((g+6) % 11)/10.0, + b=((b+9) % 11)/10.0) + for r in range(11) for g in range(11) for b in range(11)] def choice_colors(n): - if n: - return colorline[0:-1:len(colorline)/n] - return [] + if n: + return colorline[0:-1:len(colorline)/n] + return [] if __name__=='__main__': - print choice_colors(10) + print choice_colors(10) diff --git a/bin/report/print_fnc.py b/bin/report/print_fnc.py index ae9a0c63b45..081222b758d 100644 --- a/bin/report/print_fnc.py +++ b/bin/report/print_fnc.py @@ -30,13 +30,13 @@ import time functions = { - 'today': lambda x: time.strftime('%d/%m/%Y', time.localtime()).decode('latin1') + 'today': lambda x: time.strftime('%d/%m/%Y', time.localtime()).decode('latin1') } # # TODO: call an object internal function too # def print_fnc(fnc, arg): - if fnc in functions: - return functions[fnc](arg) - return '' + if fnc in functions: + return functions[fnc](arg) + return '' diff --git a/bin/report/print_xml.py b/bin/report/print_xml.py index 75c700e3927..3be8b5419c4 100644 --- a/bin/report/print_xml.py +++ b/bin/report/print_xml.py @@ -38,85 +38,85 @@ from osv.orm import browse_null, browse_record import pooler class InheritDict(dict): - # Might be usefull when we're doing name lookup for call or eval. + # Might be usefull when we're doing name lookup for call or eval. - def __init__(self, parent=None): - self.parent = parent - - def __getitem__(self, name): - if name in self: - return super(InheritDict, self).__getitem__(name) - else: - if not self.parent: - raise KeyError - else: - return self.parent[name] + def __init__(self, parent=None): + self.parent = parent + + def __getitem__(self, name): + if name in self: + return super(InheritDict, self).__getitem__(name) + else: + if not self.parent: + raise KeyError + else: + return self.parent[name] def tounicode(val): - if isinstance(val, str): - unicode_val = unicode(val, 'utf-8') - elif isinstance(val, unicode): - unicode_val = val - else: - unicode_val = unicode(val) - return unicode_val + if isinstance(val, str): + unicode_val = unicode(val, 'utf-8') + elif isinstance(val, unicode): + unicode_val = val + else: + unicode_val = unicode(val) + return unicode_val class document(object): - def __init__(self, cr, uid, datas, func=False): - # create a new document - self.cr = cr - self.pool = pooler.get_pool(cr.dbname) - self.doc = minidom.Document() - self.func = func or {} - self.datas = datas - self.uid = uid - self.bin_datas = {} + def __init__(self, cr, uid, datas, func=False): + # create a new document + self.cr = cr + self.pool = pooler.get_pool(cr.dbname) + self.doc = minidom.Document() + self.func = func or {} + self.datas = datas + self.uid = uid + self.bin_datas = {} - def node_attrs_get(self, node): - attrs = {} - nattr = node.attributes - for i in range(nattr.length): - attr = nattr.item(i) - attrs[attr.localName] = attr.nodeValue -# attrs[attr.name] = attr.nodeValue - return attrs - - def get_value(self, browser, field_path): - fields = field_path.split('.') + def node_attrs_get(self, node): + attrs = {} + nattr = node.attributes + for i in range(nattr.length): + attr = nattr.item(i) + attrs[attr.localName] = attr.nodeValue +# attrs[attr.name] = attr.nodeValue + return attrs + + def get_value(self, browser, field_path): + fields = field_path.split('.') - if not len(fields): - print "WARNING: field name is empty!" - return '' - - value = browser - for f in fields: - if isinstance(value, list): - if len(value)==0: - print "WARNING: empty list found!" - return '' -# elif len(value)>1: -# print "WARNING:", len(value), "possibilities for", value[0]._table_name , "picking first..." - value = value[0] - if isinstance(value, browse_null): - return '' - else: - value = value[f] - - if isinstance(value, browse_null) or (type(value)==bool and not value): - return '' - else: - return value - - def get_value2(self, browser, field_path): - value = self.get_value(browser, field_path) - if isinstance(value, browse_record): - return value.id - elif isinstance(value, browse_null): - return False - else: - return value - - def eval(self, record, expr): + if not len(fields): + print "WARNING: field name is empty!" + return '' + + value = browser + for f in fields: + if isinstance(value, list): + if len(value)==0: + print "WARNING: empty list found!" + return '' +# elif len(value)>1: +# print "WARNING:", len(value), "possibilities for", value[0]._table_name , "picking first..." + value = value[0] + if isinstance(value, browse_null): + return '' + else: + value = value[f] + + if isinstance(value, browse_null) or (type(value)==bool and not value): + return '' + else: + return value + + def get_value2(self, browser, field_path): + value = self.get_value(browser, field_path) + if isinstance(value, browse_record): + return value.id + elif isinstance(value, browse_null): + return False + else: + return value + + def eval(self, record, expr): #TODO: support remote variables (eg address.title) in expr # how to do that: parse the string, find dots, replace those dotted variables by temporary # "simple ones", fetch the value of those variables and add them (temporarily) to the _data @@ -126,239 +126,239 @@ class document(object): # happen if the eval node is the first one using this browse_record # the next line is a workaround for the problem: it causes the resource to be loaded #Pinky: Why not this ? eval(expr, browser) ? -# name = browser.name -# data_dict = browser._data[self.get_value(browser, 'id')] - return eval(expr) +# name = browser.name +# data_dict = browser._data[self.get_value(browser, 'id')] + return eval(expr) - def parse_node(self, node, parent, browser, datas=None): - # node is the node of the xml template to be parsed - # parent = the parent node in the xml data tree we are creating - - if node.nodeType == node.ELEMENT_NODE: -# print '-'*60 -# print "parse_node", node -# print "parent: ", parent -# print "ids:", ids -# print "model:", model -# print "datas:", datas - - # convert the attributes of the node to a dictionary - - attrs = self.node_attrs_get(node) - if 'type' in attrs: - if attrs['type']=='field': - value = self.get_value(browser, attrs['name']) + def parse_node(self, node, parent, browser, datas=None): + # node is the node of the xml template to be parsed + # parent = the parent node in the xml data tree we are creating + + if node.nodeType == node.ELEMENT_NODE: +# print '-'*60 +# print "parse_node", node +# print "parent: ", parent +# print "ids:", ids +# print "model:", model +# print "datas:", datas + + # convert the attributes of the node to a dictionary + + attrs = self.node_attrs_get(node) + if 'type' in attrs: + if attrs['type']=='field': + value = self.get_value(browser, attrs['name']) #TODO: test this - if value == '' and 'default' in attrs: - value = attrs['default'] - el = self.doc.createElement(node.localName) - parent.appendChild(el) - el_txt = self.doc.createTextNode(tounicode(value)) - el.appendChild(el_txt) + if value == '' and 'default' in attrs: + value = attrs['default'] + el = self.doc.createElement(node.localName) + parent.appendChild(el) + el_txt = self.doc.createTextNode(tounicode(value)) + el.appendChild(el_txt) #TODO: test this - for key, value in attrs.iteritems(): - if key not in ('type', 'name', 'default'): - el.setAttribute(key, value) + for key, value in attrs.iteritems(): + if key not in ('type', 'name', 'default'): + el.setAttribute(key, value) - elif attrs['type']=='attachment': - if isinstance(browser, list): - model = browser[0]._table_name - else: - model = browser._table_name + elif attrs['type']=='attachment': + if isinstance(browser, list): + model = browser[0]._table_name + else: + model = browser._table_name - value = self.get_value(browser, attrs['name']) - - service = netsvc.LocalService("object_proxy") - ids = service.execute(self.cr.dbname, self.uid, 'ir.attachment', 'search', [('res_model','=',model),('res_id','=',int(value))]) - datas = service.execute(self.cr.dbname, self.uid, 'ir.attachment', 'read', ids) - - if len(datas): - # if there are several, pick first - datas = datas[0] - fname = str(datas['datas_fname']) - ext = fname.split('.')[-1].lower() - if ext in ('jpg','jpeg', 'png'): - import base64, StringIO - dt = base64.decodestring(datas['datas']) - fp = StringIO.StringIO(dt) - i = str(len(self.bin_datas)) - self.bin_datas[i] = fp - - el = self.doc.createElement(node.localName) - parent.appendChild(el) - # node content is the length of the image - el_txt = self.doc.createTextNode(i) - el.appendChild(el_txt) - - elif attrs['type']=='data': + value = self.get_value(browser, attrs['name']) + + service = netsvc.LocalService("object_proxy") + ids = service.execute(self.cr.dbname, self.uid, 'ir.attachment', 'search', [('res_model','=',model),('res_id','=',int(value))]) + datas = service.execute(self.cr.dbname, self.uid, 'ir.attachment', 'read', ids) + + if len(datas): + # if there are several, pick first + datas = datas[0] + fname = str(datas['datas_fname']) + ext = fname.split('.')[-1].lower() + if ext in ('jpg','jpeg', 'png'): + import base64, StringIO + dt = base64.decodestring(datas['datas']) + fp = StringIO.StringIO(dt) + i = str(len(self.bin_datas)) + self.bin_datas[i] = fp + + el = self.doc.createElement(node.localName) + parent.appendChild(el) + # node content is the length of the image + el_txt = self.doc.createTextNode(i) + el.appendChild(el_txt) + + elif attrs['type']=='data': #TODO: test this - el = self.doc.createElement(node.localName) - parent.appendChild(el) - txt = self.datas.get('form', {}).get(attrs['name'], '') - el_txt = self.doc.createTextNode(tounicode(txt)) - el.appendChild(el_txt) + el = self.doc.createElement(node.localName) + parent.appendChild(el) + txt = self.datas.get('form', {}).get(attrs['name'], '') + el_txt = self.doc.createTextNode(tounicode(txt)) + el.appendChild(el_txt) - elif attrs['type']=='function': - el = self.doc.createElement(node.localName) - parent.appendChild(el) - if attrs['name'] in self.func: - txt = self.func[attrs['name']](node) - else: - txt = print_fnc.print_fnc(attrs['name'], node) - el_txt = self.doc.createTextNode(txt) - el.appendChild(el_txt) + elif attrs['type']=='function': + el = self.doc.createElement(node.localName) + parent.appendChild(el) + if attrs['name'] in self.func: + txt = self.func[attrs['name']](node) + else: + txt = print_fnc.print_fnc(attrs['name'], node) + el_txt = self.doc.createTextNode(txt) + el.appendChild(el_txt) - elif attrs['type']=='eval': + elif attrs['type']=='eval': #TODO: faire ca plus proprement - if isinstance(browser, list): - print "ERROR: EVAL!" - el = self.doc.createElement(node.localName) - parent.appendChild(el) - value = self.eval(browser, attrs['expr']) - el_txt = self.doc.createTextNode(str(value)) - el.appendChild(el_txt) + if isinstance(browser, list): + print "ERROR: EVAL!" + el = self.doc.createElement(node.localName) + parent.appendChild(el) + value = self.eval(browser, attrs['expr']) + el_txt = self.doc.createTextNode(str(value)) + el.appendChild(el_txt) - elif attrs['type']=='fields': - fields = attrs['name'].split(',') - vals = {} - for b in browser: - value = tuple([self.get_value2(b, f) for f in fields]) - if not value in vals: - vals[value]=[] - vals[value].append(b) - keys = vals.keys() - keys.sort() + elif attrs['type']=='fields': + fields = attrs['name'].split(',') + vals = {} + for b in browser: + value = tuple([self.get_value2(b, f) for f in fields]) + if not value in vals: + vals[value]=[] + vals[value].append(b) + keys = vals.keys() + keys.sort() - if 'order' in attrs and attrs['order']=='desc': - keys.reverse() + if 'order' in attrs and attrs['order']=='desc': + keys.reverse() - v_list = [vals[k] for k in keys] - for v in v_list: - el = self.doc.createElement(node.localName) - parent.appendChild(el) - el_cld = node.firstChild - while el_cld: - self.parse_node(el_cld, el, v) - el_cld = el_cld.nextSibling + v_list = [vals[k] for k in keys] + for v in v_list: + el = self.doc.createElement(node.localName) + parent.appendChild(el) + el_cld = node.firstChild + while el_cld: + self.parse_node(el_cld, el, v) + el_cld = el_cld.nextSibling - elif attrs['type']=='call': - if len(attrs['args']): -#TODO: test this - # fetches the values of the variables which names where passed in the args attribute - args = [self.eval(browser, arg) for arg in attrs['args'].split(',')] - else: - args = [] - - # get the object - if attrs.has_key('model'): - obj = self.pool.get(attrs['model']) - else: - if isinstance(browser, list): - obj = browser[0]._table - else: - obj = browser._table + elif attrs['type']=='call': + if len(attrs['args']): +#TODO: test this + # fetches the values of the variables which names where passed in the args attribute + args = [self.eval(browser, arg) for arg in attrs['args'].split(',')] + else: + args = [] + + # get the object + if attrs.has_key('model'): + obj = self.pool.get(attrs['model']) + else: + if isinstance(browser, list): + obj = browser[0]._table + else: + obj = browser._table - # get the ids - if attrs.has_key('ids'): - ids = self.eval(browser, attrs['ids']) - else: - if isinstance(browser, list): - ids = [b.id for b in browser] - else: - ids = [browser.id] - - # call the method itself - newdatas = getattr(obj, attrs['name'])(self.cr, self.uid, ids, *args) - - def parse_result_tree(node, parent, datas): - if node.nodeType == node.ELEMENT_NODE: - el = self.doc.createElement(node.localName) - parent.appendChild(el) - atr = self.node_attrs_get(node) - if 'value' in atr: - #print "type=>",type(datas[atr['value']]) - #print "value=>",datas[atr['value']] - if not isinstance(datas[atr['value']], (str, unicode)): - txt = self.doc.createTextNode(str(datas[atr['value']])) - else: - txt = self.doc.createTextNode(datas[atr['value']].decode('utf-8')) - el.appendChild(txt) - else: - el_cld = node.firstChild - while el_cld: - parse_result_tree(el_cld, el, datas) - el_cld = el_cld.nextSibling - elif node.nodeType==node.TEXT_NODE: - el = self.doc.createTextNode(node.nodeValue) - parent.appendChild(el) - else: - pass - - if not isinstance(newdatas, list): - newdatas = [newdatas] - for newdata in newdatas: - parse_result_tree(node, parent, newdata) + # get the ids + if attrs.has_key('ids'): + ids = self.eval(browser, attrs['ids']) + else: + if isinstance(browser, list): + ids = [b.id for b in browser] + else: + ids = [browser.id] + + # call the method itself + newdatas = getattr(obj, attrs['name'])(self.cr, self.uid, ids, *args) + + def parse_result_tree(node, parent, datas): + if node.nodeType == node.ELEMENT_NODE: + el = self.doc.createElement(node.localName) + parent.appendChild(el) + atr = self.node_attrs_get(node) + if 'value' in atr: + #print "type=>",type(datas[atr['value']]) + #print "value=>",datas[atr['value']] + if not isinstance(datas[atr['value']], (str, unicode)): + txt = self.doc.createTextNode(str(datas[atr['value']])) + else: + txt = self.doc.createTextNode(datas[atr['value']].decode('utf-8')) + el.appendChild(txt) + else: + el_cld = node.firstChild + while el_cld: + parse_result_tree(el_cld, el, datas) + el_cld = el_cld.nextSibling + elif node.nodeType==node.TEXT_NODE: + el = self.doc.createTextNode(node.nodeValue) + parent.appendChild(el) + else: + pass + + if not isinstance(newdatas, list): + newdatas = [newdatas] + for newdata in newdatas: + parse_result_tree(node, parent, newdata) - elif attrs['type']=='zoom': - value = self.get_value(browser, attrs['name']) - - if value: - if not isinstance(value, list): - v_list = [value] - else: - v_list = value - for v in v_list: - el = self.doc.createElement(node.localName) - parent.appendChild(el) - el_cld = node.firstChild - while el_cld: - self.parse_node(el_cld, el, v) - el_cld = el_cld.nextSibling - else: - # if there is no "type" attribute in the node, copy it to the xml data and parse its childs - el = self.doc.createElement(node.localName) - parent.appendChild(el) - el_cld = node.firstChild - while el_cld: - self.parse_node(el_cld, el, browser) - el_cld = el_cld.nextSibling + elif attrs['type']=='zoom': + value = self.get_value(browser, attrs['name']) + + if value: + if not isinstance(value, list): + v_list = [value] + else: + v_list = value + for v in v_list: + el = self.doc.createElement(node.localName) + parent.appendChild(el) + el_cld = node.firstChild + while el_cld: + self.parse_node(el_cld, el, v) + el_cld = el_cld.nextSibling + else: + # if there is no "type" attribute in the node, copy it to the xml data and parse its childs + el = self.doc.createElement(node.localName) + parent.appendChild(el) + el_cld = node.firstChild + while el_cld: + self.parse_node(el_cld, el, browser) + el_cld = el_cld.nextSibling - elif node.nodeType==node.TEXT_NODE: - # if it's a text node, copy it to the xml data - el = self.doc.createTextNode(node.nodeValue) - parent.appendChild(el) - else: - pass + elif node.nodeType==node.TEXT_NODE: + # if it's a text node, copy it to the xml data + el = self.doc.createTextNode(node.nodeValue) + parent.appendChild(el) + else: + pass - def xml_get(self): - return self.doc.toxml('utf-8') + def xml_get(self): + return self.doc.toxml('utf-8') - def parse_tree(self, ids, model, context=None): - if not context: - context={} - browser = self.pool.get(model).browse(self.cr, self.uid, ids, context) - self.parse_node(self.dom.documentElement, self.doc, browser) - - def parse_string(self, xml, ids, model, context=None): - if not context: - context={} - # parses the xml template to memory - self.dom = minidom.parseString(xml) - - # create the xml data from the xml template - self.parse_tree(ids, model, context) + def parse_tree(self, ids, model, context=None): + if not context: + context={} + browser = self.pool.get(model).browse(self.cr, self.uid, ids, context) + self.parse_node(self.dom.documentElement, self.doc, browser) + + def parse_string(self, xml, ids, model, context=None): + if not context: + context={} + # parses the xml template to memory + self.dom = minidom.parseString(xml) + + # create the xml data from the xml template + self.parse_tree(ids, model, context) - def parse(self, filename, ids, model, context=None): - if not context: - context={} - # parses the xml template to memory - self.dom = minidom.parseString(tools.file_open(filename).read()) + def parse(self, filename, ids, model, context=None): + if not context: + context={} + # parses the xml template to memory + self.dom = minidom.parseString(tools.file_open(filename).read()) - # create the xml data from the xml template - self.parse_tree(ids, model, context) + # create the xml data from the xml template + self.parse_tree(ids, model, context) - def close(self): - self.doc = None - self.dom = None + def close(self): + self.doc = None + self.dom = None diff --git a/bin/report/printscreen/ps_form.py b/bin/report/printscreen/ps_form.py index 2555ba1206a..f7bf398e61e 100644 --- a/bin/report/printscreen/ps_form.py +++ b/bin/report/printscreen/ps_form.py @@ -40,123 +40,123 @@ import libxslt import time, os class report_printscreen_list(report_int): - def __init__(self, name): - report_int.__init__(self, name) - - def _parse_node(self, root_node): - result = [] - for node in root_node.childNodes: - if node.localName == 'field': - attrsa = node.attributes - attrs = {} - if not attrsa is None: - for i in range(attrsa.length): - attrs[attrsa.item(i).localName] = attrsa.item(i).nodeValue - result.append(attrs['name']) - else: - result.extend(self._parse_node(node)) - return result - - def _parse_string(self, view): - dom = minidom.parseString(view) - return self._parse_node(dom) - - def create(self, cr, uid, ids, datas, context=None): - if not context: - context={} - datas['ids'] = ids - pool = pooler.get_pool(cr.dbname) - model_id = pool.get('ir.model').search(cr, uid, [('model','=',model._name)]) - if model_id: - model_desc = pool.get('ir.model').browse(cr, uid, model_id, context).name - else: - model_desc = model._description - - model = pool.get(datas['model']) - result = model.fields_view_get(cr, uid, view_type='tree', context=context) - - fields_order = self._parse_string(result['arch']) - rows = model.read(cr, uid, datas['ids'], result['fields'].keys() ) - res = self._create_table(uid, datas['ids'], result['fields'], fields_order, rows, context, model._description) - return (self.obj.get(), 'pdf') - - - def _create_table(self, uid, ids, fields, fields_order, results, context, title=''): - pageSize=[297.0,210.0] - - impl = minidom.getDOMImplementation() - new_doc = impl.createDocument(None, "report", None) - - # build header - config = new_doc.createElement("config") - - def _append_node(name, text): - n = new_doc.createElement(name) - t = new_doc.createTextNode(text) - n.appendChild(t) - config.appendChild(n) - - _append_node('date', time.strftime('%d/%m/%Y')) - _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) - _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) - _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) - - _append_node('report-header', title) - - l = [] - t = 0 - strmax = (pageSize[0]-40) * 2.8346 - for f in fields_order: - s = 0 - if fields[f]['type'] in ('date','time','float','integer'): - s = 60 - strmax -= s - else: - t += fields[f].get('size', 56) / 28 + 1 - l.append(s) - for pos in range(len(l)): - if not l[pos]: - s = fields[fields_order[pos]].get('size', 56) / 28 + 1 - l[pos] = strmax * s / t - _append_node('tableSize', ','.join(map(str,l)) ) - new_doc.childNodes[0].appendChild(config) - header = new_doc.createElement("header") - - for f in fields_order: - field = new_doc.createElement("field") - field_txt = new_doc.createTextNode(str(fields[f]['string'])) - field.appendChild(field_txt) - header.appendChild(field) - - new_doc.childNodes[0].appendChild(header) - - lines = new_doc.createElement("lines") - for line in results: - node_line = new_doc.createElement("row") - for f in fields_order: - if fields[f]['type']=='many2one' and line[f]: - line[f] = line[f][1] - if fields[f]['type'] in ('one2many','many2many') and line[f]: - line[f] = '( '+str(len(line[f])) + ' )' - col = new_doc.createElement("col") - col.setAttribute('tree','no') - if line[f] != None: - txt = new_doc.createTextNode(str(line[f] or '')) - else: - txt = new_doc.createTextNode('/') - col.appendChild(txt) - node_line.appendChild(col) - lines.appendChild(node_line) - new_doc.childNodes[0].appendChild(lines) - - styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl')) - style = libxslt.parseStylesheetDoc(styledoc) - doc = libxml2.parseDoc(new_doc.toxml()) - rml_obj = style.applyStylesheet(doc, None) - rml = style.saveResultToString(rml_obj) - - self.obj = render.rml(rml) - self.obj.render() - return True + def __init__(self, name): + report_int.__init__(self, name) + + def _parse_node(self, root_node): + result = [] + for node in root_node.childNodes: + if node.localName == 'field': + attrsa = node.attributes + attrs = {} + if not attrsa is None: + for i in range(attrsa.length): + attrs[attrsa.item(i).localName] = attrsa.item(i).nodeValue + result.append(attrs['name']) + else: + result.extend(self._parse_node(node)) + return result + + def _parse_string(self, view): + dom = minidom.parseString(view) + return self._parse_node(dom) + + def create(self, cr, uid, ids, datas, context=None): + if not context: + context={} + datas['ids'] = ids + pool = pooler.get_pool(cr.dbname) + model_id = pool.get('ir.model').search(cr, uid, [('model','=',model._name)]) + if model_id: + model_desc = pool.get('ir.model').browse(cr, uid, model_id, context).name + else: + model_desc = model._description + + model = pool.get(datas['model']) + result = model.fields_view_get(cr, uid, view_type='tree', context=context) + + fields_order = self._parse_string(result['arch']) + rows = model.read(cr, uid, datas['ids'], result['fields'].keys() ) + res = self._create_table(uid, datas['ids'], result['fields'], fields_order, rows, context, model._description) + return (self.obj.get(), 'pdf') + + + def _create_table(self, uid, ids, fields, fields_order, results, context, title=''): + pageSize=[297.0,210.0] + + impl = minidom.getDOMImplementation() + new_doc = impl.createDocument(None, "report", None) + + # build header + config = new_doc.createElement("config") + + def _append_node(name, text): + n = new_doc.createElement(name) + t = new_doc.createTextNode(text) + n.appendChild(t) + config.appendChild(n) + + _append_node('date', time.strftime('%d/%m/%Y')) + _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) + _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) + _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) + + _append_node('report-header', title) + + l = [] + t = 0 + strmax = (pageSize[0]-40) * 2.8346 + for f in fields_order: + s = 0 + if fields[f]['type'] in ('date','time','float','integer'): + s = 60 + strmax -= s + else: + t += fields[f].get('size', 56) / 28 + 1 + l.append(s) + for pos in range(len(l)): + if not l[pos]: + s = fields[fields_order[pos]].get('size', 56) / 28 + 1 + l[pos] = strmax * s / t + _append_node('tableSize', ','.join(map(str,l)) ) + new_doc.childNodes[0].appendChild(config) + header = new_doc.createElement("header") + + for f in fields_order: + field = new_doc.createElement("field") + field_txt = new_doc.createTextNode(str(fields[f]['string'])) + field.appendChild(field_txt) + header.appendChild(field) + + new_doc.childNodes[0].appendChild(header) + + lines = new_doc.createElement("lines") + for line in results: + node_line = new_doc.createElement("row") + for f in fields_order: + if fields[f]['type']=='many2one' and line[f]: + line[f] = line[f][1] + if fields[f]['type'] in ('one2many','many2many') and line[f]: + line[f] = '( '+str(len(line[f])) + ' )' + col = new_doc.createElement("col") + col.setAttribute('tree','no') + if line[f] != None: + txt = new_doc.createTextNode(str(line[f] or '')) + else: + txt = new_doc.createTextNode('/') + col.appendChild(txt) + node_line.appendChild(col) + lines.appendChild(node_line) + new_doc.childNodes[0].appendChild(lines) + + styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl')) + style = libxslt.parseStylesheetDoc(styledoc) + doc = libxml2.parseDoc(new_doc.toxml()) + rml_obj = style.applyStylesheet(doc, None) + rml = style.saveResultToString(rml_obj) + + self.obj = render.rml(rml) + self.obj.render() + return True report_printscreen_list('report.printscreen.form') diff --git a/bin/report/printscreen/ps_list.py b/bin/report/printscreen/ps_list.py index e3ce11c8f73..a355be15679 100644 --- a/bin/report/printscreen/ps_list.py +++ b/bin/report/printscreen/ps_list.py @@ -40,166 +40,166 @@ import libxslt import time, os class report_printscreen_list(report_int): - def __init__(self, name): - report_int.__init__(self, name) - - def _parse_node(self, root_node): - result = [] - for node in root_node.childNodes: - if node.localName == 'field': - attrsa = node.attributes - attrs = {} - if not attrsa is None: - for i in range(attrsa.length): - attrs[attrsa.item(i).localName] = attrsa.item(i).nodeValue - result.append(attrs['name']) - else: - result.extend(self._parse_node(node)) - return result - - def _parse_string(self, view): - dom = minidom.parseString(unicode(view, 'utf-8').encode('utf-8')) - return self._parse_node(dom) - - def create(self, cr, uid, ids, datas, context=None): - if not context: - context={} - pool = pooler.get_pool(cr.dbname) - model = pool.get(datas['model']) - model_id = pool.get('ir.model').search(cr, uid, [('model','=',model._name)]) - if model_id: - model_desc = pool.get('ir.model').browse(cr, uid, model_id[0], context).name - else: - model_desc = model._description - - datas['ids'] = ids - model = pooler.get_pool(cr.dbname).get(datas['model']) - - result = model.fields_view_get(cr, uid, view_type='tree', context=context) - fields_order = self._parse_string(result['arch']) - rows = model.read(cr, uid, datas['ids'], result['fields'].keys(), context ) - res = self._create_table(uid, datas['ids'], result['fields'], fields_order, rows, context, model_desc) - return (self.obj.get(), 'pdf') - - - def _create_table(self, uid, ids, fields, fields_order, results, context, title=''): - pageSize=[297.0, 210.0] - - impl = minidom.getDOMImplementation() - new_doc = impl.createDocument(None, "report", None) - - # build header - config = new_doc.createElement("config") - - def _append_node(name, text): - n = new_doc.createElement(name) - t = new_doc.createTextNode(text) - n.appendChild(t) - config.appendChild(n) - - _append_node('date', time.strftime('%d/%m/%Y')) - _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) - _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) - _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) - - _append_node('report-header', title) - l = [] - t = 0 - rowcount=0; - strmax = (pageSize[0]-40) * 2.8346 - temp = [] - count = len(fields_order) - for i in range(0,count): - temp.append(0) - - ince = -1; - for f in fields_order: - s = 0 - ince += 1 - if fields[f]['type'] in ('date','time','float','integer'): - s = 60 - strmax -= s - if fields[f]['type'] in ('float','integer'): - temp[ince]=1; - else: - t += fields[f].get('size', 80) / 28 + 1 - - l.append(s) - - for pos in range(len(l)): - if not l[pos]: - s = fields[fields_order[pos]].get('size', 80) / 28 + 1 - l[pos] = strmax * s / t - - _append_node('tableSize', ','.join(map(str,l)) ) - new_doc.childNodes[0].appendChild(config) - header = new_doc.createElement("header") - - for f in fields_order: - field = new_doc.createElement("field") - field_txt = new_doc.createTextNode(str(fields[f]['string'] or '')) - field.appendChild(field_txt) - header.appendChild(field) - - new_doc.childNodes[0].appendChild(header) - - lines = new_doc.createElement("lines") - - tsum = [] - count = len(fields_order) - for i in range(0,count): - tsum.append(0) - - for line in results: - node_line = new_doc.createElement("row") - - count = -1 - for f in fields_order: - count += 1 - if fields[f]['type']=='many2one' and line[f]: - line[f] = line[f][1] - if fields[f]['type'] in ('one2many','many2many') and line[f]: - line[f] = '( '+str(len(line[f])) + ' )' - col = new_doc.createElement("col") - col.setAttribute('para','yes') - col.setAttribute('tree','no') - if line[f] != None: - txt = new_doc.createTextNode(str(line[f] or '')) - if temp[count] == 1: - tsum[count] = tsum[count] + line[f]; - - else: - txt = new_doc.createTextNode('/') - col.appendChild(txt) - node_line.appendChild(col) - lines.appendChild(node_line) - node_line = new_doc.createElement("row") - lines.appendChild(node_line) - node_line = new_doc.createElement("row") - for f in range(0,count+1): - col = new_doc.createElement("col") - col.setAttribute('para','yes') - col.setAttribute('tree','no') - if tsum[f] != None: - txt = new_doc.createTextNode(str(tsum[f] or '')) - else: - txt = new_doc.createTextNode('/') - if f == 0: - txt = new_doc.createTextNode('Total') - - col.appendChild(txt) - node_line.appendChild(col) - lines.appendChild(node_line) - - new_doc.childNodes[0].appendChild(lines) - - styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl')) - style = libxslt.parseStylesheetDoc(styledoc) - doc = libxml2.parseDoc(new_doc.toxml()) - rml_obj = style.applyStylesheet(doc, None) - rml = style.saveResultToString(rml_obj) - self.obj = render.rml(rml) - self.obj.render() - return True + def __init__(self, name): + report_int.__init__(self, name) + + def _parse_node(self, root_node): + result = [] + for node in root_node.childNodes: + if node.localName == 'field': + attrsa = node.attributes + attrs = {} + if not attrsa is None: + for i in range(attrsa.length): + attrs[attrsa.item(i).localName] = attrsa.item(i).nodeValue + result.append(attrs['name']) + else: + result.extend(self._parse_node(node)) + return result + + def _parse_string(self, view): + dom = minidom.parseString(unicode(view, 'utf-8').encode('utf-8')) + return self._parse_node(dom) + + def create(self, cr, uid, ids, datas, context=None): + if not context: + context={} + pool = pooler.get_pool(cr.dbname) + model = pool.get(datas['model']) + model_id = pool.get('ir.model').search(cr, uid, [('model','=',model._name)]) + if model_id: + model_desc = pool.get('ir.model').browse(cr, uid, model_id[0], context).name + else: + model_desc = model._description + + datas['ids'] = ids + model = pooler.get_pool(cr.dbname).get(datas['model']) + + result = model.fields_view_get(cr, uid, view_type='tree', context=context) + fields_order = self._parse_string(result['arch']) + rows = model.read(cr, uid, datas['ids'], result['fields'].keys(), context ) + res = self._create_table(uid, datas['ids'], result['fields'], fields_order, rows, context, model_desc) + return (self.obj.get(), 'pdf') + + + def _create_table(self, uid, ids, fields, fields_order, results, context, title=''): + pageSize=[297.0, 210.0] + + impl = minidom.getDOMImplementation() + new_doc = impl.createDocument(None, "report", None) + + # build header + config = new_doc.createElement("config") + + def _append_node(name, text): + n = new_doc.createElement(name) + t = new_doc.createTextNode(text) + n.appendChild(t) + config.appendChild(n) + + _append_node('date', time.strftime('%d/%m/%Y')) + _append_node('PageSize', '%.2fmm,%.2fmm' % tuple(pageSize)) + _append_node('PageWidth', '%.2f' % (pageSize[0] * 2.8346,)) + _append_node('PageHeight', '%.2f' %(pageSize[1] * 2.8346,)) + + _append_node('report-header', title) + l = [] + t = 0 + rowcount=0; + strmax = (pageSize[0]-40) * 2.8346 + temp = [] + count = len(fields_order) + for i in range(0,count): + temp.append(0) + + ince = -1; + for f in fields_order: + s = 0 + ince += 1 + if fields[f]['type'] in ('date','time','float','integer'): + s = 60 + strmax -= s + if fields[f]['type'] in ('float','integer'): + temp[ince]=1; + else: + t += fields[f].get('size', 80) / 28 + 1 + + l.append(s) + + for pos in range(len(l)): + if not l[pos]: + s = fields[fields_order[pos]].get('size', 80) / 28 + 1 + l[pos] = strmax * s / t + + _append_node('tableSize', ','.join(map(str,l)) ) + new_doc.childNodes[0].appendChild(config) + header = new_doc.createElement("header") + + for f in fields_order: + field = new_doc.createElement("field") + field_txt = new_doc.createTextNode(str(fields[f]['string'] or '')) + field.appendChild(field_txt) + header.appendChild(field) + + new_doc.childNodes[0].appendChild(header) + + lines = new_doc.createElement("lines") + + tsum = [] + count = len(fields_order) + for i in range(0,count): + tsum.append(0) + + for line in results: + node_line = new_doc.createElement("row") + + count = -1 + for f in fields_order: + count += 1 + if fields[f]['type']=='many2one' and line[f]: + line[f] = line[f][1] + if fields[f]['type'] in ('one2many','many2many') and line[f]: + line[f] = '( '+str(len(line[f])) + ' )' + col = new_doc.createElement("col") + col.setAttribute('para','yes') + col.setAttribute('tree','no') + if line[f] != None: + txt = new_doc.createTextNode(str(line[f] or '')) + if temp[count] == 1: + tsum[count] = tsum[count] + line[f]; + + else: + txt = new_doc.createTextNode('/') + col.appendChild(txt) + node_line.appendChild(col) + lines.appendChild(node_line) + node_line = new_doc.createElement("row") + lines.appendChild(node_line) + node_line = new_doc.createElement("row") + for f in range(0,count+1): + col = new_doc.createElement("col") + col.setAttribute('para','yes') + col.setAttribute('tree','no') + if tsum[f] != None: + txt = new_doc.createTextNode(str(tsum[f] or '')) + else: + txt = new_doc.createTextNode('/') + if f == 0: + txt = new_doc.createTextNode('Total') + + col.appendChild(txt) + node_line.appendChild(col) + lines.appendChild(node_line) + + new_doc.childNodes[0].appendChild(lines) + + styledoc = libxml2.parseFile(os.path.join(tools.config['root_path'],'addons/base/report/custom_new.xsl')) + style = libxslt.parseStylesheetDoc(styledoc) + doc = libxml2.parseDoc(new_doc.toxml()) + rml_obj = style.applyStylesheet(doc, None) + rml = style.saveResultToString(rml_obj) + self.obj = render.rml(rml) + self.obj.render() + return True report_printscreen_list('report.printscreen.list') diff --git a/bin/report/render/__init__.py b/bin/report/render/__init__.py index 92a9a2247d7..263bc3c3e13 100644 --- a/bin/report/render/__init__.py +++ b/bin/report/render/__init__.py @@ -33,6 +33,6 @@ from rml import rml, rml2html from render import render try: - import Image + import Image except: - print 'WARNING; Python Imaging not installed, you can use only .JPG pictures !' + print 'WARNING; Python Imaging not installed, you can use only .JPG pictures !' diff --git a/bin/report/render/render.py b/bin/report/render/render.py index 7f0649075ef..ea03b3cfc32 100644 --- a/bin/report/render/render.py +++ b/bin/report/render/render.py @@ -44,38 +44,38 @@ import threading # _render # class render(object): - def __init__(self, bin_datas={}, path='.'): - self.done = False - self.bin_datas = bin_datas - self.path = path - - def _render(self): - return None + def __init__(self, bin_datas={}, path='.'): + self.done = False + self.bin_datas = bin_datas + self.path = path + + def _render(self): + return None - def render(self): - self.done = False - result = self._render() - self._result = result - self.done = True - return True - - def is_done(self): - res = self.done - return res + def render(self): + self.done = False + result = self._render() + self._result = result + self.done = True + return True + + def is_done(self): + res = self.done + return res - def get(self): - if self.is_done(): - return self._result - else: - return None + def get(self): + if self.is_done(): + return self._result + else: + return None if __name__=='__main__': - import time - print 'Multi-thread code !' - r = render() - r.render() - while not r.is_done(): - print 'not yet!' - time.sleep(1) - print 'done!' + import time + print 'Multi-thread code !' + r = render() + r.render() + while not r.is_done(): + print 'not yet!' + time.sleep(1) + print 'done!' diff --git a/bin/report/render/rml.py b/bin/report/render/rml.py index 6be8dc55969..b497fb7b4f7 100644 --- a/bin/report/render/rml.py +++ b/bin/report/render/rml.py @@ -32,20 +32,20 @@ import rml2pdf import rml2html as htmlizer class rml(render.render): - def __init__(self, xml, datas={}, path='.'): - render.render.__init__(self, datas) - self.xml = xml - self.output_type = 'pdf' - self.path = path + def __init__(self, xml, datas={}, path='.'): + render.render.__init__(self, datas) + self.xml = xml + self.output_type = 'pdf' + self.path = path - def _render(self): - return rml2pdf.parseString(self.xml, images=self.bin_datas, path=self.path) + def _render(self): + return rml2pdf.parseString(self.xml, images=self.bin_datas, path=self.path) class rml2html(render.render): - def __init__(self, xml, datas={}): - super(rml2html, self).__init__(datas) - self.xml = xml - self.output_type = 'html' - - def _render(self): - return htmlizer.parseString(self.xml) + def __init__(self, xml, datas={}): + super(rml2html, self).__init__(datas) + self.xml = xml + self.output_type = 'html' + + def _render(self): + return htmlizer.parseString(self.xml) diff --git a/bin/report/render/rml2html/rml2html.py b/bin/report/render/rml2html/rml2html.py index 3d0477e3c38..898d5c19ac9 100755..100644 --- a/bin/report/render/rml2html/rml2html.py +++ b/bin/report/render/rml2html/rml2html.py @@ -52,341 +52,341 @@ import copy import utils class _flowable(object): - def __init__(self, template, doc): - self._tags = { - 'title': self._tag_title, - 'spacer': self._tag_spacer, - 'para': self._tag_para, - 'nextFrame': self._tag_next_frame, - 'blockTable': self._tag_table, - 'pageBreak': self._tag_page_break, - 'setNextTemplate': self._tag_next_template, - } - self.template = template - self.doc = doc - - def _tag_page_break(self, node): - return '<br/>'*3 - - def _tag_next_template(self, node): - return '' - - def _tag_next_frame(self, node): - result=self.template.frame_stop() - result+='<br/>' - result+=self.template.frame_start() - return result - - def _tag_title(self, node): - node.tagName='h1' - return node.toxml() - - def _tag_spacer(self, node): - length = 1+int(utils.unit_get(node.getAttribute('length')))/35 - return "<br/>"*length - - def _tag_table(self, node): - node.tagName='table' - if node.hasAttribute('colWidths'): - sizes = map(lambda x: utils.unit_get(x), node.getAttribute('colWidths').split(',')) - tr = self.doc.createElement('tr') - for s in sizes: - td = self.doc.createElement('td') - td.setAttribute("width", str(s)) - tr.appendChild(td) - node.appendChild(tr) - return node.toxml() - - def _tag_para(self, node): - node.tagName='p' - if node.hasAttribute('style'): - node.setAttribute('class', node.getAttribute('style')) - return node.toxml() - - def render(self, node): - result = self.template.start() - result += self.template.frame_start() - for n in node.childNodes: - if n.nodeType==node.ELEMENT_NODE: - if n.localName in self._tags: - result += self._tags[n.localName](n) - else: - pass - #print 'tag', n.localName, 'not yet implemented!' - result += self.template.frame_stop() - result += self.template.end() - return result + def __init__(self, template, doc): + self._tags = { + 'title': self._tag_title, + 'spacer': self._tag_spacer, + 'para': self._tag_para, + 'nextFrame': self._tag_next_frame, + 'blockTable': self._tag_table, + 'pageBreak': self._tag_page_break, + 'setNextTemplate': self._tag_next_template, + } + self.template = template + self.doc = doc + + def _tag_page_break(self, node): + return '<br/>'*3 + + def _tag_next_template(self, node): + return '' + + def _tag_next_frame(self, node): + result=self.template.frame_stop() + result+='<br/>' + result+=self.template.frame_start() + return result + + def _tag_title(self, node): + node.tagName='h1' + return node.toxml() + + def _tag_spacer(self, node): + length = 1+int(utils.unit_get(node.getAttribute('length')))/35 + return "<br/>"*length + + def _tag_table(self, node): + node.tagName='table' + if node.hasAttribute('colWidths'): + sizes = map(lambda x: utils.unit_get(x), node.getAttribute('colWidths').split(',')) + tr = self.doc.createElement('tr') + for s in sizes: + td = self.doc.createElement('td') + td.setAttribute("width", str(s)) + tr.appendChild(td) + node.appendChild(tr) + return node.toxml() + + def _tag_para(self, node): + node.tagName='p' + if node.hasAttribute('style'): + node.setAttribute('class', node.getAttribute('style')) + return node.toxml() + + def render(self, node): + result = self.template.start() + result += self.template.frame_start() + for n in node.childNodes: + if n.nodeType==node.ELEMENT_NODE: + if n.localName in self._tags: + result += self._tags[n.localName](n) + else: + pass + #print 'tag', n.localName, 'not yet implemented!' + result += self.template.frame_stop() + result += self.template.end() + return result class _rml_tmpl_tag(object): - def __init__(self, *args): - pass - def tag_start(self): - return '' - def tag_end(self): - return False - def tag_stop(self): - return '' - def tag_mergeable(self): - return True + def __init__(self, *args): + pass + def tag_start(self): + return '' + def tag_end(self): + return False + def tag_stop(self): + return '' + def tag_mergeable(self): + return True class _rml_tmpl_frame(_rml_tmpl_tag): - def __init__(self, posx, width): - self.width = width - self.posx = posx - def tag_start(self): - return '<table border="0" width="%d"><tr><td width="%d"> </td><td>' % (self.width+self.posx,self.posx) - def tag_end(self): - return True - def tag_stop(self): - return '</td></tr></table><br/>' - def tag_mergeable(self): - return False - - # An awfull workaround since I don't really understand the semantic behind merge. - def merge(self, frame): - pass + def __init__(self, posx, width): + self.width = width + self.posx = posx + def tag_start(self): + return '<table border="0" width="%d"><tr><td width="%d"> </td><td>' % (self.width+self.posx,self.posx) + def tag_end(self): + return True + def tag_stop(self): + return '</td></tr></table><br/>' + def tag_mergeable(self): + return False + + # An awfull workaround since I don't really understand the semantic behind merge. + def merge(self, frame): + pass class _rml_tmpl_draw_string(_rml_tmpl_tag): - def __init__(self, node, style): - self.posx = utils.unit_get(node.getAttribute('x')) - self.posy = utils.unit_get(node.getAttribute('y')) - aligns = { - 'drawString': 'left', - 'drawRightString': 'right', - 'drawCentredString': 'center' - } - align = aligns[node.localName] - self.pos = [(self.posx, self.posy, align, utils.text_get(node), style.get('td'), style.font_size_get('td'))] - - def tag_start(self): - self.pos.sort() - res = '<table border="0" cellpadding="0" cellspacing="0"><tr>' - posx = 0 - i = 0 - for (x,y,align,txt, style, fs) in self.pos: - if align=="left": - pos2 = len(txt)*fs - res+='<td width="%d"></td><td style="%s" width="%d">%s</td>' % (x - posx, style, pos2, txt) - posx = x+pos2 - if align=="right": - res+='<td width="%d" align="right" style="%s">%s</td>' % (x - posx, style, txt) - posx = x - if align=="center": - res+='<td width="%d" align="center" style="%s">%s</td>' % ((x - posx)*2, style, txt) - posx = 2*x-posx - i+=1 - res+='</tr></table>' - return res - def merge(self, ds): - self.pos+=ds.pos + def __init__(self, node, style): + self.posx = utils.unit_get(node.getAttribute('x')) + self.posy = utils.unit_get(node.getAttribute('y')) + aligns = { + 'drawString': 'left', + 'drawRightString': 'right', + 'drawCentredString': 'center' + } + align = aligns[node.localName] + self.pos = [(self.posx, self.posy, align, utils.text_get(node), style.get('td'), style.font_size_get('td'))] + + def tag_start(self): + self.pos.sort() + res = '<table border="0" cellpadding="0" cellspacing="0"><tr>' + posx = 0 + i = 0 + for (x,y,align,txt, style, fs) in self.pos: + if align=="left": + pos2 = len(txt)*fs + res+='<td width="%d"></td><td style="%s" width="%d">%s</td>' % (x - posx, style, pos2, txt) + posx = x+pos2 + if align=="right": + res+='<td width="%d" align="right" style="%s">%s</td>' % (x - posx, style, txt) + posx = x + if align=="center": + res+='<td width="%d" align="center" style="%s">%s</td>' % ((x - posx)*2, style, txt) + posx = 2*x-posx + i+=1 + res+='</tr></table>' + return res + def merge(self, ds): + self.pos+=ds.pos class _rml_tmpl_draw_lines(_rml_tmpl_tag): - def __init__(self, node, style): - coord = [utils.unit_get(x) for x in utils.text_get(node).split(' ')] - self.ok = False - self.posx = coord[0] - self.posy = coord[1] - self.width = coord[2]-coord[0] - self.ok = coord[1]==coord[3] - self.style = style - self.style = style.get('hr') - - def tag_start(self): - if self.ok: - return '<table border="0" cellpadding="0" cellspacing="0" width="%d"><tr><td width="%d"></td><td><hr width="100%%" style="margin:0px; %s"></td></tr></table>' % (self.posx+self.width,self.posx,self.style) - else: - return '' + def __init__(self, node, style): + coord = [utils.unit_get(x) for x in utils.text_get(node).split(' ')] + self.ok = False + self.posx = coord[0] + self.posy = coord[1] + self.width = coord[2]-coord[0] + self.ok = coord[1]==coord[3] + self.style = style + self.style = style.get('hr') + + def tag_start(self): + if self.ok: + return '<table border="0" cellpadding="0" cellspacing="0" width="%d"><tr><td width="%d"></td><td><hr width="100%%" style="margin:0px; %s"></td></tr></table>' % (self.posx+self.width,self.posx,self.style) + else: + return '' class _rml_stylesheet(object): - def __init__(self, stylesheet, doc): - self.doc = doc - self.attrs = {} - self._tags = { - 'fontSize': lambda x: ('font-size',str(utils.unit_get(x))+'px'), - 'alignment': lambda x: ('text-align',str(x)) - } - result = '' - for ps in stylesheet.getElementsByTagName('paraStyle'): - attr = {} - attrs = ps.attributes - for i in range(attrs.length): - name = attrs.item(i).localName - attr[name] = ps.getAttribute(name) - attrs = [] - for a in attr: - if a in self._tags: - attrs.append("%s:%s" % self._tags[a](attr[a])) - if len(attrs): - result += "p."+attr['name']+" {"+'; '.join(attrs)+"}\n" - self.result = result - - def render(self): - return self.result + def __init__(self, stylesheet, doc): + self.doc = doc + self.attrs = {} + self._tags = { + 'fontSize': lambda x: ('font-size',str(utils.unit_get(x))+'px'), + 'alignment': lambda x: ('text-align',str(x)) + } + result = '' + for ps in stylesheet.getElementsByTagName('paraStyle'): + attr = {} + attrs = ps.attributes + for i in range(attrs.length): + name = attrs.item(i).localName + attr[name] = ps.getAttribute(name) + attrs = [] + for a in attr: + if a in self._tags: + attrs.append("%s:%s" % self._tags[a](attr[a])) + if len(attrs): + result += "p."+attr['name']+" {"+'; '.join(attrs)+"}\n" + self.result = result + + def render(self): + return self.result class _rml_draw_style(object): - def __init__(self): - self.style = {} - self._styles = { - 'fill': lambda x: {'td': {'color':x.getAttribute('color')}}, - 'setFont': lambda x: {'td': {'font-size':x.getAttribute('size')+'px'}}, - 'stroke': lambda x: {'hr': {'color':x.getAttribute('color')}}, - } - def update(self, node): - if node.localName in self._styles: - result = self._styles[node.localName](node) - for key in result: - if key in self.style: - self.style[key].update(result[key]) - else: - self.style[key] = result[key] - def font_size_get(self,tag): - size = utils.unit_get(self.style.get('td', {}).get('font-size','16')) - return size - - def get(self,tag): - if not tag in self.style: - return "" - return ';'.join(['%s:%s' % (x[0],x[1]) for x in self.style[tag].items()]) + def __init__(self): + self.style = {} + self._styles = { + 'fill': lambda x: {'td': {'color':x.getAttribute('color')}}, + 'setFont': lambda x: {'td': {'font-size':x.getAttribute('size')+'px'}}, + 'stroke': lambda x: {'hr': {'color':x.getAttribute('color')}}, + } + def update(self, node): + if node.localName in self._styles: + result = self._styles[node.localName](node) + for key in result: + if key in self.style: + self.style[key].update(result[key]) + else: + self.style[key] = result[key] + def font_size_get(self,tag): + size = utils.unit_get(self.style.get('td', {}).get('font-size','16')) + return size + + def get(self,tag): + if not tag in self.style: + return "" + return ';'.join(['%s:%s' % (x[0],x[1]) for x in self.style[tag].items()]) class _rml_template(object): - def __init__(self, template): - self.frame_pos = -1 - self.frames = [] - self.template_order = [] - self.page_template = {} - self.loop = 0 - self._tags = { - 'drawString': _rml_tmpl_draw_string, - 'drawRightString': _rml_tmpl_draw_string, - 'drawCentredString': _rml_tmpl_draw_string, - 'lines': _rml_tmpl_draw_lines - } - self.style = _rml_draw_style() - for pt in template.getElementsByTagName('pageTemplate'): - frames = {} - id = pt.getAttribute('id') - self.template_order.append(id) - for tmpl in pt.getElementsByTagName('frame'): - posy = int(utils.unit_get(tmpl.getAttribute('y1'))) #+utils.unit_get(tmpl.getAttribute('height'))) - posx = int(utils.unit_get(tmpl.getAttribute('x1'))) - frames[(posy,posx,tmpl.getAttribute('id'))] = _rml_tmpl_frame(posx, utils.unit_get(tmpl.getAttribute('width'))) - for tmpl in template.getElementsByTagName('pageGraphics'): - for n in tmpl.childNodes: - if n.nodeType==n.ELEMENT_NODE: - if n.localName in self._tags: - t = self._tags[n.localName](n, self.style) - frames[(t.posy,t.posx,n.localName)] = t - else: - self.style.update(n) - keys = frames.keys() - keys.sort() - keys.reverse() - self.page_template[id] = [] - for key in range(len(keys)): - if key>0 and keys[key-1][0] == keys[key][0]: - if type(self.page_template[id][-1]) == type(frames[keys[key]]): - if self.page_template[id][-1].tag_mergeable(): - self.page_template[id][-1].merge(frames[keys[key]]) - continue - self.page_template[id].append(frames[keys[key]]) - self.template = self.template_order[0] - - def _get_style(self): - return self.style - - def set_next_template(self): - self.template = self.template_order[(self.template_order.index(name)+1) % self.template_order] - self.frame_pos = -1 - - def set_template(self, name): - self.template = name - self.frame_pos = -1 - - def frame_start(self): - result = '' - frames = self.page_template[self.template] - ok = True - while ok: - self.frame_pos += 1 - if self.frame_pos>=len(frames): - self.frame_pos=0 - self.loop=1 - ok = False - continue - f = frames[self.frame_pos] - result+=f.tag_start() - ok = not f.tag_end() - if ok: - result+=f.tag_stop() - return result - - def frame_stop(self): - frames = self.page_template[self.template] - f = frames[self.frame_pos] - result=f.tag_stop() - return result - - def start(self): - return '' - - def end(self): - result = '' - while not self.loop: - result += self.frame_start() - result += self.frame_stop() - return result + def __init__(self, template): + self.frame_pos = -1 + self.frames = [] + self.template_order = [] + self.page_template = {} + self.loop = 0 + self._tags = { + 'drawString': _rml_tmpl_draw_string, + 'drawRightString': _rml_tmpl_draw_string, + 'drawCentredString': _rml_tmpl_draw_string, + 'lines': _rml_tmpl_draw_lines + } + self.style = _rml_draw_style() + for pt in template.getElementsByTagName('pageTemplate'): + frames = {} + id = pt.getAttribute('id') + self.template_order.append(id) + for tmpl in pt.getElementsByTagName('frame'): + posy = int(utils.unit_get(tmpl.getAttribute('y1'))) #+utils.unit_get(tmpl.getAttribute('height'))) + posx = int(utils.unit_get(tmpl.getAttribute('x1'))) + frames[(posy,posx,tmpl.getAttribute('id'))] = _rml_tmpl_frame(posx, utils.unit_get(tmpl.getAttribute('width'))) + for tmpl in template.getElementsByTagName('pageGraphics'): + for n in tmpl.childNodes: + if n.nodeType==n.ELEMENT_NODE: + if n.localName in self._tags: + t = self._tags[n.localName](n, self.style) + frames[(t.posy,t.posx,n.localName)] = t + else: + self.style.update(n) + keys = frames.keys() + keys.sort() + keys.reverse() + self.page_template[id] = [] + for key in range(len(keys)): + if key>0 and keys[key-1][0] == keys[key][0]: + if type(self.page_template[id][-1]) == type(frames[keys[key]]): + if self.page_template[id][-1].tag_mergeable(): + self.page_template[id][-1].merge(frames[keys[key]]) + continue + self.page_template[id].append(frames[keys[key]]) + self.template = self.template_order[0] + + def _get_style(self): + return self.style + + def set_next_template(self): + self.template = self.template_order[(self.template_order.index(name)+1) % self.template_order] + self.frame_pos = -1 + + def set_template(self, name): + self.template = name + self.frame_pos = -1 + + def frame_start(self): + result = '' + frames = self.page_template[self.template] + ok = True + while ok: + self.frame_pos += 1 + if self.frame_pos>=len(frames): + self.frame_pos=0 + self.loop=1 + ok = False + continue + f = frames[self.frame_pos] + result+=f.tag_start() + ok = not f.tag_end() + if ok: + result+=f.tag_stop() + return result + + def frame_stop(self): + frames = self.page_template[self.template] + f = frames[self.frame_pos] + result=f.tag_stop() + return result + + def start(self): + return '' + + def end(self): + result = '' + while not self.loop: + result += self.frame_start() + result += self.frame_stop() + return result class _rml_doc(object): - def __init__(self, data): - self.dom = xml.dom.minidom.parseString(data) - self.filename = self.dom.documentElement.getAttribute('filename') - self.result = '' + def __init__(self, data): + self.dom = xml.dom.minidom.parseString(data) + self.filename = self.dom.documentElement.getAttribute('filename') + self.result = '' - def render(self, out): - self.result += '''<!DOCTYPE HTML PUBLIC "-//w3c//DTD HTML 4.0 Frameset//EN"> + def render(self, out): + self.result += '''<!DOCTYPE HTML PUBLIC "-//w3c//DTD HTML 4.0 Frameset//EN"> <html> <head> - <style type="text/css"> - p {margin:0px; font-size:12px;} - td {font-size:14px;} + <style type="text/css"> + p {margin:0px; font-size:12px;} + td {font-size:14px;} ''' - style = self.dom.documentElement.getElementsByTagName('stylesheet')[0] - s = _rml_stylesheet(style, self.dom) - self.result += s.render() - self.result+=''' - </style> + style = self.dom.documentElement.getElementsByTagName('stylesheet')[0] + s = _rml_stylesheet(style, self.dom) + self.result += s.render() + self.result+=''' + </style> </head> <body>''' - template = _rml_template(self.dom.documentElement.getElementsByTagName('template')[0]) - f = _flowable(template, self.dom) - self.result += f.render(self.dom.documentElement.getElementsByTagName('story')[0]) - del f - self.result += '</body></html>' - out.write( self.result) + template = _rml_template(self.dom.documentElement.getElementsByTagName('template')[0]) + f = _flowable(template, self.dom) + self.result += f.render(self.dom.documentElement.getElementsByTagName('story')[0]) + del f + self.result += '</body></html>' + out.write( self.result) def parseString(data, fout=None): - r = _rml_doc(data) - if fout: - fp = file(fout,'wb') - r.render(fp) - fp.close() - return fout - else: - fp = StringIO.StringIO() - r.render(fp) - return fp.getvalue() + r = _rml_doc(data) + if fout: + fp = file(fout,'wb') + r.render(fp) + fp.close() + return fout + else: + fp = StringIO.StringIO() + r.render(fp) + return fp.getvalue() def trml2pdf_help(): - print 'Usage: rml2html input.rml >output.html' - print 'Render the standard input (RML) and output an HTML file' - sys.exit(0) + print 'Usage: rml2html input.rml >output.html' + print 'Render the standard input (RML) and output an HTML file' + sys.exit(0) if __name__=="__main__": - if len(sys.argv)>1: - if sys.argv[1]=='--help': - trml2pdf_help() - print parseString(file(sys.argv[1], 'r').read()), - else: - print 'Usage: trml2pdf input.rml >output.pdf' - print 'Try \'trml2pdf --help\' for more information.' + if len(sys.argv)>1: + if sys.argv[1]=='--help': + trml2pdf_help() + print parseString(file(sys.argv[1], 'r').read()), + else: + print 'Usage: trml2pdf input.rml >output.pdf' + print 'Try \'trml2pdf --help\' for more information.' diff --git a/bin/report/render/rml2html/utils.py b/bin/report/render/rml2html/utils.py index ad3a74445b8..2cef5ce80b2 100644 --- a/bin/report/render/rml2html/utils.py +++ b/bin/report/render/rml2html/utils.py @@ -47,48 +47,48 @@ import reportlab import reportlab.lib.units def text_get(node): - rc = '' - for node in node.childNodes: - if node.nodeType == node.TEXT_NODE: - rc = rc + node.data - return rc + rc = '' + for node in node.childNodes: + if node.nodeType == node.TEXT_NODE: + rc = rc + node.data + return rc units = [ - (re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch), - (re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm), - (re.compile('^(-?[0-9\.]+)\s*mm$'), reportlab.lib.units.mm), - (re.compile('^(-?[0-9\.]+)\s*px$'), 0.7), - (re.compile('^(-?[0-9\.]+)\s*$'), 1) + (re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch), + (re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm), + (re.compile('^(-?[0-9\.]+)\s*mm$'), reportlab.lib.units.mm), + (re.compile('^(-?[0-9\.]+)\s*px$'), 0.7), + (re.compile('^(-?[0-9\.]+)\s*$'), 1) ] def unit_get(size): - global units - for unit in units: - res = unit[0].search(size, 0) - if res: - return int(unit[1]*float(res.group(1))*1.3) - return False + global units + for unit in units: + res = unit[0].search(size, 0) + if res: + return int(unit[1]*float(res.group(1))*1.3) + return False def tuple_int_get(node, attr_name, default=None): - if not node.hasAttribute(attr_name): - return default - res = [int(x) for x in node.getAttribute(attr_name).split(',')] - return res + if not node.hasAttribute(attr_name): + return default + res = [int(x) for x in node.getAttribute(attr_name).split(',')] + return res def bool_get(value): - return (str(value)=="1") or (value.lower()=='yes') + return (str(value)=="1") or (value.lower()=='yes') def attr_get(node, attrs, dict={}): - res = {} - for name in attrs: - if node.hasAttribute(name): - res[name] = unit_get(node.getAttribute(name)) - for key in dict: - if node.hasAttribute(key): - if dict[key]=='str': - res[key] = str(node.getAttribute(key)) - elif dict[key]=='bool': - res[key] = bool_get(node.getAttribute(key)) - elif dict[key]=='int': - res[key] = int(node.getAttribute(key)) - return res + res = {} + for name in attrs: + if node.hasAttribute(name): + res[name] = unit_get(node.getAttribute(name)) + for key in dict: + if node.hasAttribute(key): + if dict[key]=='str': + res[key] = str(node.getAttribute(key)) + elif dict[key]=='bool': + res[key] = bool_get(node.getAttribute(key)) + elif dict[key]=='int': + res[key] = int(node.getAttribute(key)) + return res diff --git a/bin/report/render/rml2pdf/color.py b/bin/report/render/rml2pdf/color.py index 0e50e9f76f5..6cb772999a2 100644 --- a/bin/report/render/rml2pdf/color.py +++ b/bin/report/render/rml2pdf/color.py @@ -51,13 +51,13 @@ regex_t = re.compile('\(([0-9\.]*),([0-9\.]*),([0-9\.]*)\)') regex_h = re.compile('#([0-9a-zA-Z][0-9a-zA-Z])([0-9a-zA-Z][0-9a-zA-Z])([0-9a-zA-Z][0-9a-zA-Z])') def get(col_str): - global allcols - if col_str in allcols.keys(): - return allcols[col_str] - res = regex_t.search(col_str, 0) - if res: - return (float(res.group(1)),float(res.group(2)),float(res.group(3))) - res = regex_h.search(col_str, 0) - if res: - return tuple([ float(int(res.group(i),16))/255 for i in range(1,4)]) - return colors.red + global allcols + if col_str in allcols.keys(): + return allcols[col_str] + res = regex_t.search(col_str, 0) + if res: + return (float(res.group(1)),float(res.group(2)),float(res.group(3))) + res = regex_h.search(col_str, 0) + if res: + return tuple([ float(int(res.group(i),16))/255 for i in range(1,4)]) + return colors.red diff --git a/bin/report/render/rml2pdf/trml2pdf.py b/bin/report/render/rml2pdf/trml2pdf.py index 1aef11888e4..555a2769e04 100755..100644 --- a/bin/report/render/rml2pdf/trml2pdf.py +++ b/bin/report/render/rml2pdf/trml2pdf.py @@ -68,740 +68,740 @@ import os encoding = 'utf-8' def str2xml(s): - return s.replace('&', '&').replace('<', '<').replace('>', '>') + return s.replace('&', '&').replace('<', '<').replace('>', '>') def _child_get(node, childs): - clds = [] - for n in node.childNodes: - if (n.nodeType==n.ELEMENT_NODE) and (n.localName==childs): - clds.append(n) - return clds + clds = [] + for n in node.childNodes: + if (n.nodeType==n.ELEMENT_NODE) and (n.localName==childs): + clds.append(n) + return clds class PageCount(platypus.Flowable): - def draw(self): - self.canv.beginForm("pageCount") - self.canv.setFont("Helvetica", utils.unit_get(str(8))) - self.canv.drawString(0, 0, str(self.canv.getPageNumber())) - self.canv.endForm() + def draw(self): + self.canv.beginForm("pageCount") + self.canv.setFont("Helvetica", utils.unit_get(str(8))) + self.canv.drawString(0, 0, str(self.canv.getPageNumber())) + self.canv.endForm() class PageReset(platypus.Flowable): - def draw(self): - self.canv._pageNumber = 0 + def draw(self): + self.canv._pageNumber = 0 class _rml_styles(object): - def __init__(self, nodes): - self.styles = {} - self.names = {} - self.table_styles = {} - for node in nodes: - for style in node.getElementsByTagName('blockTableStyle'): - self.table_styles[style.getAttribute('id')] = self._table_style_get(style) - for style in node.getElementsByTagName('paraStyle'): - self.styles[style.getAttribute('name')] = self._para_style_update(style) - for variable in node.getElementsByTagName('initialize'): - for name in variable.getElementsByTagName('name'): - self.names[ name.getAttribute('id')] = name.getAttribute('value') - - def _para_style_update(self, node): - data = {} - for attr in ['textColor', 'backColor', 'bulletColor', 'borderColor']: - if node.hasAttribute(attr): - data[attr] = color.get(node.getAttribute(attr)) - for attr in ['fontName', 'bulletFontName', 'bulletText']: - if node.hasAttribute(attr): - data[attr] = node.getAttribute(attr) - for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter', - 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading', - 'borderWidth','borderPadding','borderRadius']: - if node.hasAttribute(attr): - data[attr] = utils.unit_get(node.getAttribute(attr)) - if node.hasAttribute('alignment'): - align = { - 'right':reportlab.lib.enums.TA_RIGHT, - 'center':reportlab.lib.enums.TA_CENTER, - 'justify':reportlab.lib.enums.TA_JUSTIFY - } - data['alignment'] = align.get(node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT) - return data - - def _table_style_get(self, style_node): - styles = [] - for node in style_node.childNodes: - if node.nodeType==node.ELEMENT_NODE: - start = utils.tuple_int_get(node, 'start', (0,0) ) - stop = utils.tuple_int_get(node, 'stop', (-1,-1) ) - if node.localName=='blockValign': - styles.append(('VALIGN', start, stop, str(node.getAttribute('value')))) - elif node.localName=='blockFont': - styles.append(('FONT', start, stop, str(node.getAttribute('name')))) - elif node.localName=='blockTextColor': - styles.append(('TEXTCOLOR', start, stop, color.get(str(node.getAttribute('colorName'))))) - elif node.localName=='blockLeading': - styles.append(('LEADING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockAlignment': - styles.append(('ALIGNMENT', start, stop, str(node.getAttribute('value')))) - elif node.localName=='blockSpan': - styles.append(('SPAN', start, stop)) - elif node.localName=='blockLeftPadding': - styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockRightPadding': - styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockTopPadding': - styles.append(('TOPPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockBottomPadding': - styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) - elif node.localName=='blockBackground': - styles.append(('BACKGROUND', start, stop, color.get(node.getAttribute('colorName')))) - if node.hasAttribute('size'): - styles.append(('FONTSIZE', start, stop, utils.unit_get(node.getAttribute('size')))) - elif node.localName=='lineStyle': - kind = node.getAttribute('kind') - kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ] - assert kind in kind_list - thick = 1 - if node.hasAttribute('thickness'): - thick = float(node.getAttribute('thickness')) - styles.append((kind, start, stop, thick, color.get(node.getAttribute('colorName')))) - return platypus.tables.TableStyle(styles) - - def para_style_get(self, node): - style = False - if node.hasAttribute('style'): - if node.getAttribute('style') in self.styles: - styles = reportlab.lib.styles.getSampleStyleSheet() - sname = node.getAttribute('style') - style = reportlab.lib.styles.ParagraphStyle(sname, styles["Normal"], **self.styles[sname]) - else: - sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),) ) - if not style: - styles = reportlab.lib.styles.getSampleStyleSheet() - style = copy.deepcopy(styles['Normal']) - style.__dict__.update(self._para_style_update(node)) - return style + def __init__(self, nodes): + self.styles = {} + self.names = {} + self.table_styles = {} + for node in nodes: + for style in node.getElementsByTagName('blockTableStyle'): + self.table_styles[style.getAttribute('id')] = self._table_style_get(style) + for style in node.getElementsByTagName('paraStyle'): + self.styles[style.getAttribute('name')] = self._para_style_update(style) + for variable in node.getElementsByTagName('initialize'): + for name in variable.getElementsByTagName('name'): + self.names[ name.getAttribute('id')] = name.getAttribute('value') + + def _para_style_update(self, node): + data = {} + for attr in ['textColor', 'backColor', 'bulletColor', 'borderColor']: + if node.hasAttribute(attr): + data[attr] = color.get(node.getAttribute(attr)) + for attr in ['fontName', 'bulletFontName', 'bulletText']: + if node.hasAttribute(attr): + data[attr] = node.getAttribute(attr) + for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter', + 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading', + 'borderWidth','borderPadding','borderRadius']: + if node.hasAttribute(attr): + data[attr] = utils.unit_get(node.getAttribute(attr)) + if node.hasAttribute('alignment'): + align = { + 'right':reportlab.lib.enums.TA_RIGHT, + 'center':reportlab.lib.enums.TA_CENTER, + 'justify':reportlab.lib.enums.TA_JUSTIFY + } + data['alignment'] = align.get(node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT) + return data + + def _table_style_get(self, style_node): + styles = [] + for node in style_node.childNodes: + if node.nodeType==node.ELEMENT_NODE: + start = utils.tuple_int_get(node, 'start', (0,0) ) + stop = utils.tuple_int_get(node, 'stop', (-1,-1) ) + if node.localName=='blockValign': + styles.append(('VALIGN', start, stop, str(node.getAttribute('value')))) + elif node.localName=='blockFont': + styles.append(('FONT', start, stop, str(node.getAttribute('name')))) + elif node.localName=='blockTextColor': + styles.append(('TEXTCOLOR', start, stop, color.get(str(node.getAttribute('colorName'))))) + elif node.localName=='blockLeading': + styles.append(('LEADING', start, stop, utils.unit_get(node.getAttribute('length')))) + elif node.localName=='blockAlignment': + styles.append(('ALIGNMENT', start, stop, str(node.getAttribute('value')))) + elif node.localName=='blockSpan': + styles.append(('SPAN', start, stop)) + elif node.localName=='blockLeftPadding': + styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) + elif node.localName=='blockRightPadding': + styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) + elif node.localName=='blockTopPadding': + styles.append(('TOPPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) + elif node.localName=='blockBottomPadding': + styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.getAttribute('length')))) + elif node.localName=='blockBackground': + styles.append(('BACKGROUND', start, stop, color.get(node.getAttribute('colorName')))) + if node.hasAttribute('size'): + styles.append(('FONTSIZE', start, stop, utils.unit_get(node.getAttribute('size')))) + elif node.localName=='lineStyle': + kind = node.getAttribute('kind') + kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ] + assert kind in kind_list + thick = 1 + if node.hasAttribute('thickness'): + thick = float(node.getAttribute('thickness')) + styles.append((kind, start, stop, thick, color.get(node.getAttribute('colorName')))) + return platypus.tables.TableStyle(styles) + + def para_style_get(self, node): + style = False + if node.hasAttribute('style'): + if node.getAttribute('style') in self.styles: + styles = reportlab.lib.styles.getSampleStyleSheet() + sname = node.getAttribute('style') + style = reportlab.lib.styles.ParagraphStyle(sname, styles["Normal"], **self.styles[sname]) + else: + sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),) ) + if not style: + styles = reportlab.lib.styles.getSampleStyleSheet() + style = copy.deepcopy(styles['Normal']) + style.__dict__.update(self._para_style_update(node)) + return style class _rml_doc(object): - def __init__(self, data, images={}, path='.'): - self.dom = xml.dom.minidom.parseString(data) - self.filename = self.dom.documentElement.getAttribute('filename') - self.images = images - self.path = path - - def docinit(self, els): - from reportlab.lib.fonts import addMapping - from reportlab.pdfbase import pdfmetrics - from reportlab.pdfbase.ttfonts import TTFont - - for node in els: - for font in node.getElementsByTagName('registerFont'): - name = font.getAttribute('fontName').encode('ascii') - fname = font.getAttribute('fontFile').encode('ascii') - pdfmetrics.registerFont(TTFont(name, fname )) - addMapping(name, 0, 0, name) #normal - addMapping(name, 0, 1, name) #italic - addMapping(name, 1, 0, name) #bold - addMapping(name, 1, 1, name) #italic and bold - - def _textual_image(self, node): - import base64 - rc = '' - for n in node.childNodes: - if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): - rc += n.data - return base64.decodestring(rc) - - def _images(self, el): - result = {} - for node in el.getElementsByTagName('image'): - result[node.getAttribute('name')] = self._textual_image(node) - return result - - def render(self, out): - el = self.dom.documentElement.getElementsByTagName('docinit') - if el: - self.docinit(el) - - el = self.dom.documentElement.getElementsByTagName('stylesheet') - self.styles = _rml_styles(el) - - el = self.dom.documentElement.getElementsByTagName('images') - if el: - self.images.update( self._images(el[0]) ) - - el = self.dom.documentElement.getElementsByTagName('template') - if len(el): - pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path) - pt_obj.render(self.dom.documentElement.getElementsByTagName('story')) - else: - self.canvas = canvas.Canvas(out) - pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0] - pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path) - pd_obj.render(pd) - - self.canvas.showPage() - self.canvas.save() + def __init__(self, data, images={}, path='.'): + self.dom = xml.dom.minidom.parseString(data) + self.filename = self.dom.documentElement.getAttribute('filename') + self.images = images + self.path = path + + def docinit(self, els): + from reportlab.lib.fonts import addMapping + from reportlab.pdfbase import pdfmetrics + from reportlab.pdfbase.ttfonts import TTFont + + for node in els: + for font in node.getElementsByTagName('registerFont'): + name = font.getAttribute('fontName').encode('ascii') + fname = font.getAttribute('fontFile').encode('ascii') + pdfmetrics.registerFont(TTFont(name, fname )) + addMapping(name, 0, 0, name) #normal + addMapping(name, 0, 1, name) #italic + addMapping(name, 1, 0, name) #bold + addMapping(name, 1, 1, name) #italic and bold + + def _textual_image(self, node): + import base64 + rc = '' + for n in node.childNodes: + if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): + rc += n.data + return base64.decodestring(rc) + + def _images(self, el): + result = {} + for node in el.getElementsByTagName('image'): + result[node.getAttribute('name')] = self._textual_image(node) + return result + + def render(self, out): + el = self.dom.documentElement.getElementsByTagName('docinit') + if el: + self.docinit(el) + + el = self.dom.documentElement.getElementsByTagName('stylesheet') + self.styles = _rml_styles(el) + + el = self.dom.documentElement.getElementsByTagName('images') + if el: + self.images.update( self._images(el[0]) ) + + el = self.dom.documentElement.getElementsByTagName('template') + if len(el): + pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path) + pt_obj.render(self.dom.documentElement.getElementsByTagName('story')) + else: + self.canvas = canvas.Canvas(out) + pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0] + pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path) + pd_obj.render(pd) + + self.canvas.showPage() + self.canvas.save() class _rml_canvas(object): - def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.'): - self.canvas = canvas - self.styles = doc.styles - self.doc_tmpl = doc_tmpl - self.doc = doc - self.images = images - self.path = path - - def _textual(self, node, x=0, y=0): - rc = '' - for n in node.childNodes: - if n.nodeType == n.ELEMENT_NODE: - if n.localName == 'seq': - from reportlab.lib.sequencer import getSequencer - seq = getSequencer() - rc += str(seq.next(n.getAttribute('id'))) - if n.localName == 'pageCount': - if x or y: - self.canvas.translate(x,y) - self.canvas.doForm('pageCount') - if x or y: - self.canvas.translate(-x,-y) - if n.localName == 'pageNumber': - rc += str(self.canvas.getPageNumber()) - elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): - # this doesn't need to be "entities" encoded like flowables need to - rc += n.data - return rc.encode(encoding, 'replace') - - def _drawString(self, node): - v = utils.attr_get(node, ['x','y']) - self.canvas.drawString(text=self._textual(node, **v), **v) - def _drawCenteredString(self, node): - v = utils.attr_get(node, ['x','y']) - self.canvas.drawCentredString(text=self._textual(node, **v), **v) - def _drawRightString(self, node): - v = utils.attr_get(node, ['x','y']) - self.canvas.drawRightString(text=self._textual(node, **v), **v) - def _rect(self, node): - if node.hasAttribute('round'): - self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) - else: - self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) - - def _ellipse(self, node): - x1 = utils.unit_get(node.getAttribute('x')) - x2 = utils.unit_get(node.getAttribute('width')) - y1 = utils.unit_get(node.getAttribute('y')) - y2 = utils.unit_get(node.getAttribute('height')) - self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) - def _curves(self, node): - line_str = utils.text_get(node).split() - lines = [] - while len(line_str)>7: - self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]]) - line_str = line_str[8:] - def _lines(self, node): - line_str = utils.text_get(node).split() - lines = [] - while len(line_str)>3: - lines.append([utils.unit_get(l) for l in line_str[0:4]]) - line_str = line_str[4:] - self.canvas.lines(lines) - def _grid(self, node): - xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')] - ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')] - self.canvas.grid(xlist, ylist) - def _translate(self, node): - dx = 0 - dy = 0 - if node.hasAttribute('dx'): - dx = utils.unit_get(node.getAttribute('dx')) - if node.hasAttribute('dy'): - dy = utils.unit_get(node.getAttribute('dy')) - self.canvas.translate(dx,dy) - - def _circle(self, node): - self.canvas.circle(x_cen=utils.unit_get(node.getAttribute('x')), y_cen=utils.unit_get(node.getAttribute('y')), r=utils.unit_get(node.getAttribute('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) - - def _place(self, node): - flows = _rml_flowable(self.doc, images=self.images, path=self.path).render(node) - infos = utils.attr_get(node, ['x','y','width','height']) - - infos['y']+=infos['height'] - for flow in flows: - w,h = flow.wrap(infos['width'], infos['height']) - if w<=infos['width'] and h<=infos['height']: - infos['y']-=h - flow.drawOn(self.canvas,infos['x'],infos['y']) - infos['height']-=h - else: - raise ValueError, "Not enough space" - - def _line_mode(self, node): - ljoin = {'round':1, 'mitered':0, 'bevelled':2} - lcap = {'default':0, 'round':1, 'square':2} - if node.hasAttribute('width'): - self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width'))) - if node.hasAttribute('join'): - self.canvas.setLineJoin(ljoin[node.getAttribute('join')]) - if node.hasAttribute('cap'): - self.canvas.setLineCap(lcap[node.getAttribute('cap')]) - if node.hasAttribute('miterLimit'): - self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit'))) - if node.hasAttribute('dash'): - dashes = node.getAttribute('dash').split(',') - for x in range(len(dashes)): - dashes[x]=utils.unit_get(dashes[x]) - self.canvas.setDash(node.getAttribute('dash').split(',')) - - def _image(self, node): - import urllib - from reportlab.lib.utils import ImageReader - - if not node.hasAttribute('file'): - - if node.hasAttribute('name'): - image_data = self.images[node.getAttribute('name')] - s = StringIO.StringIO(image_data) - else: - import base64 - image_data = base64.decodestring(node.firstChild.nodeValue) - if not image_data: return False - s = StringIO.StringIO(image_data) - else: - if node.getAttribute('file') in self.images: - s = StringIO.StringIO(self.images[node.getAttribute('file')]) - else: - try: - u = urllib.urlopen(str(node.getAttribute('file'))) - s = StringIO.StringIO(u.read()) - except: - u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb') - s = StringIO.StringIO(u.read()) - img = ImageReader(s) - (sx,sy) = img.getSize() - - args = {} - for tag in ('width','height','x','y'): - if node.hasAttribute(tag): - args[tag] = utils.unit_get(node.getAttribute(tag)) - if ('width' in args) and (not 'height' in args): - args['height'] = sy * args['width'] / sx - elif ('height' in args) and (not 'width' in args): - args['width'] = sx * args['height'] / sy - elif ('width' in args) and ('height' in args): - if (float(args['width'])/args['height'])>(float(sx)>sy): - args['width'] = sx * args['height'] / sy - else: - args['height'] = sy * args['width'] / sx - self.canvas.drawImage(img, **args) - - def _path(self, node): - self.path = self.canvas.beginPath() - self.path.moveTo(**utils.attr_get(node, ['x','y'])) - for n in node.childNodes: - if n.nodeType == node.ELEMENT_NODE: - if n.localName=='moveto': - vals = utils.text_get(n).split() - self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1])) - elif n.localName=='curvesto': - vals = utils.text_get(n).split() - while len(vals)>5: - pos=[] - while len(pos)<6: - pos.append(utils.unit_get(vals.pop(0))) - self.path.curveTo(*pos) - elif (n.nodeType == node.TEXT_NODE): - data = n.data.split() # Not sure if I must merge all TEXT_NODE ? - while len(data)>1: - x = utils.unit_get(data.pop(0)) - y = utils.unit_get(data.pop(0)) - self.path.lineTo(x,y) - if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')): - self.path.close() - self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) - - def render(self, node): - tags = { - 'drawCentredString': self._drawCenteredString, - 'drawRightString': self._drawRightString, - 'drawString': self._drawString, - 'rect': self._rect, - 'ellipse': self._ellipse, - 'lines': self._lines, - 'grid': self._grid, - 'curves': self._curves, - 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))), - 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))), - 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))), - 'place': self._place, - 'circle': self._circle, - 'lineMode': self._line_mode, - 'path': self._path, - 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))), - 'translate': self._translate, - 'image': self._image - } - for nd in node.childNodes: - if nd.nodeType==nd.ELEMENT_NODE: - for tag in tags: - if nd.localName==tag: - tags[tag](nd) - break + def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.'): + self.canvas = canvas + self.styles = doc.styles + self.doc_tmpl = doc_tmpl + self.doc = doc + self.images = images + self.path = path + + def _textual(self, node, x=0, y=0): + rc = '' + for n in node.childNodes: + if n.nodeType == n.ELEMENT_NODE: + if n.localName == 'seq': + from reportlab.lib.sequencer import getSequencer + seq = getSequencer() + rc += str(seq.next(n.getAttribute('id'))) + if n.localName == 'pageCount': + if x or y: + self.canvas.translate(x,y) + self.canvas.doForm('pageCount') + if x or y: + self.canvas.translate(-x,-y) + if n.localName == 'pageNumber': + rc += str(self.canvas.getPageNumber()) + elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): + # this doesn't need to be "entities" encoded like flowables need to + rc += n.data + return rc.encode(encoding, 'replace') + + def _drawString(self, node): + v = utils.attr_get(node, ['x','y']) + self.canvas.drawString(text=self._textual(node, **v), **v) + def _drawCenteredString(self, node): + v = utils.attr_get(node, ['x','y']) + self.canvas.drawCentredString(text=self._textual(node, **v), **v) + def _drawRightString(self, node): + v = utils.attr_get(node, ['x','y']) + self.canvas.drawRightString(text=self._textual(node, **v), **v) + def _rect(self, node): + if node.hasAttribute('round'): + self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) + else: + self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'})) + + def _ellipse(self, node): + x1 = utils.unit_get(node.getAttribute('x')) + x2 = utils.unit_get(node.getAttribute('width')) + y1 = utils.unit_get(node.getAttribute('y')) + y2 = utils.unit_get(node.getAttribute('height')) + self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) + def _curves(self, node): + line_str = utils.text_get(node).split() + lines = [] + while len(line_str)>7: + self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]]) + line_str = line_str[8:] + def _lines(self, node): + line_str = utils.text_get(node).split() + lines = [] + while len(line_str)>3: + lines.append([utils.unit_get(l) for l in line_str[0:4]]) + line_str = line_str[4:] + self.canvas.lines(lines) + def _grid(self, node): + xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')] + ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')] + self.canvas.grid(xlist, ylist) + def _translate(self, node): + dx = 0 + dy = 0 + if node.hasAttribute('dx'): + dx = utils.unit_get(node.getAttribute('dx')) + if node.hasAttribute('dy'): + dy = utils.unit_get(node.getAttribute('dy')) + self.canvas.translate(dx,dy) + + def _circle(self, node): + self.canvas.circle(x_cen=utils.unit_get(node.getAttribute('x')), y_cen=utils.unit_get(node.getAttribute('y')), r=utils.unit_get(node.getAttribute('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) + + def _place(self, node): + flows = _rml_flowable(self.doc, images=self.images, path=self.path).render(node) + infos = utils.attr_get(node, ['x','y','width','height']) + + infos['y']+=infos['height'] + for flow in flows: + w,h = flow.wrap(infos['width'], infos['height']) + if w<=infos['width'] and h<=infos['height']: + infos['y']-=h + flow.drawOn(self.canvas,infos['x'],infos['y']) + infos['height']-=h + else: + raise ValueError, "Not enough space" + + def _line_mode(self, node): + ljoin = {'round':1, 'mitered':0, 'bevelled':2} + lcap = {'default':0, 'round':1, 'square':2} + if node.hasAttribute('width'): + self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width'))) + if node.hasAttribute('join'): + self.canvas.setLineJoin(ljoin[node.getAttribute('join')]) + if node.hasAttribute('cap'): + self.canvas.setLineCap(lcap[node.getAttribute('cap')]) + if node.hasAttribute('miterLimit'): + self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit'))) + if node.hasAttribute('dash'): + dashes = node.getAttribute('dash').split(',') + for x in range(len(dashes)): + dashes[x]=utils.unit_get(dashes[x]) + self.canvas.setDash(node.getAttribute('dash').split(',')) + + def _image(self, node): + import urllib + from reportlab.lib.utils import ImageReader + + if not node.hasAttribute('file'): + + if node.hasAttribute('name'): + image_data = self.images[node.getAttribute('name')] + s = StringIO.StringIO(image_data) + else: + import base64 + image_data = base64.decodestring(node.firstChild.nodeValue) + if not image_data: return False + s = StringIO.StringIO(image_data) + else: + if node.getAttribute('file') in self.images: + s = StringIO.StringIO(self.images[node.getAttribute('file')]) + else: + try: + u = urllib.urlopen(str(node.getAttribute('file'))) + s = StringIO.StringIO(u.read()) + except: + u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb') + s = StringIO.StringIO(u.read()) + img = ImageReader(s) + (sx,sy) = img.getSize() + + args = {} + for tag in ('width','height','x','y'): + if node.hasAttribute(tag): + args[tag] = utils.unit_get(node.getAttribute(tag)) + if ('width' in args) and (not 'height' in args): + args['height'] = sy * args['width'] / sx + elif ('height' in args) and (not 'width' in args): + args['width'] = sx * args['height'] / sy + elif ('width' in args) and ('height' in args): + if (float(args['width'])/args['height'])>(float(sx)>sy): + args['width'] = sx * args['height'] / sy + else: + args['height'] = sy * args['width'] / sx + self.canvas.drawImage(img, **args) + + def _path(self, node): + self.path = self.canvas.beginPath() + self.path.moveTo(**utils.attr_get(node, ['x','y'])) + for n in node.childNodes: + if n.nodeType == node.ELEMENT_NODE: + if n.localName=='moveto': + vals = utils.text_get(n).split() + self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1])) + elif n.localName=='curvesto': + vals = utils.text_get(n).split() + while len(vals)>5: + pos=[] + while len(pos)<6: + pos.append(utils.unit_get(vals.pop(0))) + self.path.curveTo(*pos) + elif (n.nodeType == node.TEXT_NODE): + data = n.data.split() # Not sure if I must merge all TEXT_NODE ? + while len(data)>1: + x = utils.unit_get(data.pop(0)) + y = utils.unit_get(data.pop(0)) + self.path.lineTo(x,y) + if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')): + self.path.close() + self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'})) + + def render(self, node): + tags = { + 'drawCentredString': self._drawCenteredString, + 'drawRightString': self._drawRightString, + 'drawString': self._drawString, + 'rect': self._rect, + 'ellipse': self._ellipse, + 'lines': self._lines, + 'grid': self._grid, + 'curves': self._curves, + 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))), + 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))), + 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))), + 'place': self._place, + 'circle': self._circle, + 'lineMode': self._line_mode, + 'path': self._path, + 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))), + 'translate': self._translate, + 'image': self._image + } + for nd in node.childNodes: + if nd.nodeType==nd.ELEMENT_NODE: + for tag in tags: + if nd.localName==tag: + tags[tag](nd) + break class _rml_draw(object): - def __init__(self, node, styles, images={}, path='.'): - self.node = node - self.styles = styles - self.canvas = None - self.images = images - self.path = path - - def render(self, canvas, doc): - canvas.saveState() - cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path) - cnv.render(self.node) - canvas.restoreState() + def __init__(self, node, styles, images={}, path='.'): + self.node = node + self.styles = styles + self.canvas = None + self.images = images + self.path = path + + def render(self, canvas, doc): + canvas.saveState() + cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path) + cnv.render(self.node) + canvas.restoreState() class _rml_flowable(object): - def __init__(self, doc, images={}, path='.'): - self.doc = doc - self.styles = doc.styles - self.images = images - self.path = path - - def _textual(self, node): - rc = '' - for n in node.childNodes: - if n.nodeType == node.ELEMENT_NODE: - if n.localName == 'getName': - newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name')) - node.insertBefore(newNode, n) - node.removeChild(n) - n = newNode - elif n.localName == 'pageNumber': - rc += '<pageNumber/>' # TODO: change this ! - else: - #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag? - self._textual(n) - rc += n.toxml() - elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): - rc += str2xml(n.data) - return rc.encode(encoding, 'replace') - - def _table(self, node): - length = 0 - colwidths = None - rowheights = None - data = [] - childs = _child_get(node,'tr') - if not childs: - return None - posy = 0 - styles = [] - for tr in childs: - paraStyle = None - if tr.hasAttribute('style'): - st = copy.deepcopy(self.styles.table_styles[tr.getAttribute('style')]) - for s in st._cmds: - s[1][1] = posy - s[2][1] = posy - styles.append(st) - if tr.hasAttribute('paraStyle'): - paraStyle = self.styles.styles[tr.getAttribute('paraStyle')] - - data2 = [] - posx = 0 - for td in _child_get(tr, 'td'): - if td.hasAttribute('style'): - st = copy.deepcopy(self.styles.table_styles[td.getAttribute('style')]) - for s in st._cmds: - s[1][1] = posy - s[2][1] = posy - s[1][0] = posx - s[2][0] = posx - styles.append(st) - if td.hasAttribute('paraStyle'): - # TODO: merge styles - paraStyle = self.styles.styles[td.getAttribute('paraStyle')] - posx += 1 - - flow = [] - for n in td.childNodes: - if n.nodeType==node.ELEMENT_NODE: - fl = self._flowable(n, extra_style=paraStyle) - flow.append( fl ) - if not len(flow): - flow = self._textual(td) - data2.append( flow ) - if len(data2)>length: - length=len(data2) - for ab in data: - while len(ab)<length: - ab.append('') - while len(data2)<length: - data2.append('') - data.append( data2 ) - posy += 1 - if node.hasAttribute('colWidths'): - assert length == len(node.getAttribute('colWidths').split(',')) - colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')] - if node.hasAttribute('rowHeights'): - rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')] - if len(rowheights) == 1: - rowheights = rowheights[0] - table = platypus.Table(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'}))) - if node.hasAttribute('style'): - table.setStyle(self.styles.table_styles[node.getAttribute('style')]) - for s in styles: - table.setStyle(s) - return table - - def _illustration(self, node): - class Illustration(platypus.flowables.Flowable): - def __init__(self, node, styles, self2): - self.node = node - self.styles = styles - self.width = utils.unit_get(node.getAttribute('width')) - self.height = utils.unit_get(node.getAttribute('height')) - self.self2 = self2 - def wrap(self, *args): - return (self.width, self.height) - def draw(self): - canvas = self.canv - drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path) - drw.render(self.canv, None) - return Illustration(node, self.styles, self) - - def _textual_image(self, node): - import base64 - rc = '' - for n in node.childNodes: - if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): - rc += n.data - return base64.decodestring(rc) - - def _flowable(self, node, extra_style=None): - if node.localName=='para': - style = self.styles.para_style_get(node) - if extra_style: - style.__dict__.update(extra_style) - return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) - elif node.localName=='barCode': - try: - from reportlab.graphics.barcode import code128 - from reportlab.graphics.barcode import code39 - from reportlab.graphics.barcode import code93 - from reportlab.graphics.barcode import common - from reportlab.graphics.barcode import fourstate - from reportlab.graphics.barcode import usps - except Exception, e: - print 'Warning: Reportlab barcode extension not installed !' - return None - args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'}) - 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), - 'i2of5': lambda x: common.I2of5(x, **args), - 'extended39': lambda x: code39.Extended39(x, **args), - 'extended93': lambda x: code93.Extended93(x, **args), - 'msi': lambda x: common.MSI(x, **args), - 'fim': lambda x: usps.FIM(x, **args), - 'postnet': lambda x: usps.POSTNET(x, **args), - } - code = 'code128' - if node.hasAttribute('code'): - code = node.getAttribute('code').lower() - return codes[code](self._textual(node)) - elif node.localName=='name': - self.styles.names[ node.getAttribute('id')] = node.getAttribute('value') - return None - elif node.localName=='xpre': - style = self.styles.para_style_get(node) - return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'}))) - elif node.localName=='pre': - style = self.styles.para_style_get(node) - return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'}))) - elif node.localName=='illustration': - return self._illustration(node) - elif node.localName=='blockTable': - return self._table(node) - elif node.localName=='title': - styles = reportlab.lib.styles.getSampleStyleSheet() - style = styles['Title'] - return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) - elif re.match('^h([1-9]+[0-9]*)$', node.localName): - styles = reportlab.lib.styles.getSampleStyleSheet() - style = styles['Heading'+str(node.localName[1:])] - return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) - elif node.localName=='image': - if not node.hasAttribute('file'): - if node.hasAttribute('name'): - image_data = self.doc.images[node.getAttribute('name')].read() - else: - import base64 - image_data = base64.decodestring(node.firstChild.nodeValue) - image = StringIO.StringIO(image_data) - return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height']))) - else: - return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height']))) - - from reportlab.lib.utils import ImageReader - name = str(node.getAttribute('file')) - img = ImageReader(name) - (sx,sy) = img.getSize() - - args = {} - for tag in ('width','height'): - if node.hasAttribute(tag): - args[tag] = utils.unit_get(node.getAttribute(tag)) - if ('width' in args) and (not 'height' in args): - args['height'] = sy * args['width'] / sx - elif ('height' in args) and (not 'width' in args): - args['width'] = sx * args['height'] / sy - elif ('width' in args) and ('height' in args): - if (float(args['width'])/args['height'])>(float(sx)>sy): - args['width'] = sx * args['height'] / sy - else: - args['height'] = sy * args['width'] / sx - return platypus.Image(name, mask=(250,255,250,255,250,255), **args) - elif node.localName=='spacer': - if node.hasAttribute('width'): - width = utils.unit_get(node.getAttribute('width')) - else: - width = utils.unit_get('1cm') - length = utils.unit_get(node.getAttribute('length')) - return platypus.Spacer(width=width, height=length) - elif node.localName=='section': - return self.render(node) - elif node.localName == 'pageNumberReset': - return PageReset() - elif node.localName in ('pageBreak', 'nextPage'): - return platypus.PageBreak() - elif node.localName=='condPageBreak': - return platypus.CondPageBreak(**(utils.attr_get(node, ['height']))) - elif node.localName=='setNextTemplate': - return platypus.NextPageTemplate(str(node.getAttribute('name'))) - elif node.localName=='nextFrame': - return platypus.CondPageBreak(1000) # TODO: change the 1000 ! - elif node.localName == 'setNextFrame': - from reportlab.platypus.doctemplate import NextFrameFlowable - return NextFrameFlowable(str(node.getAttribute('name'))) - elif node.localName == 'currentFrame': - from reportlab.platypus.doctemplate import CurrentFrameFlowable - return CurrentFrameFlowable(str(node.getAttribute('name'))) - elif node.localName == 'frameEnd': - return EndFrameFlowable() - else: - sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,)) - return None - - def render(self, node_story): - story = [] - node = node_story.firstChild - while node: - if node.nodeType == node.ELEMENT_NODE: - flow = self._flowable(node) - if flow: - if type(flow) == type([]): - story = story + flow - else: - story.append(flow) - node = node.nextSibling - return story + def __init__(self, doc, images={}, path='.'): + self.doc = doc + self.styles = doc.styles + self.images = images + self.path = path + + def _textual(self, node): + rc = '' + for n in node.childNodes: + if n.nodeType == node.ELEMENT_NODE: + if n.localName == 'getName': + newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name')) + node.insertBefore(newNode, n) + node.removeChild(n) + n = newNode + elif n.localName == 'pageNumber': + rc += '<pageNumber/>' # TODO: change this ! + else: + #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag? + self._textual(n) + rc += n.toxml() + elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): + rc += str2xml(n.data) + return rc.encode(encoding, 'replace') + + def _table(self, node): + length = 0 + colwidths = None + rowheights = None + data = [] + childs = _child_get(node,'tr') + if not childs: + return None + posy = 0 + styles = [] + for tr in childs: + paraStyle = None + if tr.hasAttribute('style'): + st = copy.deepcopy(self.styles.table_styles[tr.getAttribute('style')]) + for s in st._cmds: + s[1][1] = posy + s[2][1] = posy + styles.append(st) + if tr.hasAttribute('paraStyle'): + paraStyle = self.styles.styles[tr.getAttribute('paraStyle')] + + data2 = [] + posx = 0 + for td in _child_get(tr, 'td'): + if td.hasAttribute('style'): + st = copy.deepcopy(self.styles.table_styles[td.getAttribute('style')]) + for s in st._cmds: + s[1][1] = posy + s[2][1] = posy + s[1][0] = posx + s[2][0] = posx + styles.append(st) + if td.hasAttribute('paraStyle'): + # TODO: merge styles + paraStyle = self.styles.styles[td.getAttribute('paraStyle')] + posx += 1 + + flow = [] + for n in td.childNodes: + if n.nodeType==node.ELEMENT_NODE: + fl = self._flowable(n, extra_style=paraStyle) + flow.append( fl ) + if not len(flow): + flow = self._textual(td) + data2.append( flow ) + if len(data2)>length: + length=len(data2) + for ab in data: + while len(ab)<length: + ab.append('') + while len(data2)<length: + data2.append('') + data.append( data2 ) + posy += 1 + if node.hasAttribute('colWidths'): + assert length == len(node.getAttribute('colWidths').split(',')) + colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')] + if node.hasAttribute('rowHeights'): + rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')] + if len(rowheights) == 1: + rowheights = rowheights[0] + table = platypus.Table(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'}))) + if node.hasAttribute('style'): + table.setStyle(self.styles.table_styles[node.getAttribute('style')]) + for s in styles: + table.setStyle(s) + return table + + def _illustration(self, node): + class Illustration(platypus.flowables.Flowable): + def __init__(self, node, styles, self2): + self.node = node + self.styles = styles + self.width = utils.unit_get(node.getAttribute('width')) + self.height = utils.unit_get(node.getAttribute('height')) + self.self2 = self2 + def wrap(self, *args): + return (self.width, self.height) + def draw(self): + canvas = self.canv + drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path) + drw.render(self.canv, None) + return Illustration(node, self.styles, self) + + def _textual_image(self, node): + import base64 + rc = '' + for n in node.childNodes: + if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE): + rc += n.data + return base64.decodestring(rc) + + def _flowable(self, node, extra_style=None): + if node.localName=='para': + style = self.styles.para_style_get(node) + if extra_style: + style.__dict__.update(extra_style) + return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) + elif node.localName=='barCode': + try: + from reportlab.graphics.barcode import code128 + from reportlab.graphics.barcode import code39 + from reportlab.graphics.barcode import code93 + from reportlab.graphics.barcode import common + from reportlab.graphics.barcode import fourstate + from reportlab.graphics.barcode import usps + except Exception, e: + print 'Warning: Reportlab barcode extension not installed !' + return None + args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'}) + 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), + 'i2of5': lambda x: common.I2of5(x, **args), + 'extended39': lambda x: code39.Extended39(x, **args), + 'extended93': lambda x: code93.Extended93(x, **args), + 'msi': lambda x: common.MSI(x, **args), + 'fim': lambda x: usps.FIM(x, **args), + 'postnet': lambda x: usps.POSTNET(x, **args), + } + code = 'code128' + if node.hasAttribute('code'): + code = node.getAttribute('code').lower() + return codes[code](self._textual(node)) + elif node.localName=='name': + self.styles.names[ node.getAttribute('id')] = node.getAttribute('value') + return None + elif node.localName=='xpre': + style = self.styles.para_style_get(node) + return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'}))) + elif node.localName=='pre': + style = self.styles.para_style_get(node) + return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'}))) + elif node.localName=='illustration': + return self._illustration(node) + elif node.localName=='blockTable': + return self._table(node) + elif node.localName=='title': + styles = reportlab.lib.styles.getSampleStyleSheet() + style = styles['Title'] + return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) + elif re.match('^h([1-9]+[0-9]*)$', node.localName): + styles = reportlab.lib.styles.getSampleStyleSheet() + style = styles['Heading'+str(node.localName[1:])] + return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'}))) + elif node.localName=='image': + if not node.hasAttribute('file'): + if node.hasAttribute('name'): + image_data = self.doc.images[node.getAttribute('name')].read() + else: + import base64 + image_data = base64.decodestring(node.firstChild.nodeValue) + image = StringIO.StringIO(image_data) + return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height']))) + else: + return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height']))) + + from reportlab.lib.utils import ImageReader + name = str(node.getAttribute('file')) + img = ImageReader(name) + (sx,sy) = img.getSize() + + args = {} + for tag in ('width','height'): + if node.hasAttribute(tag): + args[tag] = utils.unit_get(node.getAttribute(tag)) + if ('width' in args) and (not 'height' in args): + args['height'] = sy * args['width'] / sx + elif ('height' in args) and (not 'width' in args): + args['width'] = sx * args['height'] / sy + elif ('width' in args) and ('height' in args): + if (float(args['width'])/args['height'])>(float(sx)>sy): + args['width'] = sx * args['height'] / sy + else: + args['height'] = sy * args['width'] / sx + return platypus.Image(name, mask=(250,255,250,255,250,255), **args) + elif node.localName=='spacer': + if node.hasAttribute('width'): + width = utils.unit_get(node.getAttribute('width')) + else: + width = utils.unit_get('1cm') + length = utils.unit_get(node.getAttribute('length')) + return platypus.Spacer(width=width, height=length) + elif node.localName=='section': + return self.render(node) + elif node.localName == 'pageNumberReset': + return PageReset() + elif node.localName in ('pageBreak', 'nextPage'): + return platypus.PageBreak() + elif node.localName=='condPageBreak': + return platypus.CondPageBreak(**(utils.attr_get(node, ['height']))) + elif node.localName=='setNextTemplate': + return platypus.NextPageTemplate(str(node.getAttribute('name'))) + elif node.localName=='nextFrame': + return platypus.CondPageBreak(1000) # TODO: change the 1000 ! + elif node.localName == 'setNextFrame': + from reportlab.platypus.doctemplate import NextFrameFlowable + return NextFrameFlowable(str(node.getAttribute('name'))) + elif node.localName == 'currentFrame': + from reportlab.platypus.doctemplate import CurrentFrameFlowable + return CurrentFrameFlowable(str(node.getAttribute('name'))) + elif node.localName == 'frameEnd': + return EndFrameFlowable() + else: + sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,)) + return None + + def render(self, node_story): + story = [] + node = node_story.firstChild + while node: + if node.nodeType == node.ELEMENT_NODE: + flow = self._flowable(node) + if flow: + if type(flow) == type([]): + story = story + flow + else: + story.append(flow) + node = node.nextSibling + return story from reportlab.platypus.doctemplate import ActionFlowable class EndFrameFlowable(ActionFlowable): - def __init__(self,resume=0): - ActionFlowable.__init__(self,('frameEnd',resume)) + def __init__(self,resume=0): + ActionFlowable.__init__(self,('frameEnd',resume)) class TinyDocTemplate(platypus.BaseDocTemplate): - def ___handle_pageBegin(self): - self.page = self.page + 1 - self.pageTemplate.beforeDrawPage(self.canv,self) - self.pageTemplate.checkPageSize(self.canv,self) - self.pageTemplate.onPage(self.canv,self) - for f in self.pageTemplate.frames: f._reset() - self.beforePage() - #keep a count of flowables added to this page. zero indicates bad stuff - self._curPageFlowableCount = 0 - if hasattr(self,'_nextFrameIndex'): - del self._nextFrameIndex - for f in self.pageTemplate.frames: - if f.id == 'first': - self.frame = f - break - self.handle_frameBegin() - def afterFlowable(self, flowable): - if isinstance(flowable, PageReset): - self.canv._pageNumber = 0 + def ___handle_pageBegin(self): + self.page = self.page + 1 + self.pageTemplate.beforeDrawPage(self.canv,self) + self.pageTemplate.checkPageSize(self.canv,self) + self.pageTemplate.onPage(self.canv,self) + for f in self.pageTemplate.frames: f._reset() + self.beforePage() + #keep a count of flowables added to this page. zero indicates bad stuff + self._curPageFlowableCount = 0 + if hasattr(self,'_nextFrameIndex'): + del self._nextFrameIndex + for f in self.pageTemplate.frames: + if f.id == 'first': + self.frame = f + break + self.handle_frameBegin() + def afterFlowable(self, flowable): + if isinstance(flowable, PageReset): + self.canv._pageNumber = 0 class _rml_template(object): - def __init__(self, out, node, doc, images={}, path='.'): - self.images= images - self.path = path - if not node.hasAttribute('pageSize'): - pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm')) - else: - ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(',')) - pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) ) - cm = reportlab.lib.units.cm - self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'})) - self.page_templates = [] - self.styles = doc.styles - self.doc = doc - pts = node.getElementsByTagName('pageTemplate') - for pt in pts: - frames = [] - for frame_el in pt.getElementsByTagName('frame'): - frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) ) - if utils.attr_get(frame_el, ['last']): - frame.lastFrame = True - frames.append( frame ) - gr = pt.getElementsByTagName('pageGraphics') - if len(gr): - drw = _rml_draw(gr[0], self.doc, images=images, path=self.path) - self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) )) - else: - self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) )) - self.doc_tmpl.addPageTemplates(self.page_templates) - - def render(self, node_stories): - fis = [] - r = _rml_flowable(self.doc,images=self.images, path=self.path) - for node_story in node_stories: - fis += r.render(node_story) - if node_story==node_stories[-1]: - - fis.append(PageCount()) - - fis.append(platypus.PageBreak()) - - self.doc_tmpl.build(fis) + def __init__(self, out, node, doc, images={}, path='.'): + self.images= images + self.path = path + if not node.hasAttribute('pageSize'): + pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm')) + else: + ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(',')) + pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) ) + cm = reportlab.lib.units.cm + self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'})) + self.page_templates = [] + self.styles = doc.styles + self.doc = doc + pts = node.getElementsByTagName('pageTemplate') + for pt in pts: + frames = [] + for frame_el in pt.getElementsByTagName('frame'): + frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) ) + if utils.attr_get(frame_el, ['last']): + frame.lastFrame = True + frames.append( frame ) + gr = pt.getElementsByTagName('pageGraphics') + if len(gr): + drw = _rml_draw(gr[0], self.doc, images=images, path=self.path) + self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) )) + else: + self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) )) + self.doc_tmpl.addPageTemplates(self.page_templates) + + def render(self, node_stories): + fis = [] + r = _rml_flowable(self.doc,images=self.images, path=self.path) + for node_story in node_stories: + fis += r.render(node_story) + if node_story==node_stories[-1]: + + fis.append(PageCount()) + + fis.append(platypus.PageBreak()) + + self.doc_tmpl.build(fis) def parseString(data, fout=None, images={}, path='.'): - r = _rml_doc(data, images, path) - if fout: - fp = file(fout,'wb') - r.render(fp) - fp.close() - return fout - else: - fp = StringIO.StringIO() - r.render(fp) - return fp.getvalue() + r = _rml_doc(data, images, path) + if fout: + fp = file(fout,'wb') + r.render(fp) + fp.close() + return fout + else: + fp = StringIO.StringIO() + r.render(fp) + return fp.getvalue() def trml2pdf_help(): - print 'Usage: trml2pdf input.rml >output.pdf' - print 'Render the standard input (RML) and output a PDF file' - sys.exit(0) + print 'Usage: trml2pdf input.rml >output.pdf' + print 'Render the standard input (RML) and output a PDF file' + sys.exit(0) if __name__=="__main__": - if len(sys.argv)>1: - if sys.argv[1]=='--help': - trml2pdf_help() - print parseString(file(sys.argv[1], 'r').read()), - else: - print 'Usage: trml2pdf input.rml >output.pdf' - print 'Try \'trml2pdf --help\' for more information.' + if len(sys.argv)>1: + if sys.argv[1]=='--help': + trml2pdf_help() + print parseString(file(sys.argv[1], 'r').read()), + else: + print 'Usage: trml2pdf input.rml >output.pdf' + print 'Try \'trml2pdf --help\' for more information.' diff --git a/bin/report/render/rml2pdf/utils.py b/bin/report/render/rml2pdf/utils.py index 555d00bdb13..5ee7997a5e5 100644 --- a/bin/report/render/rml2pdf/utils.py +++ b/bin/report/render/rml2pdf/utils.py @@ -48,47 +48,47 @@ import re import reportlab def text_get(node): - rc = '' - for node in node.childNodes: - if node.nodeType == node.TEXT_NODE: - rc = rc + node.data - return rc + rc = '' + for node in node.childNodes: + if node.nodeType == node.TEXT_NODE: + rc = rc + node.data + return rc units = [ - (re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch), - (re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm), - (re.compile('^(-?[0-9\.]+)\s*mm$'), reportlab.lib.units.mm), - (re.compile('^(-?[0-9\.]+)\s*$'), 1) + (re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch), + (re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm), + (re.compile('^(-?[0-9\.]+)\s*mm$'), reportlab.lib.units.mm), + (re.compile('^(-?[0-9\.]+)\s*$'), 1) ] def unit_get(size): - global units - for unit in units: - res = unit[0].search(size, 0) - if res: - return unit[1]*float(res.group(1)) - return False + global units + for unit in units: + res = unit[0].search(size, 0) + if res: + return unit[1]*float(res.group(1)) + return False def tuple_int_get(node, attr_name, default=None): - if not node.hasAttribute(attr_name): - return default - res = [int(x) for x in node.getAttribute(attr_name).split(',')] - return res + if not node.hasAttribute(attr_name): + return default + res = [int(x) for x in node.getAttribute(attr_name).split(',')] + return res def bool_get(value): - return (str(value)=="1") or (value.lower()=='yes') + return (str(value)=="1") or (value.lower()=='yes') def attr_get(node, attrs, dict={}): - res = {} - for name in attrs: - if node.hasAttribute(name): - res[name] = unit_get(node.getAttribute(name)) - for key in dict: - if node.hasAttribute(key): - if dict[key]=='str': - res[key] = str(node.getAttribute(key)) - elif dict[key]=='bool': - res[key] = bool_get(node.getAttribute(key)) - elif dict[key]=='int': - res[key] = int(node.getAttribute(key)) - return res + res = {} + for name in attrs: + if node.hasAttribute(name): + res[name] = unit_get(node.getAttribute(name)) + for key in dict: + if node.hasAttribute(key): + if dict[key]=='str': + res[key] = str(node.getAttribute(key)) + elif dict[key]=='bool': + res[key] = bool_get(node.getAttribute(key)) + elif dict[key]=='int': + res[key] = int(node.getAttribute(key)) + return res diff --git a/bin/report/render/simple.py b/bin/report/render/simple.py index ea830250d71..a3a642a6119 100644 --- a/bin/report/render/simple.py +++ b/bin/report/render/simple.py @@ -40,52 +40,52 @@ import reportlab.lib import copy class simple(render.render): - def _render(self): - self.result = StringIO() - parser = xml.dom.minidom.parseString(self.xml) + def _render(self): + self.result = StringIO() + parser = xml.dom.minidom.parseString(self.xml) - title = parser.documentElement.tagName - doc = SimpleDocTemplate(self.result, pagesize=A4, title=title, - author='Tiny ERP, Fabien Pinckaers', leftmargin=10*mm, rightmargin=10*mm) + title = parser.documentElement.tagName + doc = SimpleDocTemplate(self.result, pagesize=A4, title=title, + author='Tiny ERP, Fabien Pinckaers', leftmargin=10*mm, rightmargin=10*mm) - styles = reportlab.lib.styles.getSampleStyleSheet() - title_style = copy.deepcopy(styles["Heading1"]) - title_style.alignment = reportlab.lib.enums.TA_CENTER - story = [ Paragraph(title, title_style) ] - style_level = {} - nodes = [ (parser.documentElement,0) ] - while len(nodes): - node = nodes.pop(0) - value = '' - n=len(node[0].childNodes)-1 - while n>=0: - if node[0].childNodes[n].nodeType==3: - value += node[0].childNodes[n].nodeValue - else: - nodes.insert( 0, (node[0].childNodes[n], node[1]+1) ) - n-=1 - if not node[1] in style_level: - style = copy.deepcopy(styles["Normal"]) - style.leftIndent=node[1]*6*mm - style.firstLineIndent=-3*mm - style_level[node[1]] = style - story.append( Paragraph('<b>%s</b>: %s' % (node[0].tagName, value), style_level[node[1]])) - doc.build(story) - return self.result.getvalue() + styles = reportlab.lib.styles.getSampleStyleSheet() + title_style = copy.deepcopy(styles["Heading1"]) + title_style.alignment = reportlab.lib.enums.TA_CENTER + story = [ Paragraph(title, title_style) ] + style_level = {} + nodes = [ (parser.documentElement,0) ] + while len(nodes): + node = nodes.pop(0) + value = '' + n=len(node[0].childNodes)-1 + while n>=0: + if node[0].childNodes[n].nodeType==3: + value += node[0].childNodes[n].nodeValue + else: + nodes.insert( 0, (node[0].childNodes[n], node[1]+1) ) + n-=1 + if not node[1] in style_level: + style = copy.deepcopy(styles["Normal"]) + style.leftIndent=node[1]*6*mm + style.firstLineIndent=-3*mm + style_level[node[1]] = style + story.append( Paragraph('<b>%s</b>: %s' % (node[0].tagName, value), style_level[node[1]])) + doc.build(story) + return self.result.getvalue() if __name__=='__main__': - import time - s = simple('''<test> - <author-list> - <author> - <name>Fabien Pinckaers</name> - <age>23</age> - </author> - <author> - <name>Michel Pinckaers</name> - <age>53</age> - </author> - No other - </author-list> - </test>''') - print s.render() + import time + s = simple('''<test> + <author-list> + <author> + <name>Fabien Pinckaers</name> + <age>23</age> + </author> + <author> + <name>Michel Pinckaers</name> + <age>53</age> + </author> + No other + </author-list> + </test>''') + print s.render() diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index 586527a13d0..13a3ca02f3b 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -51,611 +51,611 @@ HM_FORMAT = '%H:%M:%S' import base64 if not hasattr(locale, 'nl_langinfo'): - locale.nl_langinfo = lambda *a: '%x' + locale.nl_langinfo = lambda *a: '%x' if not hasattr(locale, 'D_FMT'): - locale.D_FMT = None + locale.D_FMT = None rml_parents = { - 'tr':1, - 'li':1, - 'story': 0, - 'section': 0 + 'tr':1, + 'li':1, + 'story': 0, + 'section': 0 } rml_tag="para" sxw_parents = { - 'table-row': 1, - 'list-item': 1, - 'body': 0, - 'section': 0, + 'table-row': 1, + 'list-item': 1, + 'body': 0, + 'section': 0, } sxw_tag = "p" rml2sxw = { - 'para': 'p', + 'para': 'p', } _LOCALE2WIN32 = { - 'af_ZA': 'Afrikaans_South Africa', - 'sq_AL': 'Albanian_Albania', - 'ar_SA': 'Arabic_Saudi Arabia', - 'eu_ES': 'Basque_Spain', - 'be_BY': 'Belarusian_Belarus', - 'bs_BA': 'Serbian (Latin)', - 'bg_BG': 'Bulgarian_Bulgaria', - 'ca_ES': 'Catalan_Spain', - 'hr_HR': 'Croatian_Croatia', - 'zh_CN': 'Chinese_China', - 'zh_TW': 'Chinese_Taiwan', - 'cs_CZ': 'Czech_Czech Republic', - 'da_DK': 'Danish_Denmark', - 'nl_NL': 'Dutch_Netherlands', - 'et_EE': 'Estonian_Estonia', - 'fa_IR': 'Farsi_Iran', - 'ph_PH': 'Filipino_Philippines', - 'fi_FI': 'Finnish_Finland', - 'fr_FR': 'French_France', - 'fr_BE': 'French_France', - 'fr_CH': 'French_France', - 'fr_CA': 'French_France', - 'ga': 'Scottish Gaelic', - 'gl_ES': 'Galician_Spain', - 'ka_GE': 'Georgian_Georgia', - 'de_DE': 'German_Germany', - 'el_GR': 'Greek_Greece', - 'gu': 'Gujarati_India', - 'he_IL': 'Hebrew_Israel', - 'hi_IN': 'Hindi', - 'hu': 'Hungarian_Hungary', - 'is_IS': 'Icelandic_Iceland', - 'id_ID': 'Indonesian_indonesia', - 'it_IT': 'Italian_Italy', - 'ja_JP': 'Japanese_Japan', - 'kn_IN': 'Kannada', - 'km_KH': 'Khmer', - 'ko_KR': 'Korean_Korea', - 'lo_LA': 'Lao_Laos', - 'lt_LT': 'Lithuanian_Lithuania', - 'lat': 'Latvian_Latvia', - 'ml_IN': 'Malayalam_India', - 'id_ID': 'Indonesian_indonesia', - 'mi_NZ': 'Maori', - 'mn': 'Cyrillic_Mongolian', - 'no_NO': 'Norwegian_Norway', - 'nn_NO': 'Norwegian-Nynorsk_Norway', - 'pl': 'Polish_Poland', - 'pt_PT': 'Portuguese_Portugal', - 'pt_BR': 'Portuguese_Brazil', - 'ro_RO': 'Romanian_Romania', - 'ru_RU': 'Russian_Russia', - 'mi_NZ': 'Maori', - 'sr_CS': 'Serbian (Cyrillic)_Serbia and Montenegro', - 'sk_SK': 'Slovak_Slovakia', - 'sl_SI': 'Slovenian_Slovenia', - 'es_ES': 'Spanish_Spain', - 'sv_SE': 'Swedish_Sweden', - 'ta_IN': 'English_Australia', - 'th_TH': 'Thai_Thailand', - 'mi_NZ': 'Maori', - 'tr_TR': 'Turkish_Turkey', - 'uk_UA': 'Ukrainian_Ukraine', - 'vi_VN': 'Vietnamese_Viet Nam', + 'af_ZA': 'Afrikaans_South Africa', + 'sq_AL': 'Albanian_Albania', + 'ar_SA': 'Arabic_Saudi Arabia', + 'eu_ES': 'Basque_Spain', + 'be_BY': 'Belarusian_Belarus', + 'bs_BA': 'Serbian (Latin)', + 'bg_BG': 'Bulgarian_Bulgaria', + 'ca_ES': 'Catalan_Spain', + 'hr_HR': 'Croatian_Croatia', + 'zh_CN': 'Chinese_China', + 'zh_TW': 'Chinese_Taiwan', + 'cs_CZ': 'Czech_Czech Republic', + 'da_DK': 'Danish_Denmark', + 'nl_NL': 'Dutch_Netherlands', + 'et_EE': 'Estonian_Estonia', + 'fa_IR': 'Farsi_Iran', + 'ph_PH': 'Filipino_Philippines', + 'fi_FI': 'Finnish_Finland', + 'fr_FR': 'French_France', + 'fr_BE': 'French_France', + 'fr_CH': 'French_France', + 'fr_CA': 'French_France', + 'ga': 'Scottish Gaelic', + 'gl_ES': 'Galician_Spain', + 'ka_GE': 'Georgian_Georgia', + 'de_DE': 'German_Germany', + 'el_GR': 'Greek_Greece', + 'gu': 'Gujarati_India', + 'he_IL': 'Hebrew_Israel', + 'hi_IN': 'Hindi', + 'hu': 'Hungarian_Hungary', + 'is_IS': 'Icelandic_Iceland', + 'id_ID': 'Indonesian_indonesia', + 'it_IT': 'Italian_Italy', + 'ja_JP': 'Japanese_Japan', + 'kn_IN': 'Kannada', + 'km_KH': 'Khmer', + 'ko_KR': 'Korean_Korea', + 'lo_LA': 'Lao_Laos', + 'lt_LT': 'Lithuanian_Lithuania', + 'lat': 'Latvian_Latvia', + 'ml_IN': 'Malayalam_India', + 'id_ID': 'Indonesian_indonesia', + 'mi_NZ': 'Maori', + 'mn': 'Cyrillic_Mongolian', + 'no_NO': 'Norwegian_Norway', + 'nn_NO': 'Norwegian-Nynorsk_Norway', + 'pl': 'Polish_Poland', + 'pt_PT': 'Portuguese_Portugal', + 'pt_BR': 'Portuguese_Brazil', + 'ro_RO': 'Romanian_Romania', + 'ru_RU': 'Russian_Russia', + 'mi_NZ': 'Maori', + 'sr_CS': 'Serbian (Cyrillic)_Serbia and Montenegro', + 'sk_SK': 'Slovak_Slovakia', + 'sl_SI': 'Slovenian_Slovenia', + 'es_ES': 'Spanish_Spain', + 'sv_SE': 'Swedish_Sweden', + 'ta_IN': 'English_Australia', + 'th_TH': 'Thai_Thailand', + 'mi_NZ': 'Maori', + 'tr_TR': 'Turkish_Turkey', + 'uk_UA': 'Ukrainian_Ukraine', + 'vi_VN': 'Vietnamese_Viet Nam', } class _format(object): - def set_value(self, name, object, field): - #super(_date_format, self).__init__(self) - self.object = object - self._field = field - self.name=name - lc, encoding = locale.getdefaultlocale() - if not encoding: - encoding = 'UTF-8' - if encoding == 'utf': - encoding = 'UTF-8' - if encoding == 'cp1252': - encoding= '1252' - lang = self.object._context.get('lang', 'en_US') or 'en_US' - try: - if os.name == 'nt': - locale.setlocale(locale.LC_ALL, _LOCALE2WIN32.get(lang, lang) + '.' + encoding) - else: - locale.setlocale(locale.LC_ALL, lang + '.' + encoding) - except Exception: - netsvc.Logger().notifyChannel('report', netsvc.LOG_WARNING, - 'report %s: unable to set locale "%s"' % (self.name, - self.object._context.get('lang', 'en_US') or 'en_US')) + def set_value(self, name, object, field): + #super(_date_format, self).__init__(self) + self.object = object + self._field = field + self.name=name + lc, encoding = locale.getdefaultlocale() + if not encoding: + encoding = 'UTF-8' + if encoding == 'utf': + encoding = 'UTF-8' + if encoding == 'cp1252': + encoding= '1252' + lang = self.object._context.get('lang', 'en_US') or 'en_US' + try: + if os.name == 'nt': + locale.setlocale(locale.LC_ALL, _LOCALE2WIN32.get(lang, lang) + '.' + encoding) + else: + locale.setlocale(locale.LC_ALL, lang + '.' + encoding) + except Exception: + netsvc.Logger().notifyChannel('report', netsvc.LOG_WARNING, + 'report %s: unable to set locale "%s"' % (self.name, + self.object._context.get('lang', 'en_US') or 'en_US')) class _float_format(float, _format): - def __str__(self): - if not self.object._context: - return self.name - digit = 2 - if hasattr(self._field, 'digits') and self._field.digits: - digit = self._field.digits[1] - return locale.format('%.' + str(digit) + 'f', self.name, True) + def __str__(self): + if not self.object._context: + return self.name + digit = 2 + if hasattr(self._field, 'digits') and self._field.digits: + digit = self._field.digits[1] + return locale.format('%.' + str(digit) + 'f', self.name, True) class _int_format(int, _format): - def __str__(self): - if not self.object._context: - return self.name - return locale.format('%d', self.name, True) + def __str__(self): + if not self.object._context: + return self.name + return locale.format('%d', self.name, True) class _date_format(str, _format): - def __str__(self): - if not self.object._context: - return self.name - if self.name: - datedata = time.strptime(self.name, DT_FORMAT) - return time.strftime(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y'), - datedata) - return '' + def __str__(self): + if not self.object._context: + return self.name + if self.name: + datedata = time.strptime(self.name, DT_FORMAT) + return time.strftime(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y'), + datedata) + return '' _fields_process = { - 'float': _float_format, - 'date': _date_format, - 'integer': _int_format + 'float': _float_format, + 'date': _date_format, + 'integer': _int_format } # # Context: {'node': node.dom} # class browse_record_list(list): - def __init__(self, lst, context): - super(browse_record_list, self).__init__(lst) - self.context = context - - def __getattr__(self, name): - res = browse_record_list([getattr(x,name) for x in self], self.context) - return res - - def __str__(self): - return "browse_record_list("+str(len(self))+")" - - def repeatIn(self, name): - warnings.warn('Use repeatIn(object_list, \'variable\')', DeprecationWarning) - node = self.context['_node'] - parents = self.context.get('parents', rml_parents) - node.data = '' - while True: - if not node.parentNode: - break - node = node.parentNode - if node.nodeType == node.ELEMENT_NODE and node.localName in parents: - break - parent_node = node - if not len(self): - return None - nodes = [(0,node)] - for i in range(1,len(self)): - newnode = parent_node.cloneNode(1) - n = parent_node.parentNode - n.insertBefore(newnode, parent_node) - nodes.append((i,newnode)) - for i,node in nodes: - self.context[name] = self[i] - self.context['_self']._parse_node(node) - return None + def __init__(self, lst, context): + super(browse_record_list, self).__init__(lst) + self.context = context + + def __getattr__(self, name): + res = browse_record_list([getattr(x,name) for x in self], self.context) + return res + + def __str__(self): + return "browse_record_list("+str(len(self))+")" + + def repeatIn(self, name): + warnings.warn('Use repeatIn(object_list, \'variable\')', DeprecationWarning) + node = self.context['_node'] + parents = self.context.get('parents', rml_parents) + node.data = '' + while True: + if not node.parentNode: + break + node = node.parentNode + if node.nodeType == node.ELEMENT_NODE and node.localName in parents: + break + parent_node = node + if not len(self): + return None + nodes = [(0,node)] + for i in range(1,len(self)): + newnode = parent_node.cloneNode(1) + n = parent_node.parentNode + n.insertBefore(newnode, parent_node) + nodes.append((i,newnode)) + for i,node in nodes: + self.context[name] = self[i] + self.context['_self']._parse_node(node) + return None class rml_parse(object): - def __init__(self, cr, uid, name, parents=rml_parents, tag=rml_tag, context=None): - if not context: - context={} - self.cr = cr - self.uid = uid - self.pool = pooler.get_pool(cr.dbname) - user = self.pool.get('res.users').browse(cr, uid, uid) - self.localcontext = { - 'user': user, - 'company': user.company_id, - 'repeatIn': self.repeatIn, - 'setLang': self.setLang, - 'setTag': self.setTag, - 'removeParentNode': self.removeParentNode, - 'format': self.format, - 'formatLang': self.formatLang, - 'logo' : user.company_id.logo, - } - self.localcontext.update(context) - self.rml_header = user.company_id.rml_header - self.rml_header2 = user.company_id.rml_header2 - self.logo = user.company_id.logo - self.name = name - self._regex = re.compile('\[\[(.+?)\]\]') - self._transl_regex = re.compile('(\[\[.+?\]\])') - self._node = None - self.parents = parents - self.tag = tag - self._lang_cache = {} -# self.already = {} - - def setTag(self, oldtag, newtag, attrs=None): - if not attrs: - attrs={} - node = self._find_parent(self._node, [oldtag]) - if node: - node.tagName = newtag - for key, val in attrs.items(): - node.setAttribute(key, val) - return None - - def format(self, text, oldtag=None): - if not oldtag: - oldtag = self.tag - self._node.data = '' - node = self._find_parent(self._node, [oldtag]) - - pp = node.parentNode - ns = node.nextSibling - pp.removeChild(node) - self._node = pp - lst = text.split('\n') - if not len(lst): - return None - nodes = [] - for i in range(len(lst)): - newnode = node.cloneNode(1) - newnode.tagName=rml_tag - newnode.__dict__['childNodes'][0].__dict__['data'] = lst[i].decode('utf8') - if ns: - pp.insertBefore(newnode, ns) - else: - pp.appendChild(newnode) - nodes.append((i, newnode)) - - def removeParentNode(self, tag=None): - if not tag: - tag = self.tag - if self.tag == sxw_tag and rml2sxw.get(tag, False): - tag = rml2sxw[tag] - node = self._find_parent(self._node, [tag]) - if node: - parentNode = node.parentNode - parentNode.removeChild(node) - self._node = parentNode - - def setLang(self, lang): - self.localcontext['lang'] = lang - for obj in self.objects: - obj._context['lang'] = lang - for table in obj._cache: - for id in obj._cache[table]: - self._lang_cache.setdefault(obj._context['lang'], {}).setdefault(table, - {}).update(obj._cache[table][id]) - if lang in self._lang_cache \ - and table in self._lang_cache[lang] \ - and id in self._lang_cache[lang][table]: - obj._cache[table][id] = self._lang_cache[lang][table][id] - else: - obj._cache[table][id] = {'id': id} - - def formatLang(self, value, digit=2, date=False): - if not value: - return '' - lc, encoding = locale.getdefaultlocale() - if not encoding: - encoding = 'UTF-8' - if encoding == 'utf': - encoding = 'UTF-8' - if encoding == 'cp1252': - encoding= '1252' - lang = self.localcontext.get('lang', 'en_US') or 'en_US' - try: - if os.name == 'nt': - locale.setlocale(locale.LC_ALL, _LOCALE2WIN32.get(lang, lang) + '.' + encoding) - else: - locale.setlocale(locale.LC_ALL, lang + '.' + encoding) - except Exception: - netsvc.Logger().notifyChannel('report', netsvc.LOG_WARNING, - 'report %s: unable to set locale "%s"' % (self.name, - self.localcontext.get('lang', 'en_US') or 'en_US')) - if date: - date = time.strptime(value, DT_FORMAT) - return time.strftime(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y'), - date) - return locale.format('%.' + str(digit) + 'f', value, True) - - def repeatIn(self, lst, name, nodes_parent=False): - self._node.data = '' - node = self._find_parent(self._node, nodes_parent or self.parents) - - pp = node.parentNode - ns = node.nextSibling - pp.removeChild(node) - self._node = pp - - if not len(lst): - return None - nodes = [] - for i in range(len(lst)): - newnode = node.cloneNode(1) - if ns: - pp.insertBefore(newnode, ns) - else: - pp.appendChild(newnode) - nodes.append((i, newnode)) - for i, node in nodes: - self.node_context[node] = {name: lst[i]} - return None - - def _eval(self, expr): - try: - res = eval(expr, self.localcontext) - if not res: - res = '' - except Exception,e: - import traceback, sys - tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) - netsvc.Logger().notifyChannel('report', netsvc.LOG_ERROR, - 'report %s:\n%s\n%s\nexpr: %s' % (self.name, tb_s, str(e), - expr.encode('utf-8'))) - res = '' - return res - - def _find_parent(self, node, parents): - while True: - if not node.parentNode: - return False - node = node.parentNode - if node.nodeType == node.ELEMENT_NODE and node.localName in parents: - break - return node - - def _parse_text(self, text, level=None): - if not level: - level=[] - res = self._regex.findall(text) - todo = [] - # translate the text - # the "split [[]] if not match [[]]" is not very nice, but I - # don't see how I could do it better... - # what I'd like to do is a re.sub(NOT pattern, func, string) - # but I don't know how to do that... - # translate the RML file - if 'lang' in self.localcontext: - lang = self.localcontext['lang'] - if lang and text and not text.isspace(): - transl_obj = self.pool.get('ir.translation') - piece_list = self._transl_regex.split(text) - for pn in range(len(piece_list)): - if not self._transl_regex.match(piece_list[pn]): - source_string = piece_list[pn].replace('\n', ' ').strip() - if len(source_string): - translated_string = transl_obj._get_source(self.cr, self.uid, self.name, 'rml', lang, source_string) - if translated_string: - piece_list[pn] = piece_list[pn].replace(source_string, translated_string.decode('utf8')) - text = ''.join(piece_list) - for key in res: - newtext = self._eval(key) - for i in range(len(level)): - if isinstance(newtext, list): - newtext = newtext[level[i]] - if isinstance(newtext, list): - todo.append((key, newtext)) - else: - #if not isinstance(newtext, basestring): - newtext = str(newtext) - # if there are two [[]] blocks the same, it will replace both - # but it's ok because it should evaluate to the same thing - # anyway - text = text.replace('[['+key+']]', newtext.decode('utf8')) - self._node.data = text - if len(todo): - for key, newtext in todo: - parent_node = self._find_parent(self._node, parents) - assert parents.get(parent_node.localName, False), 'No parent node found !' - nodes = [parent_node] - for i in range(len(newtext) - 1): - newnode = parent_node.cloneNode(1) - if parents.get(parent_node.localName, False): - n = parent_node.parentNode - parent_node.parentNode.insertAfter(newnode, parent_node) - nodes.append(newnode) - return False - return text - - def _parse_node(self): - level = [] - while True: - if self._node.nodeType==self._node.ELEMENT_NODE: - if self._node.hasAttribute('expr'): - newattrs = self._eval(self._node.getAttribute('expr')) - for key,val in newattrs.items(): - self._node.setAttribute(key,val) - - if self._node.hasChildNodes(): - self._node = self._node.firstChild - elif self._node.nextSibling: - self._node = self._node.nextSibling - else: - while self._node and not self._node.nextSibling: - self._node = self._node.parentNode - if not self._node: - break - self._node = self._node.nextSibling - if self._node in self.node_context: - self.localcontext.update(self.node_context[self._node]) - if self._node.nodeType in (self._node.CDATA_SECTION_NODE, self._node.TEXT_NODE): -# if self._node in self.already: -# self.already[self._node] += 1 -# print "second pass!", self.already[self._node], '---%s---' % self._node.data -# else: -# self.already[self._node] = 0 - self._parse_text(self._node.data, level) - return True - - def _find_node(self, node, localname): - if node.localName==localname: - return node - for tag in node.childNodes: - if tag.nodeType==tag.ELEMENT_NODE: - found = self._find_node(tag, localname) - if found: - return found - return False - - def _add_header(self, node, header=1): - if header==2: - rml_head = self.rml_header2 - else: - rml_head = self.rml_header - - # Refactor this patch, to use the minidom interface - if self.logo and (rml_head.find('company.logo')<0 or rml_head.find('<image')<0) and rml_head.find('<!--image')<0: - rml_head = rml_head.replace('<pageGraphics>','''<pageGraphics> <image x="10" y="26cm" height="70" width="90" >[[company.logo]] </image> ''') - if not self.logo and rml_head.find('company.logo')>=0: - rml_head = rml_head.replace('<image','<!--image') - rml_head = rml_head.replace('</image>','</image-->') - - head_dom = xml.dom.minidom.parseString(rml_head) - #for frame in head_dom.getElementsByTagName('frame'): - # frame.parentNode.removeChild(frame) - node2 = head_dom.documentElement - for tag in node2.childNodes: - if tag.nodeType==tag.ELEMENT_NODE: - found = self._find_node(node, tag.localName) - # rml_frames = found.getElementsByTagName('frame') - if found: - if tag.hasAttribute('position') and (tag.getAttribute('position')=='inside'): - found.appendChild(tag) - else: - found.parentNode.replaceChild(tag, found) - # for frame in rml_frames: - # tag.appendChild(frame) - return True - - def preprocess(self, objects, data, ids): - self.localcontext['data'] = data - self.localcontext['objects'] = objects - self.datas = data - self.ids = ids - self.objects = objects - - def _parse(self, rml_dom, objects, data, header=0): - self.node_context = {} - self.dom = rml_dom - self._node = self.dom.documentElement - if header: - self._add_header(self._node, header) - self._parse_node() - res = self.dom.documentElement.toxml('utf-8') - return res + def __init__(self, cr, uid, name, parents=rml_parents, tag=rml_tag, context=None): + if not context: + context={} + self.cr = cr + self.uid = uid + self.pool = pooler.get_pool(cr.dbname) + user = self.pool.get('res.users').browse(cr, uid, uid) + self.localcontext = { + 'user': user, + 'company': user.company_id, + 'repeatIn': self.repeatIn, + 'setLang': self.setLang, + 'setTag': self.setTag, + 'removeParentNode': self.removeParentNode, + 'format': self.format, + 'formatLang': self.formatLang, + 'logo' : user.company_id.logo, + } + self.localcontext.update(context) + self.rml_header = user.company_id.rml_header + self.rml_header2 = user.company_id.rml_header2 + self.logo = user.company_id.logo + self.name = name + self._regex = re.compile('\[\[(.+?)\]\]') + self._transl_regex = re.compile('(\[\[.+?\]\])') + self._node = None + self.parents = parents + self.tag = tag + self._lang_cache = {} +# self.already = {} + + def setTag(self, oldtag, newtag, attrs=None): + if not attrs: + attrs={} + node = self._find_parent(self._node, [oldtag]) + if node: + node.tagName = newtag + for key, val in attrs.items(): + node.setAttribute(key, val) + return None + + def format(self, text, oldtag=None): + if not oldtag: + oldtag = self.tag + self._node.data = '' + node = self._find_parent(self._node, [oldtag]) + + pp = node.parentNode + ns = node.nextSibling + pp.removeChild(node) + self._node = pp + lst = text.split('\n') + if not len(lst): + return None + nodes = [] + for i in range(len(lst)): + newnode = node.cloneNode(1) + newnode.tagName=rml_tag + newnode.__dict__['childNodes'][0].__dict__['data'] = lst[i].decode('utf8') + if ns: + pp.insertBefore(newnode, ns) + else: + pp.appendChild(newnode) + nodes.append((i, newnode)) + + def removeParentNode(self, tag=None): + if not tag: + tag = self.tag + if self.tag == sxw_tag and rml2sxw.get(tag, False): + tag = rml2sxw[tag] + node = self._find_parent(self._node, [tag]) + if node: + parentNode = node.parentNode + parentNode.removeChild(node) + self._node = parentNode + + def setLang(self, lang): + self.localcontext['lang'] = lang + for obj in self.objects: + obj._context['lang'] = lang + for table in obj._cache: + for id in obj._cache[table]: + self._lang_cache.setdefault(obj._context['lang'], {}).setdefault(table, + {}).update(obj._cache[table][id]) + if lang in self._lang_cache \ + and table in self._lang_cache[lang] \ + and id in self._lang_cache[lang][table]: + obj._cache[table][id] = self._lang_cache[lang][table][id] + else: + obj._cache[table][id] = {'id': id} + + def formatLang(self, value, digit=2, date=False): + if not value: + return '' + lc, encoding = locale.getdefaultlocale() + if not encoding: + encoding = 'UTF-8' + if encoding == 'utf': + encoding = 'UTF-8' + if encoding == 'cp1252': + encoding= '1252' + lang = self.localcontext.get('lang', 'en_US') or 'en_US' + try: + if os.name == 'nt': + locale.setlocale(locale.LC_ALL, _LOCALE2WIN32.get(lang, lang) + '.' + encoding) + else: + locale.setlocale(locale.LC_ALL, lang + '.' + encoding) + except Exception: + netsvc.Logger().notifyChannel('report', netsvc.LOG_WARNING, + 'report %s: unable to set locale "%s"' % (self.name, + self.localcontext.get('lang', 'en_US') or 'en_US')) + if date: + date = time.strptime(value, DT_FORMAT) + return time.strftime(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y'), + date) + return locale.format('%.' + str(digit) + 'f', value, True) + + def repeatIn(self, lst, name, nodes_parent=False): + self._node.data = '' + node = self._find_parent(self._node, nodes_parent or self.parents) + + pp = node.parentNode + ns = node.nextSibling + pp.removeChild(node) + self._node = pp + + if not len(lst): + return None + nodes = [] + for i in range(len(lst)): + newnode = node.cloneNode(1) + if ns: + pp.insertBefore(newnode, ns) + else: + pp.appendChild(newnode) + nodes.append((i, newnode)) + for i, node in nodes: + self.node_context[node] = {name: lst[i]} + return None + + def _eval(self, expr): + try: + res = eval(expr, self.localcontext) + if not res: + res = '' + except Exception,e: + import traceback, sys + tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) + netsvc.Logger().notifyChannel('report', netsvc.LOG_ERROR, + 'report %s:\n%s\n%s\nexpr: %s' % (self.name, tb_s, str(e), + expr.encode('utf-8'))) + res = '' + return res + + def _find_parent(self, node, parents): + while True: + if not node.parentNode: + return False + node = node.parentNode + if node.nodeType == node.ELEMENT_NODE and node.localName in parents: + break + return node + + def _parse_text(self, text, level=None): + if not level: + level=[] + res = self._regex.findall(text) + todo = [] + # translate the text + # the "split [[]] if not match [[]]" is not very nice, but I + # don't see how I could do it better... + # what I'd like to do is a re.sub(NOT pattern, func, string) + # but I don't know how to do that... + # translate the RML file + if 'lang' in self.localcontext: + lang = self.localcontext['lang'] + if lang and text and not text.isspace(): + transl_obj = self.pool.get('ir.translation') + piece_list = self._transl_regex.split(text) + for pn in range(len(piece_list)): + if not self._transl_regex.match(piece_list[pn]): + source_string = piece_list[pn].replace('\n', ' ').strip() + if len(source_string): + translated_string = transl_obj._get_source(self.cr, self.uid, self.name, 'rml', lang, source_string) + if translated_string: + piece_list[pn] = piece_list[pn].replace(source_string, translated_string.decode('utf8')) + text = ''.join(piece_list) + for key in res: + newtext = self._eval(key) + for i in range(len(level)): + if isinstance(newtext, list): + newtext = newtext[level[i]] + if isinstance(newtext, list): + todo.append((key, newtext)) + else: + #if not isinstance(newtext, basestring): + newtext = str(newtext) + # if there are two [[]] blocks the same, it will replace both + # but it's ok because it should evaluate to the same thing + # anyway + text = text.replace('[['+key+']]', newtext.decode('utf8')) + self._node.data = text + if len(todo): + for key, newtext in todo: + parent_node = self._find_parent(self._node, parents) + assert parents.get(parent_node.localName, False), 'No parent node found !' + nodes = [parent_node] + for i in range(len(newtext) - 1): + newnode = parent_node.cloneNode(1) + if parents.get(parent_node.localName, False): + n = parent_node.parentNode + parent_node.parentNode.insertAfter(newnode, parent_node) + nodes.append(newnode) + return False + return text + + def _parse_node(self): + level = [] + while True: + if self._node.nodeType==self._node.ELEMENT_NODE: + if self._node.hasAttribute('expr'): + newattrs = self._eval(self._node.getAttribute('expr')) + for key,val in newattrs.items(): + self._node.setAttribute(key,val) + + if self._node.hasChildNodes(): + self._node = self._node.firstChild + elif self._node.nextSibling: + self._node = self._node.nextSibling + else: + while self._node and not self._node.nextSibling: + self._node = self._node.parentNode + if not self._node: + break + self._node = self._node.nextSibling + if self._node in self.node_context: + self.localcontext.update(self.node_context[self._node]) + if self._node.nodeType in (self._node.CDATA_SECTION_NODE, self._node.TEXT_NODE): +# if self._node in self.already: +# self.already[self._node] += 1 +# print "second pass!", self.already[self._node], '---%s---' % self._node.data +# else: +# self.already[self._node] = 0 + self._parse_text(self._node.data, level) + return True + + def _find_node(self, node, localname): + if node.localName==localname: + return node + for tag in node.childNodes: + if tag.nodeType==tag.ELEMENT_NODE: + found = self._find_node(tag, localname) + if found: + return found + return False + + def _add_header(self, node, header=1): + if header==2: + rml_head = self.rml_header2 + else: + rml_head = self.rml_header + + # Refactor this patch, to use the minidom interface + if self.logo and (rml_head.find('company.logo')<0 or rml_head.find('<image')<0) and rml_head.find('<!--image')<0: + rml_head = rml_head.replace('<pageGraphics>','''<pageGraphics> <image x="10" y="26cm" height="70" width="90" >[[company.logo]] </image> ''') + if not self.logo and rml_head.find('company.logo')>=0: + rml_head = rml_head.replace('<image','<!--image') + rml_head = rml_head.replace('</image>','</image-->') + + head_dom = xml.dom.minidom.parseString(rml_head) + #for frame in head_dom.getElementsByTagName('frame'): + # frame.parentNode.removeChild(frame) + node2 = head_dom.documentElement + for tag in node2.childNodes: + if tag.nodeType==tag.ELEMENT_NODE: + found = self._find_node(node, tag.localName) + # rml_frames = found.getElementsByTagName('frame') + if found: + if tag.hasAttribute('position') and (tag.getAttribute('position')=='inside'): + found.appendChild(tag) + else: + found.parentNode.replaceChild(tag, found) + # for frame in rml_frames: + # tag.appendChild(frame) + return True + + def preprocess(self, objects, data, ids): + self.localcontext['data'] = data + self.localcontext['objects'] = objects + self.datas = data + self.ids = ids + self.objects = objects + + def _parse(self, rml_dom, objects, data, header=0): + self.node_context = {} + self.dom = rml_dom + self._node = self.dom.documentElement + if header: + self._add_header(self._node, header) + self._parse_node() + res = self.dom.documentElement.toxml('utf-8') + return res class report_sxw(report_rml): - def __init__(self, name, table, rml, parser=rml_parse, header=True): - report_rml.__init__(self, name, table, rml, '') - self.name = name - self.parser = parser - self.header = header - - def getObjects(self, cr, uid, ids, context): - table_obj = pooler.get_pool(cr.dbname).get(self.table) - return table_obj.browse(cr, uid, ids, list_class=browse_record_list, context=context, fields_process=_fields_process) - - def create(self, cr, uid, ids, data, context=None): - logo = None - if not context: - context={} - context = context.copy() - pool = pooler.get_pool(cr.dbname) - ir_actions_report_xml_obj = pool.get('ir.actions.report.xml') - report_xml_ids = ir_actions_report_xml_obj.search(cr, uid, - [('report_name', '=', self.name[7:])], context=context) - report_type = 'pdf' - report_xml = None - if report_xml_ids: - report_xml = ir_actions_report_xml_obj.browse(cr, uid, report_xml_ids[0], - context=context) - rml = report_xml.report_rml_content - report_type = report_xml.report_type - else: - rml = tools.file_open(self.tmpl, subdir=None).read() - report_type= data.get('report_type', report_type) - - if report_type == 'sxw' and report_xml: - context['parents'] = sxw_parents - sxw_io = StringIO.StringIO(report_xml.report_sxw_content) - sxw_z = zipfile.ZipFile(sxw_io, mode='r') - rml = sxw_z.read('content.xml') - meta = sxw_z.read('meta.xml') - sxw_z.close() - rml_parser = self.parser(cr, uid, self.name2, context) - rml_parser.parents = sxw_parents - rml_parser.tag = sxw_tag - objs = self.getObjects(cr, uid, ids, context) - rml_parser.preprocess(objs, data, ids) - rml_dom = xml.dom.minidom.parseString(rml) - - node = rml_dom.documentElement - - elements = node.getElementsByTagName("text:p") - - for pe in elements: - e = pe.getElementsByTagName("text:drop-down") - for de in e: - pp=de.parentNode - for cnd in de.childNodes: - if cnd.nodeType in (cnd.CDATA_SECTION_NODE, cnd.TEXT_NODE): - pe.appendChild(cnd) - pp.removeChild(de) - - - - # Add Information : Resource ID and Model - rml_dom_meta = xml.dom.minidom.parseString(meta) - node = rml_dom_meta.documentElement - elements = node.getElementsByTagName("meta:user-defined") - for pe in elements: - if pe.hasAttribute("meta:name"): - if pe.getAttribute("meta:name") == "Info 3": - pe.childNodes[0].data=data['id'] - if pe.getAttribute("meta:name") == "Info 4": - pe.childNodes[0].data=data['model'] - meta = rml_dom_meta.documentElement.toxml('utf-8') - - rml2 = rml_parser._parse(rml_dom, objs, data, header=self.header) - sxw_z = zipfile.ZipFile(sxw_io, mode='a') - sxw_z.writestr('content.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \ - rml2) - sxw_z.writestr('meta.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \ - meta) - - if self.header: - #Add corporate header/footer - rml = tools.file_open('custom/corporate_sxw_header.xml').read() - rml_parser = self.parser(cr, uid, self.name2, context) - rml_parser.parents = sxw_parents - rml_parser.tag = sxw_tag - objs = self.getObjects(cr, uid, ids, context) - rml_parser.preprocess(objs, data, ids) - rml_dom = xml.dom.minidom.parseString(rml) - rml2 = rml_parser._parse(rml_dom, objs, data, header=self.header) - sxw_z.writestr('styles.xml',"<?xml version='1.0' encoding='UTF-8'?>" + \ - rml2) - sxw_z.close() - rml2 = sxw_io.getvalue() - sxw_io.close() - else: - context['parents'] = rml_parents - rml_parser = self.parser(cr, uid, self.name2, context) - rml_parser.parents = rml_parents - rml_parser.tag = rml_tag - objs = self.getObjects(cr, uid, ids, context) - rml_parser.preprocess(objs, data, ids) - rml_dom = xml.dom.minidom.parseString(rml) - rml2 = rml_parser._parse(rml_dom, objs, data, header=self.header) - #if os.name != "nt": - # f = file("/tmp/debug.rml", "w") - # f.write(rml2) - # f.close() - if rml_parser.logo: - logo = base64.decodestring(rml_parser.logo) - - create_doc = self.generators[report_type] - pdf = create_doc(rml2, logo) - return (pdf, report_type) + def __init__(self, name, table, rml, parser=rml_parse, header=True): + report_rml.__init__(self, name, table, rml, '') + self.name = name + self.parser = parser + self.header = header + + def getObjects(self, cr, uid, ids, context): + table_obj = pooler.get_pool(cr.dbname).get(self.table) + return table_obj.browse(cr, uid, ids, list_class=browse_record_list, context=context, fields_process=_fields_process) + + def create(self, cr, uid, ids, data, context=None): + logo = None + if not context: + context={} + context = context.copy() + pool = pooler.get_pool(cr.dbname) + ir_actions_report_xml_obj = pool.get('ir.actions.report.xml') + report_xml_ids = ir_actions_report_xml_obj.search(cr, uid, + [('report_name', '=', self.name[7:])], context=context) + report_type = 'pdf' + report_xml = None + if report_xml_ids: + report_xml = ir_actions_report_xml_obj.browse(cr, uid, report_xml_ids[0], + context=context) + rml = report_xml.report_rml_content + report_type = report_xml.report_type + else: + rml = tools.file_open(self.tmpl, subdir=None).read() + report_type= data.get('report_type', report_type) + + if report_type == 'sxw' and report_xml: + context['parents'] = sxw_parents + sxw_io = StringIO.StringIO(report_xml.report_sxw_content) + sxw_z = zipfile.ZipFile(sxw_io, mode='r') + rml = sxw_z.read('content.xml') + meta = sxw_z.read('meta.xml') + sxw_z.close() + rml_parser = self.parser(cr, uid, self.name2, context) + rml_parser.parents = sxw_parents + rml_parser.tag = sxw_tag + objs = self.getObjects(cr, uid, ids, context) + rml_parser.preprocess(objs, data, ids) + rml_dom = xml.dom.minidom.parseString(rml) + + node = rml_dom.documentElement + + elements = node.getElementsByTagName("text:p") + + for pe in elements: + e = pe.getElementsByTagName("text:drop-down") + for de in e: + pp=de.parentNode + for cnd in de.childNodes: + if cnd.nodeType in (cnd.CDATA_SECTION_NODE, cnd.TEXT_NODE): + pe.appendChild(cnd) + pp.removeChild(de) + + + + # Add Information : Resource ID and Model + rml_dom_meta = xml.dom.minidom.parseString(meta) + node = rml_dom_meta.documentElement + elements = node.getElementsByTagName("meta:user-defined") + for pe in elements: + if pe.hasAttribute("meta:name"): + if pe.getAttribute("meta:name") == "Info 3": + pe.childNodes[0].data=data['id'] + if pe.getAttribute("meta:name") == "Info 4": + pe.childNodes[0].data=data['model'] + meta = rml_dom_meta.documentElement.toxml('utf-8') + + rml2 = rml_parser._parse(rml_dom, objs, data, header=self.header) + sxw_z = zipfile.ZipFile(sxw_io, mode='a') + sxw_z.writestr('content.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \ + rml2) + sxw_z.writestr('meta.xml', "<?xml version='1.0' encoding='UTF-8'?>" + \ + meta) + + if self.header: + #Add corporate header/footer + rml = tools.file_open('custom/corporate_sxw_header.xml').read() + rml_parser = self.parser(cr, uid, self.name2, context) + rml_parser.parents = sxw_parents + rml_parser.tag = sxw_tag + objs = self.getObjects(cr, uid, ids, context) + rml_parser.preprocess(objs, data, ids) + rml_dom = xml.dom.minidom.parseString(rml) + rml2 = rml_parser._parse(rml_dom, objs, data, header=self.header) + sxw_z.writestr('styles.xml',"<?xml version='1.0' encoding='UTF-8'?>" + \ + rml2) + sxw_z.close() + rml2 = sxw_io.getvalue() + sxw_io.close() + else: + context['parents'] = rml_parents + rml_parser = self.parser(cr, uid, self.name2, context) + rml_parser.parents = rml_parents + rml_parser.tag = rml_tag + objs = self.getObjects(cr, uid, ids, context) + rml_parser.preprocess(objs, data, ids) + rml_dom = xml.dom.minidom.parseString(rml) + rml2 = rml_parser._parse(rml_dom, objs, data, header=self.header) + #if os.name != "nt": + # f = file("/tmp/debug.rml", "w") + # f.write(rml2) + # f.close() + if rml_parser.logo: + logo = base64.decodestring(rml_parser.logo) + + create_doc = self.generators[report_type] + pdf = create_doc(rml2, logo) + return (pdf, report_type) diff --git a/bin/service/security.py b/bin/service/security.py index a1a26ebed28..629f27b050d 100644 --- a/bin/service/security.py +++ b/bin/service/security.py @@ -32,44 +32,44 @@ import tools _uid_cache = {} def login(db, login, password): - cr = pooler.get_db(db).cursor() - cr.execute('select id from res_users where login=%s and password=%s and active', (login.encode('utf-8'), password.encode('utf-8'))) - res = cr.fetchone() - cr.close() - if res: - return res[0] - else: - return False + cr = pooler.get_db(db).cursor() + cr.execute('select id from res_users where login=%s and password=%s and active', (login.encode('utf-8'), password.encode('utf-8'))) + res = cr.fetchone() + cr.close() + if res: + return res[0] + else: + return False def check_super(passwd): - if passwd == tools.config['admin_passwd']: - return True - else: - raise Exception('AccessDenied') - + if passwd == tools.config['admin_passwd']: + return True + else: + raise Exception('AccessDenied') + def check(db, uid, passwd): - if _uid_cache.get(db, {}).get(uid) == passwd: - return True - - cr = pooler.get_db(db).cursor() - cr.execute('select count(*) from res_users where id=%d and password=%s', (int(uid), passwd)) - res = cr.fetchone()[0] - cr.close() - if not bool(res): - raise Exception('AccessDenied') - if res: - if _uid_cache.has_key(db): - ulist = _uid_cache[db] - ulist[uid] = passwd - else: - _uid_cache[db] = {uid:passwd} - return bool(res) + if _uid_cache.get(db, {}).get(uid) == passwd: + return True + + cr = pooler.get_db(db).cursor() + cr.execute('select count(*) from res_users where id=%d and password=%s', (int(uid), passwd)) + res = cr.fetchone()[0] + cr.close() + if not bool(res): + raise Exception('AccessDenied') + if res: + if _uid_cache.has_key(db): + ulist = _uid_cache[db] + ulist[uid] = passwd + else: + _uid_cache[db] = {uid:passwd} + return bool(res) def access(db, uid, passwd, sec_level, ids): - cr = pooler.get_db(db).cursor() - cr.execute('select id from res_users where id=%s and password=%s', (uid, passwd)) - res = cr.fetchone() - cr.close() - if not res: - raise Exception('Bad username or password') - return res[0] + cr = pooler.get_db(db).cursor() + cr.execute('select id from res_users where id=%s and password=%s', (uid, passwd)) + res = cr.fetchone() + cr.close() + if not res: + raise Exception('Bad username or password') + return res[0] diff --git a/bin/service/web_services.py b/bin/service/web_services.py index 896757f2be4..7d431a76a6d 100644 --- a/bin/service/web_services.py +++ b/bin/service/web_services.py @@ -45,292 +45,292 @@ from tools.translate import _ logging.basicConfig() class db(netsvc.Service): - def __init__(self, name="db"): - netsvc.Service.__init__(self, name) - self.joinGroup("web-services") - self.exportMethod(self.create) - self.exportMethod(self.get_progress) - self.exportMethod(self.drop) - self.exportMethod(self.dump) - self.exportMethod(self.restore) - self.exportMethod(self.list) - self.exportMethod(self.list_lang) - self.exportMethod(self.change_admin_password) - self.actions = {} - self.id = 0 - self.id_protect = threading.Semaphore() - - def create(self, password, db_name, demo, lang): - security.check_super(password) - self.id_protect.acquire() - self.id += 1 - id = self.id - self.id_protect.release() - - self.actions[id] = {'clean': False} - - db = sql_db.db_connect('template1', serialize=1) - db.truedb.autocommit() - cr = db.cursor() - cr.execute('CREATE DATABASE ' + db_name + ' ENCODING \'unicode\'') - cr.close() - class DBInitialize(object): - def __call__(self, serv, id, db_name, demo, lang): - try: - serv.actions[id]['progress'] = 0 - clean = False - cr = sql_db.db_connect(db_name).cursor() - tools.init_db(cr) - cr.commit() - cr.close() - cr = None - pool = pooler.get_pool(db_name, demo,serv.actions[id], - update_module=True) - if lang and lang != 'en_US': - filename = tools.config["root_path"] + "/i18n/" + lang + ".csv" - tools.trans_load(db_name, filename, lang) - serv.actions[id]['clean'] = True - cr = sql_db.db_connect(db_name).cursor() - cr.execute('select login, password, name ' \ - 'from res_users ' \ - 'where login <> \'root\' order by login') - serv.actions[id]['users'] = cr.dictfetchall() - cr.close() - except Exception, e: - serv.actions[id]['clean'] = False - serv.actions[id]['exception'] = e - from StringIO import StringIO - import traceback - e_str = StringIO() - traceback.print_exc(file=e_str) - traceback_str = e_str.getvalue() - e_str.close() - print traceback_str - serv.actions[id]['traceback'] = traceback_str - if cr: - cr.close() - logger = netsvc.Logger() - logger.notifyChannel("web-services", netsvc.LOG_INFO, - 'CREATE DB: %s' % (db_name)) - dbi = DBInitialize() - create_thread = threading.Thread(target=dbi, - args=(self, id, db_name, demo, lang)) - create_thread.start() - self.actions[id]['thread'] = create_thread - return id - - def get_progress(self, password, id): - security.check_super(password) - if self.actions[id]['thread'].isAlive(): -# return addons.init_progress[db_name] - return (min(self.actions[id].get('progress', 0),0.95), []) - else: - clean = self.actions[id]['clean'] - if clean: - users = self.actions[id]['users'] - del self.actions[id] - return (1.0, users) - else: - e = self.actions[id]['exception'] - del self.actions[id] - raise Exception, e - - def drop(self, password, db_name): - security.check_super(password) - pooler.close_db(db_name) - logger = netsvc.Logger() - - db = sql_db.db_connect('template1', serialize=1) - db.truedb.autocommit() - cr = db.cursor() - try: - try: - cr.execute('DROP DATABASE ' + db_name) - except: - logger.notifyChannel("web-service", netsvc.LOG_ERROR, - 'DROP DB: %s failed' % (db_name,)) - raise - else: - logger.notifyChannel("web-services", netsvc.LOG_INFO, - 'DROP DB: %s' % (db_name)) - finally: - cr.close() - return True - - def dump(self, password, db_name): - security.check_super(password) - logger = netsvc.Logger() - - if tools.config['db_password']: - logger.notifyChannel("web-service", netsvc.LOG_ERROR, - 'DUMP DB: %s doesn\'t work with password' % (db_name,)) - raise Exception, "Couldn't dump database with password" - - cmd = ['pg_dump', '--format=c'] - if tools.config['db_user']: - cmd.append('--username=' + tools.config['db_user']) - if tools.config['db_host']: - cmd.append('--host=' + tools.config['db_host']) - if tools.config['db_port']: - cmd.append('--port=' + tools.config['db_port']) - cmd.append(db_name) - - stdin, stdout = tools.exec_pg_command_pipe(*tuple(cmd)) - stdin.close() - data = stdout.read() - res = stdout.close() - if res: - logger.notifyChannel("web-service", netsvc.LOG_ERROR, - 'DUMP DB: %s failed\n%s' % (db_name, data)) - raise Exception, "Couldn't dump database" - logger.notifyChannel("web-services", netsvc.LOG_INFO, - 'DUMP DB: %s' % (db_name)) - return base64.encodestring(data) - - def restore(self, password, db_name, data): - security.check_super(password) - logger = netsvc.Logger() - - if self.db_exist(db_name): - logger.notifyChannel("web-service", netsvc.LOG_WARNING, - 'RESTORE DB: %s already exists' % (db_name,)) - raise Exception, "Database already exists" - - if tools.config['db_password']: - logger.notifyChannel("web-service", netsvc.LOG_ERROR, - 'RESTORE DB: %s doesn\'t work with password' % (db_name,)) - raise Exception, "Couldn't restore database with password" - - db = sql_db.db_connect('template1', serialize=1) - db.truedb.autocommit() - cr = db.cursor() - cr.execute('CREATE DATABASE ' + db_name + ' ENCODING \'unicode\'') - cr.close() - - cmd = ['pg_restore'] - if tools.config['db_user']: - cmd.append('--username=' + tools.config['db_user']) - if tools.config['db_host']: - cmd.append('--host=' + tools.config['db_host']) - if tools.config['db_port']: - cmd.append('--port=' + tools.config['db_port']) - cmd.append('--dbname=' + db_name) - args2 = tuple(cmd) - - buf=base64.decodestring(data) - if os.name == "nt": - tmpfile = (os.environ['TMP'] or 'C:\\') + os.tmpnam() - file(tmpfile, 'wb').write(buf) - args2=list(args2) - args2.append(' ' + tmpfile) - args2=tuple(args2) - stdin, stdout = tools.exec_pg_command_pipe(*args2) - if not os.name == "nt": - stdin.write(base64.decodestring(data)) - stdin.close() - res = stdout.close() - if res: - raise Exception, "Couldn't restore database" - logger.notifyChannel("web-services", netsvc.LOG_INFO, - 'RESTORE DB: %s' % (db_name)) - return True - - def db_exist(self, db_name): - try: - db = sql_db.db_connect(db_name) - db.truedb.close() - return True - except: - return False - - def list(self): - db = sql_db.db_connect('template1') - try: - cr = db.cursor() - db_user = tools.config["db_user"] - if not db_user and os.name == 'posix': - import pwd - db_user = pwd.getpwuid(os.getuid())[0] - if not db_user: - cr.execute("select usename from pg_user where usesysid=(select datdba from pg_database where datname=%s)", (tools.config["db_name"],)) - res = cr.fetchone() - db_user = res and res[0] - if db_user: - cr.execute("select datname from pg_database where datdba=(select usesysid from pg_user where usename=%s) and datname not in ('template0', 'template1', 'postgres')", (db_user,)) - else: - cr.execute("select datname from pg_database where datname not in('template0', 'template1','postgres')") - res = [name for (name,) in cr.fetchall()] - cr.close() - except: - res = [] - db.truedb.close() - return res - - def change_admin_password(self, old_password, new_password): - security.check_super(old_password) - tools.config['admin_passwd'] = new_password - tools.config.save() - return True - - def list_lang(self): - return tools.scan_languages() - import glob - file_list = glob.glob(os.path.join(tools.config['root_path'], 'i18n', '*.csv')) - def lang_tuple(fname): - lang_dict=tools.get_languages() - lang = os.path.basename(fname).split(".")[0] - return (lang, lang_dict.get(lang, lang)) - return [lang_tuple(fname) for fname in file_list] + def __init__(self, name="db"): + netsvc.Service.__init__(self, name) + self.joinGroup("web-services") + self.exportMethod(self.create) + self.exportMethod(self.get_progress) + self.exportMethod(self.drop) + self.exportMethod(self.dump) + self.exportMethod(self.restore) + self.exportMethod(self.list) + self.exportMethod(self.list_lang) + self.exportMethod(self.change_admin_password) + self.actions = {} + self.id = 0 + self.id_protect = threading.Semaphore() + + def create(self, password, db_name, demo, lang): + security.check_super(password) + self.id_protect.acquire() + self.id += 1 + id = self.id + self.id_protect.release() + + self.actions[id] = {'clean': False} + + db = sql_db.db_connect('template1', serialize=1) + db.truedb.autocommit() + cr = db.cursor() + cr.execute('CREATE DATABASE ' + db_name + ' ENCODING \'unicode\'') + cr.close() + class DBInitialize(object): + def __call__(self, serv, id, db_name, demo, lang): + try: + serv.actions[id]['progress'] = 0 + clean = False + cr = sql_db.db_connect(db_name).cursor() + tools.init_db(cr) + cr.commit() + cr.close() + cr = None + pool = pooler.get_pool(db_name, demo,serv.actions[id], + update_module=True) + if lang and lang != 'en_US': + filename = tools.config["root_path"] + "/i18n/" + lang + ".csv" + tools.trans_load(db_name, filename, lang) + serv.actions[id]['clean'] = True + cr = sql_db.db_connect(db_name).cursor() + cr.execute('select login, password, name ' \ + 'from res_users ' \ + 'where login <> \'root\' order by login') + serv.actions[id]['users'] = cr.dictfetchall() + cr.close() + except Exception, e: + serv.actions[id]['clean'] = False + serv.actions[id]['exception'] = e + from StringIO import StringIO + import traceback + e_str = StringIO() + traceback.print_exc(file=e_str) + traceback_str = e_str.getvalue() + e_str.close() + print traceback_str + serv.actions[id]['traceback'] = traceback_str + if cr: + cr.close() + logger = netsvc.Logger() + logger.notifyChannel("web-services", netsvc.LOG_INFO, + 'CREATE DB: %s' % (db_name)) + dbi = DBInitialize() + create_thread = threading.Thread(target=dbi, + args=(self, id, db_name, demo, lang)) + create_thread.start() + self.actions[id]['thread'] = create_thread + return id + + def get_progress(self, password, id): + security.check_super(password) + if self.actions[id]['thread'].isAlive(): +# return addons.init_progress[db_name] + return (min(self.actions[id].get('progress', 0),0.95), []) + else: + clean = self.actions[id]['clean'] + if clean: + users = self.actions[id]['users'] + del self.actions[id] + return (1.0, users) + else: + e = self.actions[id]['exception'] + del self.actions[id] + raise Exception, e + + def drop(self, password, db_name): + security.check_super(password) + pooler.close_db(db_name) + logger = netsvc.Logger() + + db = sql_db.db_connect('template1', serialize=1) + db.truedb.autocommit() + cr = db.cursor() + try: + try: + cr.execute('DROP DATABASE ' + db_name) + except: + logger.notifyChannel("web-service", netsvc.LOG_ERROR, + 'DROP DB: %s failed' % (db_name,)) + raise + else: + logger.notifyChannel("web-services", netsvc.LOG_INFO, + 'DROP DB: %s' % (db_name)) + finally: + cr.close() + return True + + def dump(self, password, db_name): + security.check_super(password) + logger = netsvc.Logger() + + if tools.config['db_password']: + logger.notifyChannel("web-service", netsvc.LOG_ERROR, + 'DUMP DB: %s doesn\'t work with password' % (db_name,)) + raise Exception, "Couldn't dump database with password" + + cmd = ['pg_dump', '--format=c'] + if tools.config['db_user']: + cmd.append('--username=' + tools.config['db_user']) + if tools.config['db_host']: + cmd.append('--host=' + tools.config['db_host']) + if tools.config['db_port']: + cmd.append('--port=' + tools.config['db_port']) + cmd.append(db_name) + + stdin, stdout = tools.exec_pg_command_pipe(*tuple(cmd)) + stdin.close() + data = stdout.read() + res = stdout.close() + if res: + logger.notifyChannel("web-service", netsvc.LOG_ERROR, + 'DUMP DB: %s failed\n%s' % (db_name, data)) + raise Exception, "Couldn't dump database" + logger.notifyChannel("web-services", netsvc.LOG_INFO, + 'DUMP DB: %s' % (db_name)) + return base64.encodestring(data) + + def restore(self, password, db_name, data): + security.check_super(password) + logger = netsvc.Logger() + + if self.db_exist(db_name): + logger.notifyChannel("web-service", netsvc.LOG_WARNING, + 'RESTORE DB: %s already exists' % (db_name,)) + raise Exception, "Database already exists" + + if tools.config['db_password']: + logger.notifyChannel("web-service", netsvc.LOG_ERROR, + 'RESTORE DB: %s doesn\'t work with password' % (db_name,)) + raise Exception, "Couldn't restore database with password" + + db = sql_db.db_connect('template1', serialize=1) + db.truedb.autocommit() + cr = db.cursor() + cr.execute('CREATE DATABASE ' + db_name + ' ENCODING \'unicode\'') + cr.close() + + cmd = ['pg_restore'] + if tools.config['db_user']: + cmd.append('--username=' + tools.config['db_user']) + if tools.config['db_host']: + cmd.append('--host=' + tools.config['db_host']) + if tools.config['db_port']: + cmd.append('--port=' + tools.config['db_port']) + cmd.append('--dbname=' + db_name) + args2 = tuple(cmd) + + buf=base64.decodestring(data) + if os.name == "nt": + tmpfile = (os.environ['TMP'] or 'C:\\') + os.tmpnam() + file(tmpfile, 'wb').write(buf) + args2=list(args2) + args2.append(' ' + tmpfile) + args2=tuple(args2) + stdin, stdout = tools.exec_pg_command_pipe(*args2) + if not os.name == "nt": + stdin.write(base64.decodestring(data)) + stdin.close() + res = stdout.close() + if res: + raise Exception, "Couldn't restore database" + logger.notifyChannel("web-services", netsvc.LOG_INFO, + 'RESTORE DB: %s' % (db_name)) + return True + + def db_exist(self, db_name): + try: + db = sql_db.db_connect(db_name) + db.truedb.close() + return True + except: + return False + + def list(self): + db = sql_db.db_connect('template1') + try: + cr = db.cursor() + db_user = tools.config["db_user"] + if not db_user and os.name == 'posix': + import pwd + db_user = pwd.getpwuid(os.getuid())[0] + if not db_user: + cr.execute("select usename from pg_user where usesysid=(select datdba from pg_database where datname=%s)", (tools.config["db_name"],)) + res = cr.fetchone() + db_user = res and res[0] + if db_user: + cr.execute("select datname from pg_database where datdba=(select usesysid from pg_user where usename=%s) and datname not in ('template0', 'template1', 'postgres')", (db_user,)) + else: + cr.execute("select datname from pg_database where datname not in('template0', 'template1','postgres')") + res = [name for (name,) in cr.fetchall()] + cr.close() + except: + res = [] + db.truedb.close() + return res + + def change_admin_password(self, old_password, new_password): + security.check_super(old_password) + tools.config['admin_passwd'] = new_password + tools.config.save() + return True + + def list_lang(self): + return tools.scan_languages() + import glob + file_list = glob.glob(os.path.join(tools.config['root_path'], 'i18n', '*.csv')) + def lang_tuple(fname): + lang_dict=tools.get_languages() + lang = os.path.basename(fname).split(".")[0] + return (lang, lang_dict.get(lang, lang)) + return [lang_tuple(fname) for fname in file_list] db() class common(netsvc.Service): - def __init__(self,name="common"): - netsvc.Service.__init__(self,name) - self.joinGroup("web-services") - self.exportMethod(self.ir_get) - self.exportMethod(self.ir_set) - self.exportMethod(self.ir_del) - self.exportMethod(self.about) - self.exportMethod(self.login) - self.exportMethod(self.timezone_get) - - def ir_set(self, db, uid, password, keys, args, name, value, replace=True, isobject=False): - security.check(db, uid, password) - cr = pooler.get_db(db).cursor() - res = ir.ir_set(cr,uid, keys, args, name, value, replace, isobject) - cr.commit() - cr.close() - return res - - def ir_del(self, db, uid, password, id): - security.check(db, uid, password) - cr = pooler.get_db(db).cursor() - res = ir.ir_del(cr,uid, id) - cr.commit() - cr.close() - return res - - def ir_get(self, db, uid, password, keys, args=None, meta=None, context=None): - if not args: - args=[] - if not context: - context={} - security.check(db, uid, password) - cr = pooler.get_db(db).cursor() - res = ir.ir_get(cr,uid, keys, args, meta, context) - cr.commit() - cr.close() - return res - - def login(self, db, login, password): - res = security.login(db, login, password) - logger = netsvc.Logger() - msg = res and 'successful login' or 'bad login or password' - logger.notifyChannel("web-service", netsvc.LOG_INFO, "%s from '%s' using database '%s'" % (msg, login, db)) - return res or False - - def about(self): - return _(''' + def __init__(self,name="common"): + netsvc.Service.__init__(self,name) + self.joinGroup("web-services") + self.exportMethod(self.ir_get) + self.exportMethod(self.ir_set) + self.exportMethod(self.ir_del) + self.exportMethod(self.about) + self.exportMethod(self.login) + self.exportMethod(self.timezone_get) + + def ir_set(self, db, uid, password, keys, args, name, value, replace=True, isobject=False): + security.check(db, uid, password) + cr = pooler.get_db(db).cursor() + res = ir.ir_set(cr,uid, keys, args, name, value, replace, isobject) + cr.commit() + cr.close() + return res + + def ir_del(self, db, uid, password, id): + security.check(db, uid, password) + cr = pooler.get_db(db).cursor() + res = ir.ir_del(cr,uid, id) + cr.commit() + cr.close() + return res + + def ir_get(self, db, uid, password, keys, args=None, meta=None, context=None): + if not args: + args=[] + if not context: + context={} + security.check(db, uid, password) + cr = pooler.get_db(db).cursor() + res = ir.ir_get(cr,uid, keys, args, meta, context) + cr.commit() + cr.close() + return res + + def login(self, db, login, password): + res = security.login(db, login, password) + logger = netsvc.Logger() + msg = res and 'successful login' or 'bad login or password' + logger.notifyChannel("web-service", netsvc.LOG_INFO, "%s from '%s' using database '%s'" % (msg, login, db)) + return res or False + + def about(self): + return _(''' OpenERP is an ERP+CRM program for small and medium businesses. @@ -339,35 +339,35 @@ GNU Public Licence. (c) 2003-TODAY, Fabien Pinckaers - Tiny sprl''') - def timezone_get(self, db, login, password): - return time.tzname[0] + def timezone_get(self, db, login, password): + return time.tzname[0] common() class objects_proxy(netsvc.Service): - def __init__(self, name="object"): - netsvc.Service.__init__(self,name) - self.joinGroup('web-services') - self.exportMethod(self.execute) - self.exportMethod(self.exec_workflow) - self.exportMethod(self.obj_list) - - def exec_workflow(self, db, uid, passwd, object, method, id): - security.check(db, uid, passwd) - service = netsvc.LocalService("object_proxy") - res = service.exec_workflow(db, uid, object, method, id) - return res - - def execute(self, db, uid, passwd, object, method, *args): - security.check(db, uid, passwd) - service = netsvc.LocalService("object_proxy") - res = service.execute(db, uid, object, method, *args) - return res - - def obj_list(self, db, uid, passwd): - security.check(db, uid, passwd) - service = netsvc.LocalService("object_proxy") - res = service.obj_list() - return res + def __init__(self, name="object"): + netsvc.Service.__init__(self,name) + self.joinGroup('web-services') + self.exportMethod(self.execute) + self.exportMethod(self.exec_workflow) + self.exportMethod(self.obj_list) + + def exec_workflow(self, db, uid, passwd, object, method, id): + security.check(db, uid, passwd) + service = netsvc.LocalService("object_proxy") + res = service.exec_workflow(db, uid, object, method, id) + return res + + def execute(self, db, uid, passwd, object, method, *args): + security.check(db, uid, passwd) + service = netsvc.LocalService("object_proxy") + res = service.execute(db, uid, object, method, *args) + return res + + def obj_list(self, db, uid, passwd): + security.check(db, uid, passwd) + service = netsvc.LocalService("object_proxy") + res = service.obj_list() + return res objects_proxy() @@ -383,44 +383,44 @@ objects_proxy() # TODO: change local request to OSE request/reply pattern # class wizard(netsvc.Service): - def __init__(self, name='wizard'): - netsvc.Service.__init__(self,name) - self.joinGroup('web-services') - self.exportMethod(self.execute) - self.exportMethod(self.create) - self.id = 0 - self.wiz_datas = {} - self.wiz_name = {} - self.wiz_uid = {} - - def _execute(self, db, uid, wiz_id, datas, action, context): - self.wiz_datas[wiz_id].update(datas) - wiz = netsvc.LocalService('wizard.'+self.wiz_name[wiz_id]) - return wiz.execute(db, uid, self.wiz_datas[wiz_id], action, context) - - def create(self, db, uid, passwd, wiz_name, datas=None): - if not datas: - datas={} - security.check(db, uid, passwd) + def __init__(self, name='wizard'): + netsvc.Service.__init__(self,name) + self.joinGroup('web-services') + self.exportMethod(self.execute) + self.exportMethod(self.create) + self.id = 0 + self.wiz_datas = {} + self.wiz_name = {} + self.wiz_uid = {} + + def _execute(self, db, uid, wiz_id, datas, action, context): + self.wiz_datas[wiz_id].update(datas) + wiz = netsvc.LocalService('wizard.'+self.wiz_name[wiz_id]) + return wiz.execute(db, uid, self.wiz_datas[wiz_id], action, context) + + def create(self, db, uid, passwd, wiz_name, datas=None): + if not datas: + datas={} + security.check(db, uid, passwd) #FIXME: this is not thread-safe - self.id += 1 - self.wiz_datas[self.id] = {} - self.wiz_name[self.id] = wiz_name - self.wiz_uid[self.id] = uid - return self.id - - def execute(self, db, uid, passwd, wiz_id, datas, action='init', context=None): - if not context: - context={} - security.check(db, uid, passwd) - - if wiz_id in self.wiz_uid: - if self.wiz_uid[wiz_id] == uid: - return self._execute(db, uid, wiz_id, datas, action, context) - else: - raise Exception, 'AccessDenied' - else: - raise Exception, 'WizardNotFound' + self.id += 1 + self.wiz_datas[self.id] = {} + self.wiz_name[self.id] = wiz_name + self.wiz_uid[self.id] = uid + return self.id + + def execute(self, db, uid, passwd, wiz_id, datas, action='init', context=None): + if not context: + context={} + security.check(db, uid, passwd) + + if wiz_id in self.wiz_uid: + if self.wiz_uid[wiz_id] == uid: + return self._execute(db, uid, wiz_id, datas, action, context) + else: + raise Exception, 'AccessDenied' + else: + raise Exception, 'WizardNotFound' wizard() # @@ -430,85 +430,85 @@ wizard() # False -> True # class report_spool(netsvc.Service): - def __init__(self, name='report'): - netsvc.Service.__init__(self, name) - self.joinGroup('web-services') - self.exportMethod(self.report) - self.exportMethod(self.report_get) - self._reports = {} - self.id = 0 - self.id_protect = threading.Semaphore() - - def report(self, db, uid, passwd, object, ids, datas=None, context=None): - if not datas: - datas={} - if not context: - context={} - security.check(db, uid, passwd) - - self.id_protect.acquire() - self.id += 1 - id = self.id - self.id_protect.release() - - self._reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None} - - def go(id, uid, ids, datas, context): - try: - cr = pooler.get_db(db).cursor() - obj = netsvc.LocalService('report.'+object) - (result, format) = obj.create(cr, uid, ids, datas, context) - cr.close() - self._reports[id]['result'] = result - self._reports[id]['format'] = format - self._reports[id]['state'] = True - except Exception, exception: - import traceback - import sys - tb_s = reduce(lambda x, y: x+y, traceback.format_exception( - sys.exc_type, sys.exc_value, sys.exc_traceback)) - logger = netsvc.Logger() - logger.notifyChannel('web-service', netsvc.LOG_ERROR, - 'Exception: %s\n%s' % (str(exception), tb_s)) - self._reports[id]['exception'] = exception - self._reports[id]['state'] = True - return True - - thread.start_new_thread(go, (id, uid, ids, datas, context)) - return id - - def _check_report(self, report_id): - result = self._reports[report_id] - if result['exception']: - raise result['exception'] - res = {'state': result['state']} - if res['state']: - if tools.config['reportgz']: - import zlib - res2 = zlib.compress(result['result']) - res['code'] = 'zlib' - else: - #CHECKME: why is this needed??? - if isinstance(result['result'], unicode): - res2 = result['result'].encode('latin1', 'replace') - else: - res2 = result['result'] - if res2: - res['result'] = base64.encodestring(res2) - res['format'] = result['format'] - del self._reports[report_id] - return res - - def report_get(self, db, uid, passwd, report_id): - security.check(db, uid, passwd) - - if report_id in self._reports: - if self._reports[report_id]['uid'] == uid: - return self._check_report(report_id) - else: - raise Exception, 'AccessDenied' - else: - raise Exception, 'ReportNotFound' + def __init__(self, name='report'): + netsvc.Service.__init__(self, name) + self.joinGroup('web-services') + self.exportMethod(self.report) + self.exportMethod(self.report_get) + self._reports = {} + self.id = 0 + self.id_protect = threading.Semaphore() + + def report(self, db, uid, passwd, object, ids, datas=None, context=None): + if not datas: + datas={} + if not context: + context={} + security.check(db, uid, passwd) + + self.id_protect.acquire() + self.id += 1 + id = self.id + self.id_protect.release() + + self._reports[id] = {'uid': uid, 'result': False, 'state': False, 'exception': None} + + def go(id, uid, ids, datas, context): + try: + cr = pooler.get_db(db).cursor() + obj = netsvc.LocalService('report.'+object) + (result, format) = obj.create(cr, uid, ids, datas, context) + cr.close() + self._reports[id]['result'] = result + self._reports[id]['format'] = format + self._reports[id]['state'] = True + except Exception, exception: + import traceback + import sys + tb_s = reduce(lambda x, y: x+y, traceback.format_exception( + sys.exc_type, sys.exc_value, sys.exc_traceback)) + logger = netsvc.Logger() + logger.notifyChannel('web-service', netsvc.LOG_ERROR, + 'Exception: %s\n%s' % (str(exception), tb_s)) + self._reports[id]['exception'] = exception + self._reports[id]['state'] = True + return True + + thread.start_new_thread(go, (id, uid, ids, datas, context)) + return id + + def _check_report(self, report_id): + result = self._reports[report_id] + if result['exception']: + raise result['exception'] + res = {'state': result['state']} + if res['state']: + if tools.config['reportgz']: + import zlib + res2 = zlib.compress(result['result']) + res['code'] = 'zlib' + else: + #CHECKME: why is this needed??? + if isinstance(result['result'], unicode): + res2 = result['result'].encode('latin1', 'replace') + else: + res2 = result['result'] + if res2: + res['result'] = base64.encodestring(res2) + res['format'] = result['format'] + del self._reports[report_id] + return res + + def report_get(self, db, uid, passwd, report_id): + security.check(db, uid, passwd) + + if report_id in self._reports: + if self._reports[report_id]['uid'] == uid: + return self._check_report(report_id) + else: + raise Exception, 'AccessDenied' + else: + raise Exception, 'ReportNotFound' report_spool() diff --git a/bin/sql_db.py b/bin/sql_db.py index 33e4d1a3b1a..e089280a24f 100644 --- a/bin/sql_db.py +++ b/bin/sql_db.py @@ -31,9 +31,9 @@ import tools import sys,os #try: -# import decimal +# import decimal #except ImportError: -# from tools import decimal +# from tools import decimal import re @@ -43,112 +43,112 @@ re_from = re.compile('.* from "?([a-zA-Z_0-9]+)"? .*$'); re_into = re.compile('.* into "?([a-zA-Z_0-9]+)"? .*$'); class fake_cursor: - nbr = 0 - _tables = {} - sql_from_log = {} - sql_into_log = {} - sql_log = False - count = 0 - - def __init__(self, db, con, dbname): - self.db = db - self.obj = db.cursor() - self.con = con - self.dbname = dbname - - def execute(self, sql, params=None): - if not params: - params=() - def base_string(s): - if isinstance(s, unicode): - return s.encode('utf-8') - return s - p=map(base_string, params) - if isinstance(sql, unicode): - sql = sql.encode('utf-8') - if self.sql_log: - now = mdt.now() - if p: - res = self.obj.execute(sql, p) - else: - res = self.obj.execute(sql) - if self.sql_log: - print "SQL LOG query:", sql - print "SQL LOG params:", repr(p) - self.count+=1 - res_from = re_from.match(sql.lower()) - if res_from: - self.sql_from_log.setdefault(res_from.group(1), [0, 0]) - self.sql_from_log[res_from.group(1)][0] += 1 - self.sql_from_log[res_from.group(1)][1] += mdt.now() - now - res_into = re_into.match(sql.lower()) - if res_into: - self.sql_into_log.setdefault(res_into.group(1), [0, 0]) - self.sql_into_log[res_into.group(1)][0] += 1 - self.sql_into_log[res_into.group(1)][1] += mdt.now() - now - return res - - def print_log(self, type='from'): - print "SQL LOG %s:" % (type,) - if type == 'from': - logs = self.sql_from_log.items() - else: - logs = self.sql_into_log.items() - logs.sort(lambda x, y: cmp(x[1][1], y[1][1])) - sum=0 - for r in logs: - print "table:", r[0], ":", str(r[1][1]), "/", r[1][0] - sum+= r[1][1] - print "SUM:%s/%d"% (sum, self.count) - - def close(self): - if self.sql_log: - self.print_log('from') - self.print_log('into') - self.obj.close() - - # This force the cursor to be freed, and thus, available again. It is - # important because otherwise we can overload the server very easily - # because of a cursor shortage (because cursors are not garbage - # collected as fast as they should). The problem is probably due in - # part because browse records keep a reference to the cursor. - del self.obj - - def __getattr__(self, name): - return getattr(self.obj, name) + nbr = 0 + _tables = {} + sql_from_log = {} + sql_into_log = {} + sql_log = False + count = 0 + + def __init__(self, db, con, dbname): + self.db = db + self.obj = db.cursor() + self.con = con + self.dbname = dbname + + def execute(self, sql, params=None): + if not params: + params=() + def base_string(s): + if isinstance(s, unicode): + return s.encode('utf-8') + return s + p=map(base_string, params) + if isinstance(sql, unicode): + sql = sql.encode('utf-8') + if self.sql_log: + now = mdt.now() + if p: + res = self.obj.execute(sql, p) + else: + res = self.obj.execute(sql) + if self.sql_log: + print "SQL LOG query:", sql + print "SQL LOG params:", repr(p) + self.count+=1 + res_from = re_from.match(sql.lower()) + if res_from: + self.sql_from_log.setdefault(res_from.group(1), [0, 0]) + self.sql_from_log[res_from.group(1)][0] += 1 + self.sql_from_log[res_from.group(1)][1] += mdt.now() - now + res_into = re_into.match(sql.lower()) + if res_into: + self.sql_into_log.setdefault(res_into.group(1), [0, 0]) + self.sql_into_log[res_into.group(1)][0] += 1 + self.sql_into_log[res_into.group(1)][1] += mdt.now() - now + return res + + def print_log(self, type='from'): + print "SQL LOG %s:" % (type,) + if type == 'from': + logs = self.sql_from_log.items() + else: + logs = self.sql_into_log.items() + logs.sort(lambda x, y: cmp(x[1][1], y[1][1])) + sum=0 + for r in logs: + print "table:", r[0], ":", str(r[1][1]), "/", r[1][0] + sum+= r[1][1] + print "SUM:%s/%d"% (sum, self.count) + + def close(self): + if self.sql_log: + self.print_log('from') + self.print_log('into') + self.obj.close() + + # This force the cursor to be freed, and thus, available again. It is + # important because otherwise we can overload the server very easily + # because of a cursor shortage (because cursors are not garbage + # collected as fast as they should). The problem is probably due in + # part because browse records keep a reference to the cursor. + del self.obj + + def __getattr__(self, name): + return getattr(self.obj, name) class fakedb: - def __init__(self, truedb, dbname): - self.truedb = truedb - self.dbname = dbname - - def cursor(self): - return fake_cursor(self.truedb, {}, self.dbname) + def __init__(self, truedb, dbname): + self.truedb = truedb + self.dbname = dbname + + def cursor(self): + return fake_cursor(self.truedb, {}, self.dbname) def decimalize(symb): - if symb is None: return None - if isinstance(symb, float): - return decimal.Decimal('%f' % symb) - return decimal.Decimal(symb) + if symb is None: return None + if isinstance(symb, float): + return decimal.Decimal('%f' % symb) + return decimal.Decimal(symb) def db_connect(db_name, serialize=0): - host = tools.config['db_host'] and "host=%s" % tools.config['db_host'] or '' - port = tools.config['db_port'] and "port=%s" % tools.config['db_port'] or '' - name = "dbname=%s" % db_name - user = tools.config['db_user'] and "user=%s" % tools.config['db_user'] or '' - password = tools.config['db_password'] and "password=%s" % tools.config['db_password'] or '' - maxconn = int(tools.config['db_maxconn']) or 64 - tdb = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), - serialize=serialize, maxconn=maxconn) - fdb = fakedb(tdb, db_name) - return fdb + host = tools.config['db_host'] and "host=%s" % tools.config['db_host'] or '' + port = tools.config['db_port'] and "port=%s" % tools.config['db_port'] or '' + name = "dbname=%s" % db_name + user = tools.config['db_user'] and "user=%s" % tools.config['db_user'] or '' + password = tools.config['db_password'] and "password=%s" % tools.config['db_password'] or '' + maxconn = int(tools.config['db_maxconn']) or 64 + tdb = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password), + serialize=serialize, maxconn=maxconn) + fdb = fakedb(tdb, db_name) + return fdb def init(): - #define DATEOID 1082, define TIMESTAMPOID 1114 see pgtypes.h - psycopg.register_type(psycopg.new_type((1082,), "date", lambda x:x)) - psycopg.register_type(psycopg.new_type((1083,), "time", lambda x:x)) - psycopg.register_type(psycopg.new_type((1114,), "datetime", lambda x:x)) - #psycopg.register_type(psycopg.new_type((700, 701, 1700), 'decimal', decimalize)) + #define DATEOID 1082, define TIMESTAMPOID 1114 see pgtypes.h + psycopg.register_type(psycopg.new_type((1082,), "date", lambda x:x)) + psycopg.register_type(psycopg.new_type((1083,), "time", lambda x:x)) + psycopg.register_type(psycopg.new_type((1114,), "datetime", lambda x:x)) + #psycopg.register_type(psycopg.new_type((700, 701, 1700), 'decimal', decimalize)) psycopg.register_type(psycopg.new_type((1082,), "date", lambda x:x)) psycopg.register_type(psycopg.new_type((1083,), "time", lambda x:x)) diff --git a/bin/tiny_socket.py b/bin/tiny_socket.py index ef575714c7d..843565406c7 100644 --- a/bin/tiny_socket.py +++ b/bin/tiny_socket.py @@ -30,61 +30,61 @@ import cPickle import marshal class Myexception(Exception): - def __init__(self, faultCode, faultString): - self.faultCode = faultCode - self.faultString = faultString - self.args = (faultCode, faultString) + def __init__(self, faultCode, faultString): + self.faultCode = faultCode + self.faultString = faultString + self.args = (faultCode, faultString) class mysocket: - def __init__(self, sock=None): - if sock is None: - self.sock = socket.socket( - socket.AF_INET, socket.SOCK_STREAM) - else: - self.sock = sock - self.sock.settimeout(120) - def connect(self, host, port=False): - if not port: - protocol, buf = host.split('//') - host, port = buf.split(':') - self.sock.connect((host, int(port))) - def disconnect(self): - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - def mysend(self, msg, exception=False, traceback=None): - msg = cPickle.dumps([msg,traceback]) - size = len(msg) - self.sock.send('%8d' % size) - self.sock.send(exception and "1" or "0") - totalsent = 0 - while totalsent < size: - sent = self.sock.send(msg[totalsent:]) - if sent == 0: - raise RuntimeError, "socket connection broken" - totalsent = totalsent + sent - def myreceive(self): - buf='' - while len(buf) < 8: - chunk = self.sock.recv(8 - len(buf)) - if chunk == '': - raise RuntimeError, "socket connection broken" - buf += chunk - size = int(buf) - buf = self.sock.recv(1) - if buf != "0": - exception = buf - else: - exception = False - msg = '' - while len(msg) < size: - chunk = self.sock.recv(size-len(msg)) - if chunk == '': - raise RuntimeError, "socket connection broken" - msg = msg + chunk - res = cPickle.loads(msg) - if isinstance(res[0],Exception): - if exception: - raise Myexception(str(res[0]), str(res[1])) - raise res[0] - else: - return res[0] + def __init__(self, sock=None): + if sock is None: + self.sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM) + else: + self.sock = sock + self.sock.settimeout(120) + def connect(self, host, port=False): + if not port: + protocol, buf = host.split('//') + host, port = buf.split(':') + self.sock.connect((host, int(port))) + def disconnect(self): + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + def mysend(self, msg, exception=False, traceback=None): + msg = cPickle.dumps([msg,traceback]) + size = len(msg) + self.sock.send('%8d' % size) + self.sock.send(exception and "1" or "0") + totalsent = 0 + while totalsent < size: + sent = self.sock.send(msg[totalsent:]) + if sent == 0: + raise RuntimeError, "socket connection broken" + totalsent = totalsent + sent + def myreceive(self): + buf='' + while len(buf) < 8: + chunk = self.sock.recv(8 - len(buf)) + if chunk == '': + raise RuntimeError, "socket connection broken" + buf += chunk + size = int(buf) + buf = self.sock.recv(1) + if buf != "0": + exception = buf + else: + exception = False + msg = '' + while len(msg) < size: + chunk = self.sock.recv(size-len(msg)) + if chunk == '': + raise RuntimeError, "socket connection broken" + msg = msg + chunk + res = cPickle.loads(msg) + if isinstance(res[0],Exception): + if exception: + raise Myexception(str(res[0]), str(res[1])) + raise res[0] + else: + return res[0] diff --git a/bin/tinyerp-server.py b/bin/tinyerp-server.py index c022188ff72..bd08be09346 100755..100644 --- a/bin/tinyerp-server.py +++ b/bin/tinyerp-server.py @@ -75,8 +75,8 @@ import tools import time if sys.platform=='win32': - import mx.DateTime - mx.DateTime.strptime = lambda x,y: mx.DateTime.mktime(time.strptime(x, y)) + import mx.DateTime + mx.DateTime.strptime = lambda x,y: mx.DateTime.mktime(time.strptime(x, y)) #os.chdir(tools.file_path_root) @@ -98,18 +98,18 @@ import pooler # try to connect to the database try: -# pooler.init() - pass +# pooler.init() + pass except psycopg.OperationalError, err: - logger.notifyChannel("init", netsvc.LOG_ERROR, "could not connect to database '%s'!" % (tools.config["db_name"],)) - - msg = str(err).replace("FATAL:","").strip() - db_msg = "database \"%s\" does not exist" % (tools.config["db_name"],) - - # Note: this is ugly but since psycopg only uses one exception for all errors - # I don't think it's possible to do differently - if msg == db_msg: - print """ + logger.notifyChannel("init", netsvc.LOG_ERROR, "could not connect to database '%s'!" % (tools.config["db_name"],)) + + msg = str(err).replace("FATAL:","").strip() + db_msg = "database \"%s\" does not exist" % (tools.config["db_name"],) + + # Note: this is ugly but since psycopg only uses one exception for all errors + # I don't think it's possible to do differently + if msg == db_msg: + print """ this database does not exist You need to create it using the command: @@ -126,42 +126,42 @@ Two accounts will be created by default: 2. login: demo password : demo """ % (tools.config["db_name"]) - else: - print "\n "+msg+"\n" - sys.exit(1) + else: + print "\n "+msg+"\n" + sys.exit(1) db_name = tools.config["db_name"] # test whether it is needed to initialize the db (the db is empty) try: - cr = pooler.get_db_only(db_name).cursor() + cr = pooler.get_db_only(db_name).cursor() except psycopg.OperationalError: - logger.notifyChannel("init", netsvc.LOG_INFO, "could not connect to database '%s'!" % db_name,) - cr = None + logger.notifyChannel("init", netsvc.LOG_INFO, "could not connect to database '%s'!" % db_name,) + cr = None if cr: - cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_ui_menu'") - if len(cr.fetchall())==0: + cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_ui_menu'") + if len(cr.fetchall())==0: #if False: - logger.notifyChannel("init", netsvc.LOG_INFO, "init db") - tools.init_db(cr) - # in that case, force --init=all - tools.config["init"]["all"] = 1 - tools.config['update']['all'] = 1 - if not tools.config['without_demo']: - tools.config["demo"]['all'] = 1 - cr.close() + logger.notifyChannel("init", netsvc.LOG_INFO, "init db") + tools.init_db(cr) + # in that case, force --init=all + tools.config["init"]["all"] = 1 + tools.config['update']['all'] = 1 + if not tools.config['without_demo']: + tools.config["demo"]['all'] = 1 + cr.close() #---------------------------------------------------------- # launch modules install/upgrade/removes if needed #---------------------------------------------------------- if tools.config['upgrade']: - print 'Upgrading new modules...' - import tools.upgrade - (toinit, toupdate) = tools.upgrade.upgrade() - for m in toinit: - tools.config['init'][m] = 1 - for m in toupdate: - tools.config['update'][m] = 1 + print 'Upgrading new modules...' + import tools.upgrade + (toinit, toupdate) = tools.upgrade.upgrade() + for m in toinit: + tools.config['init'][m] = 1 + for m in toupdate: + tools.config['update'][m] = 1 #---------------------------------------------------------- # import basic modules @@ -175,27 +175,27 @@ import addons addons.register_classes() if tools.config['init'] or tools.config['update']: - pooler.get_db_and_pool(tools.config['db_name'], update_module=True) + pooler.get_db_and_pool(tools.config['db_name'], update_module=True) #---------------------------------------------------------- # translation stuff #---------------------------------------------------------- if tools.config["translate_out"]: - import csv + import csv - logger.notifyChannel("init", netsvc.LOG_INFO, 'writing translation file for language %s to %s' % (tools.config["language"], tools.config["translate_out"])) + logger.notifyChannel("init", netsvc.LOG_INFO, 'writing translation file for language %s to %s' % (tools.config["language"], tools.config["translate_out"])) - fileformat = os.path.splitext(tools.config["translate_out"])[-1][1:].lower() - buf = file(tools.config["translate_out"], "w") - tools.trans_export(tools.config["language"], tools.config["translate_modules"], buf, fileformat) - buf.close() - - logger.notifyChannel("init", netsvc.LOG_INFO, 'translation file written succesfully') - sys.exit(0) + fileformat = os.path.splitext(tools.config["translate_out"])[-1][1:].lower() + buf = file(tools.config["translate_out"], "w") + tools.trans_export(tools.config["language"], tools.config["translate_modules"], buf, fileformat) + buf.close() + + logger.notifyChannel("init", netsvc.LOG_INFO, 'translation file written succesfully') + sys.exit(0) if tools.config["translate_in"]: - tools.trans_load(tools.config["db_name"], tools.config["translate_in"], tools.config["language"]) - sys.exit(0) + tools.trans_load(tools.config["db_name"], tools.config["translate_in"], tools.config["language"]) + sys.exit(0) #---------------------------------------------------------------------------------- # if we don't want the server to continue to run after initialization, we quit here @@ -209,69 +209,69 @@ if tools.config["stop_after_init"]: #---------------------------------------------------------- if tools.config['xmlrpc']: - try: - port = int(tools.config["port"]) - except Exception: - logger.notifyChannel("init", netsvc.LOG_ERROR, "invalid port '%s'!" % (tools.config["port"],)) - sys.exit(1) - interface = tools.config["interface"] - secure = tools.config["secure"] - - httpd = netsvc.HttpDaemon(interface,port, secure) - - if tools.config["xmlrpc"]: - xml_gw = netsvc.xmlrpc.RpcGateway('web-services') - httpd.attach("/xmlrpc", xml_gw ) - logger.notifyChannel("web-services", netsvc.LOG_INFO, - "starting XML-RPC" + \ - (tools.config['secure'] and ' Secure' or '') + \ - " services, port " + str(port)) - - # - #if tools.config["soap"]: - # soap_gw = netsvc.xmlrpc.RpcGateway('web-services') - # httpd.attach("/soap", soap_gw ) - # logger.notifyChannel("web-services", netsvc.LOG_INFO, 'starting SOAP services, port '+str(port)) - # + try: + port = int(tools.config["port"]) + except Exception: + logger.notifyChannel("init", netsvc.LOG_ERROR, "invalid port '%s'!" % (tools.config["port"],)) + sys.exit(1) + interface = tools.config["interface"] + secure = tools.config["secure"] + + httpd = netsvc.HttpDaemon(interface,port, secure) + + if tools.config["xmlrpc"]: + xml_gw = netsvc.xmlrpc.RpcGateway('web-services') + httpd.attach("/xmlrpc", xml_gw ) + logger.notifyChannel("web-services", netsvc.LOG_INFO, + "starting XML-RPC" + \ + (tools.config['secure'] and ' Secure' or '') + \ + " services, port " + str(port)) + + # + #if tools.config["soap"]: + # soap_gw = netsvc.xmlrpc.RpcGateway('web-services') + # httpd.attach("/soap", soap_gw ) + # logger.notifyChannel("web-services", netsvc.LOG_INFO, 'starting SOAP services, port '+str(port)) + # if tools.config['netrpc']: - try: - netport = int(tools.config["netport"]) - except Exception: - logger.notifyChannel("init", netsvc.LOG_ERROR, "invalid port '%s'!" % (tools.config["netport"],)) - sys.exit(1) - netinterface = tools.config["netinterface"] - - tinySocket = netsvc.TinySocketServerThread(netinterface, netport, False) - logger.notifyChannel("web-services", netsvc.LOG_INFO, "starting netrpc service, port "+str(netport)) + try: + netport = int(tools.config["netport"]) + except Exception: + logger.notifyChannel("init", netsvc.LOG_ERROR, "invalid port '%s'!" % (tools.config["netport"],)) + sys.exit(1) + netinterface = tools.config["netinterface"] + + tinySocket = netsvc.TinySocketServerThread(netinterface, netport, False) + logger.notifyChannel("web-services", netsvc.LOG_INFO, "starting netrpc service, port "+str(netport)) def handler(signum, frame): - from tools import config - if tools.config['netrpc']: - tinySocket.stop() - if tools.config['xmlrpc']: - httpd.stop() - netsvc.Agent.quit() - if config['pidfile']: - os.unlink(config['pidfile']) - sys.exit(0) + from tools import config + if tools.config['netrpc']: + tinySocket.stop() + if tools.config['xmlrpc']: + httpd.stop() + netsvc.Agent.quit() + if config['pidfile']: + os.unlink(config['pidfile']) + sys.exit(0) from tools import config if config['pidfile']: - fd=open(config['pidfile'], 'w') - pidtext="%d" % (os.getpid()) - fd.write(pidtext) - fd.close() + fd=open(config['pidfile'], 'w') + pidtext="%d" % (os.getpid()) + fd.write(pidtext) + fd.close() signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGTERM, handler) logger.notifyChannel("web-services", netsvc.LOG_INFO, 'the server is running, waiting for connections...') if tools.config['netrpc']: - tinySocket.start() + tinySocket.start() if tools.config['xmlrpc']: - httpd.start() + httpd.start() #dispatcher.run() while True: - time.sleep(1) + time.sleep(1) diff --git a/bin/tools/amount_to_text.py b/bin/tools/amount_to_text.py index e7ef353a845..ae4ff453161 100644 --- a/bin/tools/amount_to_text.py +++ b/bin/tools/amount_to_text.py @@ -32,182 +32,182 @@ #------------------------------------------------------------- unites = { - 0: '', 1:'un', 2:'deux', 3:'trois', 4:'quatre', 5:'cinq', 6:'six', 7:'sept', 8:'huit', 9:'neuf', - 10:'dix', 11:'onze', 12:'douze', 13:'treize', 14:'quatorze', 15:'quinze', 16:'seize', - 21:'vingt et un', 31:'trente et un', 41:'quarante et un', 51:'cinquante et un', 61:'soixante et un', - 71:'septante et un', 91:'nonante et un', 80:'quatre-vingts' + 0: '', 1:'un', 2:'deux', 3:'trois', 4:'quatre', 5:'cinq', 6:'six', 7:'sept', 8:'huit', 9:'neuf', + 10:'dix', 11:'onze', 12:'douze', 13:'treize', 14:'quatorze', 15:'quinze', 16:'seize', + 21:'vingt et un', 31:'trente et un', 41:'quarante et un', 51:'cinquante et un', 61:'soixante et un', + 71:'septante et un', 91:'nonante et un', 80:'quatre-vingts' } dizaine = { - 1: 'dix', 2:'vingt', 3:'trente',4:'quarante', 5:'cinquante', 6:'soixante', 7:'septante', 8:'quatre-vingt', 9:'nonante' + 1: 'dix', 2:'vingt', 3:'trente',4:'quarante', 5:'cinquante', 6:'soixante', 7:'septante', 8:'quatre-vingt', 9:'nonante' } centaine = { - 0:'', 1: 'cent', 2:'deux cent', 3:'trois cent',4:'quatre cent', 5:'cinq cent', 6:'six cent', 7:'sept cent', 8:'huit cent', 9:'neuf cent' + 0:'', 1: 'cent', 2:'deux cent', 3:'trois cent',4:'quatre cent', 5:'cinq cent', 6:'six cent', 7:'sept cent', 8:'huit cent', 9:'neuf cent' } mille = { - 0:'', 1:'mille' + 0:'', 1:'mille' } def _100_to_text_fr(chiffre): - if chiffre in unites: - return unites[chiffre] - else: - if chiffre%10>0: - return dizaine[chiffre / 10]+'-'+unites[chiffre % 10] - else: - return dizaine[chiffre / 10] + if chiffre in unites: + return unites[chiffre] + else: + if chiffre%10>0: + return dizaine[chiffre / 10]+'-'+unites[chiffre % 10] + else: + return dizaine[chiffre / 10] def _1000_to_text_fr(chiffre): - d = _100_to_text_fr(chiffre % 100) - d2 = chiffre/100 - if d2>0 and d: - return centaine[d2]+' '+d - elif d2>1 and not(d): - return centaine[d2]+'s' - else: - return centaine[d2] or d + d = _100_to_text_fr(chiffre % 100) + d2 = chiffre/100 + if d2>0 and d: + return centaine[d2]+' '+d + elif d2>1 and not(d): + return centaine[d2]+'s' + else: + return centaine[d2] or d def _10000_to_text_fr(chiffre): - if chiffre==0: - return 'zero' - part1 = _1000_to_text_fr(chiffre % 1000) - part2 = mille.get(chiffre / 1000, _1000_to_text_fr(chiffre / 1000)+' mille') - if part2 and part1: - part1 = ' '+part1 - return part2+part1 - + if chiffre==0: + return 'zero' + part1 = _1000_to_text_fr(chiffre % 1000) + part2 = mille.get(chiffre / 1000, _1000_to_text_fr(chiffre / 1000)+' mille') + if part2 and part1: + part1 = ' '+part1 + return part2+part1 + def amount_to_text_fr(number, currency): - units_number = int(number) - units_name = currency - if units_number > 1: - units_name += 's' - units = _10000_to_text_fr(units_number) - units = units_number and '%s %s' % (units, units_name) or '' - - cents_number = int(number * 100) % 100 - cents_name = (cents_number > 1) and 'cents' or 'cent' - cents = _100_to_text_fr(cents_number) - cents = cents_number and '%s %s' % (cents, cents_name) or '' - - if units and cents: - cents = ' '+cents - - return units + cents + units_number = int(number) + units_name = currency + if units_number > 1: + units_name += 's' + units = _10000_to_text_fr(units_number) + units = units_number and '%s %s' % (units, units_name) or '' + + cents_number = int(number * 100) % 100 + cents_name = (cents_number > 1) and 'cents' or 'cent' + cents = _100_to_text_fr(cents_number) + cents = cents_number and '%s %s' % (cents, cents_name) or '' + + if units and cents: + cents = ' '+cents + + return units + cents #------------------------------------------------------------- # Dutch #------------------------------------------------------------- units_nl = { - 0:'', 1:'een', 2:'twee', 3:'drie', 4:'vier', 5:'vijf', 6:'zes', 7:'zeven', 8:'acht', 9:'negen', - 10:'tien', 11:'elf', 12:'twaalf', 13:'dertien', 14:'veertien' + 0:'', 1:'een', 2:'twee', 3:'drie', 4:'vier', 5:'vijf', 6:'zes', 7:'zeven', 8:'acht', 9:'negen', + 10:'tien', 11:'elf', 12:'twaalf', 13:'dertien', 14:'veertien' } tens_nl = { - 1: 'tien', 2:'twintig', 3:'dertig',4:'veertig', 5:'vijftig', 6:'zestig', 7:'zeventig', 8:'tachtig', 9:'negentig' + 1: 'tien', 2:'twintig', 3:'dertig',4:'veertig', 5:'vijftig', 6:'zestig', 7:'zeventig', 8:'tachtig', 9:'negentig' } hundreds_nl = { - 0:'', 1: 'honderd', + 0:'', 1: 'honderd', } thousands_nl = { - 0:'', 1:'duizend' + 0:'', 1:'duizend' } def _100_to_text_nl(number): - if number in units_nl: - return units_nl[number] - else: - if number%10 > 0: - if number>10 and number<20: - return units_nl[number % 10]+tens_nl[number / 10] - else: - units = units_nl[number % 10] - if units[-1] == 'e': - joinword = 'ën' - else: - joinword = 'en' - return units+joinword+tens_nl[number / 10] - else: - return tens_nl[number / 10] + if number in units_nl: + return units_nl[number] + else: + if number%10 > 0: + if number>10 and number<20: + return units_nl[number % 10]+tens_nl[number / 10] + else: + units = units_nl[number % 10] + if units[-1] == 'e': + joinword = 'ën' + else: + joinword = 'en' + return units+joinword+tens_nl[number / 10] + else: + return tens_nl[number / 10] def _1000_to_text_nl(number): - part1 = _100_to_text_nl(number % 100) - part2 = hundreds_nl.get(number / 100, units_nl[number/100] + hundreds_nl[1]) - if part2 and part1: - part1 = ' ' + part1 - return part2 + part1 + part1 = _100_to_text_nl(number % 100) + part2 = hundreds_nl.get(number / 100, units_nl[number/100] + hundreds_nl[1]) + if part2 and part1: + part1 = ' ' + part1 + return part2 + part1 def _10000_to_text_nl(number): - if number==0: - return 'nul' - part1 = _1000_to_text_nl(number % 1000) - if thousands_nl.has_key(number / 1000): - part2 = thousands_nl[number / 1000] - else: - if (number / 1000 % 100 > 0) and (number / 1000 > 100): - space = ' ' - else: - space = '' - part2 = _1000_to_text_nl(number / 1000) + space + thousands_nl[1] - if part2 and part1: - part1 = ' ' + part1 - return part2 + part1 - + if number==0: + return 'nul' + part1 = _1000_to_text_nl(number % 1000) + if thousands_nl.has_key(number / 1000): + part2 = thousands_nl[number / 1000] + else: + if (number / 1000 % 100 > 0) and (number / 1000 > 100): + space = ' ' + else: + space = '' + part2 = _1000_to_text_nl(number / 1000) + space + thousands_nl[1] + if part2 and part1: + part1 = ' ' + part1 + return part2 + part1 + def amount_to_text_nl(number, currency): - units_number = int(number) - units_name = currency - units = _10000_to_text_nl(units_number) - units = units_number and '%s %s' % (units, units_name) or '' - - cents_number = int(number * 100) % 100 - cents_name = 'cent' - cents = _100_to_text_nl(cents_number) - cents = cents_number and '%s %s' % (cents, cents_name) or '' - - if units and cents: - cents = ' ' + cents - - return units + cents + units_number = int(number) + units_name = currency + units = _10000_to_text_nl(units_number) + units = units_number and '%s %s' % (units, units_name) or '' + + cents_number = int(number * 100) % 100 + cents_name = 'cent' + cents = _100_to_text_nl(cents_number) + cents = cents_number and '%s %s' % (cents, cents_name) or '' + + if units and cents: + cents = ' ' + cents + + return units + cents #------------------------------------------------------------- # Generic functions #------------------------------------------------------------- _translate_funcs = {'fr' : amount_to_text_fr, 'nl' : amount_to_text_nl} - + #TODO: we should use the country AND language (ex: septante VS soixante dix) #TODO: we should use en by default, but the translation func is yet to be implemented def amount_to_text(nbr, lang='fr', currency='euro'): - """ - Converts an integer to its textual representation, using the language set in the context if any. - Example: - 1654: mille six cent cinquante-quatre. - """ - if nbr > 1000000: -#TODO: use logger - print "WARNING: number too large '%d', can't translate it!" % (nbr,) - return str(nbr) - - if not _translate_funcs.has_key(lang): -#TODO: use logger - print "WARNING: no translation function found for lang: '%s'" % (lang,) + """ + Converts an integer to its textual representation, using the language set in the context if any. + Example: + 1654: mille six cent cinquante-quatre. + """ + if nbr > 1000000: +#TODO: use logger + print "WARNING: number too large '%d', can't translate it!" % (nbr,) + return str(nbr) + + if not _translate_funcs.has_key(lang): +#TODO: use logger + print "WARNING: no translation function found for lang: '%s'" % (lang,) #TODO: (default should be en) same as above - lang = 'fr' - return _translate_funcs[lang](nbr, currency) + lang = 'fr' + return _translate_funcs[lang](nbr, currency) if __name__=='__main__': - from sys import argv - - lang = 'nl' - if len(argv) < 2: - for i in range(1,200): - print i, ">>", amount_to_text(i, lang) - for i in range(200,999999,139): - print i, ">>", amount_to_text(i, lang) - else: - print amount_to_text(int(argv[1]), lang) + from sys import argv + + lang = 'nl' + if len(argv) < 2: + for i in range(1,200): + print i, ">>", amount_to_text(i, lang) + for i in range(200,999999,139): + print i, ">>", amount_to_text(i, lang) + else: + print amount_to_text(int(argv[1]), lang) diff --git a/bin/tools/config.py b/bin/tools/config.py index 72f0f98091f..5b279a9eed2 100644 --- a/bin/tools/config.py +++ b/bin/tools/config.py @@ -31,208 +31,208 @@ import netsvc,logging class configmanager(object): - def __init__(self, fname=None): - self.options = { - 'verbose': False, - 'interface': '', # this will bind the server to all interfaces - 'port': '8069', - 'netinterface': '', - 'netport': '8070', - 'db_host': False, - 'db_port': False, - 'db_name': 'terp', - 'db_user': False, - 'db_password': False, - 'db_maxconn': 64, - 'reportgz': False, - 'netrpc': True, - 'xmlrpc': True, - 'soap': False, - 'translate_in': None, - 'translate_out': None, - 'language': None, - 'pg_path': None, - 'admin_passwd': 'admin', - 'addons_path': None, - 'root_path': None, - 'debug_mode': False, - 'commit_mode': False, - 'pidfile': None, - 'logfile': None, - 'secure': False, - 'smtp_server': 'localhost', - 'smtp_user': False, - 'smtp_password': False, - 'stop_after_init': False, # this will stop the server after initialization - 'price_accuracy': 2, - - 'assert_exit_level': logging.WARNING, # level above which a failed assert will - } - - assert_exit_levels = (netsvc.LOG_CRITICAL, netsvc.LOG_ERROR, netsvc.LOG_WARNING, netsvc.LOG_INFO, netsvc.LOG_DEBUG) - - parser = optparse.OptionParser(version=tinyerp_version_string) - - parser.add_option("-c", "--config", dest="config", help="specify alternate config file") - parser.add_option("-s", "--save", action="store_true", dest="save", default=False, help="save configuration to ~/.terp_serverrc") - parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="enable debugging") - parser.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored") - parser.add_option("--logfile", dest="logfile", help="file where the server log will be stored") - - parser.add_option("-n", "--interface", dest="interface", help="specify the TCP IP address") - parser.add_option("-p", "--port", dest="port", help="specify the TCP port") - parser.add_option("--net_interface", dest="netinterface", help="specify the TCP IP address for netrpc") - parser.add_option("--net_port", dest="netport", help="specify the TCP port for netrpc") - parser.add_option("--no-netrpc", dest="netrpc", action="store_false", default=True, help="disable netrpc") - parser.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", default=True, help="disable xmlrpc") - - parser.add_option("-i", "--init", dest="init", help="init a module (use \"all\" for all modules)") - parser.add_option("--without-demo", dest="without_demo", help="load demo data for a module (use \"all\" for all modules)", default=False) - parser.add_option("-u", "--update", dest="update", help="update a module (use \"all\" for all modules)") - # stops the server from launching after initialization - parser.add_option("--stop-after-init", action="store_true", dest="stop_after_init", default=False, help="stop the server after it initializes") - parser.add_option('--debug', dest='debug_mode', action='store_true', default=False, help='enable debug mode') - parser.add_option("--assert-exit-level", dest='assert_exit_level', help="specify the level at which a failed assertion will stop the server " + str(assert_exit_levels)) - parser.add_option("-S", "--secure", dest="secure", action="store_true", help="launch server over https instead of http", default=False) - parser.add_option('--smtp', dest='smtp_server', default='', help='specify the SMTP server for sending email') - parser.add_option('--smtp-user', dest='smtp_user', default='', help='specify the SMTP username for sending email') - parser.add_option('--smtp-password', dest='smtp_password', default='', help='specify the SMTP password for sending email') - parser.add_option('--price_accuracy', dest='price_accuracy', default='2', help='specify the price accuracy') - - group = optparse.OptionGroup(parser, "Modules related options") - group.add_option("-g", "--upgrade", action="store_true", dest="upgrade", default=False, help="Upgrade/install/uninstall modules") - - group = optparse.OptionGroup(parser, "Database related options") - group.add_option("-d", "--database", dest="db_name", help="specify the database name") - group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name") - group.add_option("-w", "--db_password", dest="db_password", help="specify the database password") - group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path") - group.add_option("--db_host", dest="db_host", help="specify the database host") - group.add_option("--db_port", dest="db_port", help="specify the database port") - group.add_option("--db_maxconn", dest="db_maxconn", default='64', help="specify the the maximum number of physical connections to posgresql") - group.add_option("-C", "--commit-mode", dest="commit_mode", action="store_true", help="Several commit during one file importation. Use this for big data importation.", default=False) - parser.add_option_group(group) - - group = optparse.OptionGroup(parser, "Internationalisation options", - "Use these options to translate Tiny ERP to another language." - "See i18n section of the user manual. Options '-l' and '-d' are mandatory.") - - group.add_option('-l', "--language", dest="language", help="specify the language of the translation file. Use it with --i18n-export and --i18n-import") - group.add_option("--i18n-export", dest="translate_out", help="export all sentences to be translated to a CSV or a PO file and exit") - group.add_option("--i18n-import", dest="translate_in", help="import a CSV or a PO file with translations and exit") - group.add_option("--modules", dest="translate_modules", help="specify modules to export. Use in combination with --i18n-export") - group.add_option("--addons-path", dest="addons_path", help="specify an alternative addons path.") - parser.add_option_group(group) - - (opt, args) = parser.parse_args() - - assert not ((opt.translate_in or opt.translate_out) and (not opt.language or not opt.db_name)), "the i18n-import and i18n-export options cannot be used without the language (-l) and database (-d) options" - - # place/search the config file on Win32 near the server installation - # (../etc from the server) - # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write, - # else he won't be able to save the configurations, or even to start the server... - if os.name == 'nt': - rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'tinyerp-server.conf') - else: - rcfilepath = os.path.expanduser('~/.terp_serverrc') - - self.rcfile = fname or opt.config or os.environ.get('TERP_SERVER') or rcfilepath - self.load() - - - # Verify that we want to log or not, if not the output will go to stdout - if self.options['logfile'] in ('None', 'False'): - self.options['logfile'] = False - # the same for the pidfile - if self.options['pidfile'] in ('None', 'False'): - self.options['pidfile'] = False - - for arg in ('interface', 'port', 'db_name', 'db_user', 'db_password', 'db_host', - 'db_port', 'logfile', 'pidfile', 'secure', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy', 'netinterface', 'netport', 'db_maxconn', 'commit_mode', 'addons_path'): - if getattr(opt, arg): - self.options[arg] = getattr(opt, arg) - - for arg in ('language', 'translate_out', 'translate_in', - 'upgrade', 'verbose', 'debug_mode', - 'stop_after_init', 'without_demo', 'netrpc', 'xmlrpc'): - self.options[arg] = getattr(opt, arg) - - if opt.assert_exit_level: - assert opt.assert_exit_level in assert_exit_levels, 'ERROR: The assert-exit-level must be one of those values: '+str(assert_exit_levels) - self.options['assert_exit_level'] = getattr(logging, opt.assert_exit_level.upper()) - - if not self.options['root_path'] or self.options['root_path']=='None': - self.options['root_path'] = os.path.abspath(os.path.dirname(sys.argv[0])) - if not self.options['addons_path'] or self.options['addons_path']=='None': - self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons') - - init = {} - if opt.init: - for i in opt.init.split(','): - init[i] = 1 - self.options['init'] = init - self.options["demo"] = not opt.without_demo and self.options['init'] or {} - - update = {} - if opt.update: - for i in opt.update.split(','): - update[i] = 1 - self.options['update'] = update - - self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all'] - self.options['translate_modules'].sort() - - if opt.pg_path: - self.options['pg_path'] = opt.pg_path - - if self.options.get('language', False): - assert len(self.options['language'])<=5, 'ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE' - if opt.save: - self.save() - - def load(self): - p = ConfigParser.ConfigParser() - try: - p.read([self.rcfile]) - for (name,value) in p.items('options'): - if value=='True' or value=='true': - value = True - if value=='False' or value=='false': - value = False - self.options[name] = value - except IOError: - pass - except ConfigParser.NoSectionError: - pass - - def save(self): - p = ConfigParser.ConfigParser() - p.add_section('options') - for o in [opt for opt in self.options.keys() if opt not in ('version','language','translate_out','translate_in','init','update')]: - p.set('options', o, self.options[o]) - - # try to create the directories and write the file - try: - if not os.path.exists(os.path.dirname(self.rcfile)): - os.makedirs(os.path.dirname(self.rcfile)) - try: - p.write(file(self.rcfile, 'w')) - except IOError: - sys.stderr.write("ERROR: couldn't write the config file\n") - - except OSError: - # what to do if impossible? - sys.stderr.write("ERROR: couldn't create the config directory\n") - - def get(self, key, default=None): - return self.options.get(key, default) - - def __setitem__(self, key, value): - self.options[key] = value - - def __getitem__(self, key): - return self.options[key] + def __init__(self, fname=None): + self.options = { + 'verbose': False, + 'interface': '', # this will bind the server to all interfaces + 'port': '8069', + 'netinterface': '', + 'netport': '8070', + 'db_host': False, + 'db_port': False, + 'db_name': 'terp', + 'db_user': False, + 'db_password': False, + 'db_maxconn': 64, + 'reportgz': False, + 'netrpc': True, + 'xmlrpc': True, + 'soap': False, + 'translate_in': None, + 'translate_out': None, + 'language': None, + 'pg_path': None, + 'admin_passwd': 'admin', + 'addons_path': None, + 'root_path': None, + 'debug_mode': False, + 'commit_mode': False, + 'pidfile': None, + 'logfile': None, + 'secure': False, + 'smtp_server': 'localhost', + 'smtp_user': False, + 'smtp_password': False, + 'stop_after_init': False, # this will stop the server after initialization + 'price_accuracy': 2, + + 'assert_exit_level': logging.WARNING, # level above which a failed assert will + } + + assert_exit_levels = (netsvc.LOG_CRITICAL, netsvc.LOG_ERROR, netsvc.LOG_WARNING, netsvc.LOG_INFO, netsvc.LOG_DEBUG) + + parser = optparse.OptionParser(version=tinyerp_version_string) + + parser.add_option("-c", "--config", dest="config", help="specify alternate config file") + parser.add_option("-s", "--save", action="store_true", dest="save", default=False, help="save configuration to ~/.terp_serverrc") + parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="enable debugging") + parser.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored") + parser.add_option("--logfile", dest="logfile", help="file where the server log will be stored") + + parser.add_option("-n", "--interface", dest="interface", help="specify the TCP IP address") + parser.add_option("-p", "--port", dest="port", help="specify the TCP port") + parser.add_option("--net_interface", dest="netinterface", help="specify the TCP IP address for netrpc") + parser.add_option("--net_port", dest="netport", help="specify the TCP port for netrpc") + parser.add_option("--no-netrpc", dest="netrpc", action="store_false", default=True, help="disable netrpc") + parser.add_option("--no-xmlrpc", dest="xmlrpc", action="store_false", default=True, help="disable xmlrpc") + + parser.add_option("-i", "--init", dest="init", help="init a module (use \"all\" for all modules)") + parser.add_option("--without-demo", dest="without_demo", help="load demo data for a module (use \"all\" for all modules)", default=False) + parser.add_option("-u", "--update", dest="update", help="update a module (use \"all\" for all modules)") + # stops the server from launching after initialization + parser.add_option("--stop-after-init", action="store_true", dest="stop_after_init", default=False, help="stop the server after it initializes") + parser.add_option('--debug', dest='debug_mode', action='store_true', default=False, help='enable debug mode') + parser.add_option("--assert-exit-level", dest='assert_exit_level', help="specify the level at which a failed assertion will stop the server " + str(assert_exit_levels)) + parser.add_option("-S", "--secure", dest="secure", action="store_true", help="launch server over https instead of http", default=False) + parser.add_option('--smtp', dest='smtp_server', default='', help='specify the SMTP server for sending email') + parser.add_option('--smtp-user', dest='smtp_user', default='', help='specify the SMTP username for sending email') + parser.add_option('--smtp-password', dest='smtp_password', default='', help='specify the SMTP password for sending email') + parser.add_option('--price_accuracy', dest='price_accuracy', default='2', help='specify the price accuracy') + + group = optparse.OptionGroup(parser, "Modules related options") + group.add_option("-g", "--upgrade", action="store_true", dest="upgrade", default=False, help="Upgrade/install/uninstall modules") + + group = optparse.OptionGroup(parser, "Database related options") + group.add_option("-d", "--database", dest="db_name", help="specify the database name") + group.add_option("-r", "--db_user", dest="db_user", help="specify the database user name") + group.add_option("-w", "--db_password", dest="db_password", help="specify the database password") + group.add_option("--pg_path", dest="pg_path", help="specify the pg executable path") + group.add_option("--db_host", dest="db_host", help="specify the database host") + group.add_option("--db_port", dest="db_port", help="specify the database port") + group.add_option("--db_maxconn", dest="db_maxconn", default='64', help="specify the the maximum number of physical connections to posgresql") + group.add_option("-C", "--commit-mode", dest="commit_mode", action="store_true", help="Several commit during one file importation. Use this for big data importation.", default=False) + parser.add_option_group(group) + + group = optparse.OptionGroup(parser, "Internationalisation options", + "Use these options to translate Tiny ERP to another language." + "See i18n section of the user manual. Options '-l' and '-d' are mandatory.") + + group.add_option('-l', "--language", dest="language", help="specify the language of the translation file. Use it with --i18n-export and --i18n-import") + group.add_option("--i18n-export", dest="translate_out", help="export all sentences to be translated to a CSV or a PO file and exit") + group.add_option("--i18n-import", dest="translate_in", help="import a CSV or a PO file with translations and exit") + group.add_option("--modules", dest="translate_modules", help="specify modules to export. Use in combination with --i18n-export") + group.add_option("--addons-path", dest="addons_path", help="specify an alternative addons path.") + parser.add_option_group(group) + + (opt, args) = parser.parse_args() + + assert not ((opt.translate_in or opt.translate_out) and (not opt.language or not opt.db_name)), "the i18n-import and i18n-export options cannot be used without the language (-l) and database (-d) options" + + # place/search the config file on Win32 near the server installation + # (../etc from the server) + # if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write, + # else he won't be able to save the configurations, or even to start the server... + if os.name == 'nt': + rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'tinyerp-server.conf') + else: + rcfilepath = os.path.expanduser('~/.terp_serverrc') + + self.rcfile = fname or opt.config or os.environ.get('TERP_SERVER') or rcfilepath + self.load() + + + # Verify that we want to log or not, if not the output will go to stdout + if self.options['logfile'] in ('None', 'False'): + self.options['logfile'] = False + # the same for the pidfile + if self.options['pidfile'] in ('None', 'False'): + self.options['pidfile'] = False + + for arg in ('interface', 'port', 'db_name', 'db_user', 'db_password', 'db_host', + 'db_port', 'logfile', 'pidfile', 'secure', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy', 'netinterface', 'netport', 'db_maxconn', 'commit_mode', 'addons_path'): + if getattr(opt, arg): + self.options[arg] = getattr(opt, arg) + + for arg in ('language', 'translate_out', 'translate_in', + 'upgrade', 'verbose', 'debug_mode', + 'stop_after_init', 'without_demo', 'netrpc', 'xmlrpc'): + self.options[arg] = getattr(opt, arg) + + if opt.assert_exit_level: + assert opt.assert_exit_level in assert_exit_levels, 'ERROR: The assert-exit-level must be one of those values: '+str(assert_exit_levels) + self.options['assert_exit_level'] = getattr(logging, opt.assert_exit_level.upper()) + + if not self.options['root_path'] or self.options['root_path']=='None': + self.options['root_path'] = os.path.abspath(os.path.dirname(sys.argv[0])) + if not self.options['addons_path'] or self.options['addons_path']=='None': + self.options['addons_path'] = os.path.join(self.options['root_path'], 'addons') + + init = {} + if opt.init: + for i in opt.init.split(','): + init[i] = 1 + self.options['init'] = init + self.options["demo"] = not opt.without_demo and self.options['init'] or {} + + update = {} + if opt.update: + for i in opt.update.split(','): + update[i] = 1 + self.options['update'] = update + + self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all'] + self.options['translate_modules'].sort() + + if opt.pg_path: + self.options['pg_path'] = opt.pg_path + + if self.options.get('language', False): + assert len(self.options['language'])<=5, 'ERROR: The Lang name must take max 5 chars, Eg: -lfr_BE' + if opt.save: + self.save() + + def load(self): + p = ConfigParser.ConfigParser() + try: + p.read([self.rcfile]) + for (name,value) in p.items('options'): + if value=='True' or value=='true': + value = True + if value=='False' or value=='false': + value = False + self.options[name] = value + except IOError: + pass + except ConfigParser.NoSectionError: + pass + + def save(self): + p = ConfigParser.ConfigParser() + p.add_section('options') + for o in [opt for opt in self.options.keys() if opt not in ('version','language','translate_out','translate_in','init','update')]: + p.set('options', o, self.options[o]) + + # try to create the directories and write the file + try: + if not os.path.exists(os.path.dirname(self.rcfile)): + os.makedirs(os.path.dirname(self.rcfile)) + try: + p.write(file(self.rcfile, 'w')) + except IOError: + sys.stderr.write("ERROR: couldn't write the config file\n") + + except OSError: + # what to do if impossible? + sys.stderr.write("ERROR: couldn't create the config directory\n") + + def get(self, key, default=None): + return self.options.get(key, default) + + def __setitem__(self, key, value): + self.options[key] = value + + def __getitem__(self, key): + return self.options[key] config = configmanager() diff --git a/bin/tools/convert.py b/bin/tools/convert.py index 0e3bb482019..3157505325f 100644 --- a/bin/tools/convert.py +++ b/bin/tools/convert.py @@ -44,737 +44,737 @@ from lxml import etree class ConvertError(Exception): - def __init__(self, doc, orig_excpt): - self.d = doc - self.orig = orig_excpt + def __init__(self, doc, orig_excpt): + self.d = doc + self.orig = orig_excpt - def __str__(self): - return 'Exception:\n\t%s\nUsing file:\n%s' % (self.orig, self.d) + def __str__(self): + return 'Exception:\n\t%s\nUsing file:\n%s' % (self.orig, self.d) def _ref(self, cr): - return lambda x: self.id_get(cr, False, x) + return lambda x: self.id_get(cr, False, x) def _obj(pool, cr, uid, model_str, context=None): - model = pool.get(model_str) - return lambda x: model.browse(cr, uid, x, context=context) + model = pool.get(model_str) + return lambda x: model.browse(cr, uid, x, context=context) def _eval_xml(self,node, pool, cr, uid, idref, context=None): - if context is None: - context = {} - if node.nodeType == node.TEXT_NODE: - return node.data.encode("utf8") - elif node.nodeType == node.ELEMENT_NODE: - if node.nodeName in ('field','value'): - t = node.getAttribute('type') or 'char' - f_model = node.getAttribute("model").encode('ascii') - if len(node.getAttribute('search')): - f_search = node.getAttribute("search").encode('utf-8') - f_use = node.getAttribute("use").encode('ascii') - f_name = node.getAttribute("name").encode('utf-8') - if len(f_use)==0: - f_use = "id" - q = eval(f_search, idref) - ids = pool.get(f_model).search(cr, uid, q) - if f_use<>'id': - ids = map(lambda x: x[f_use], pool.get(f_model).read(cr, uid, ids, [f_use])) - _cols = pool.get(f_model)._columns - if (f_name in _cols) and _cols[f_name]._type=='many2many': - return ids - f_val = False - if len(ids): - f_val = ids[0] - if isinstance(f_val, tuple): - f_val = f_val[0] - return f_val - a_eval = node.getAttribute('eval') - if len(a_eval): - import time - idref['time'] = time - import release - idref['version'] = release.version.rsplit('.', 1)[0] - idref['ref'] = lambda x: self.id_get(cr, False, x) - if len(f_model): - idref['obj'] = _obj(self.pool, cr, uid, f_model, context=context) - try: - import pytz - except: - logger = netsvc.Logger() - logger.notifyChannel("init", netsvc.LOG_INFO, 'could not find pytz library') - class pytzclass(object): - all_timezones=[] - pytz=pytzclass() - idref['pytz'] = pytz - return eval(a_eval, idref) - if t == 'xml': - def _process(s, idref): - m = re.findall('[^%]%\((.*?)\)[ds]', s) - for id in m: - if not id in idref: - idref[id]=self.id_get(cr, False, id) - return s % idref - txt = '<?xml version="1.0"?>\n'+_process("".join([i.toxml().encode("utf8") for i in node.childNodes]), idref) -# txt = '<?xml version="1.0"?>\n'+"".join([i.toxml().encode("utf8") for i in node.childNodes]) % idref - - return txt - if t in ('char', 'int', 'float'): - d = "" - for n in [i for i in node.childNodes]: - d+=str(_eval_xml(self,n,pool,cr,uid,idref)) - if t == 'int': - d = d.strip() - if d=='None': - return None - else: - d=int(d.strip()) - elif t=='float': - d=float(d.strip()) - return d - elif t in ('list','tuple'): - res=[] - for n in [i for i in node.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=='value')]: - res.append(_eval_xml(self,n,pool,cr,uid,idref)) - if t=='tuple': - return tuple(res) - return res - elif node.nodeName=="getitem": - for n in [i for i in node.childNodes if (i.nodeType == i.ELEMENT_NODE)]: - res=_eval_xml(self,n,pool,cr,uid,idref) - if not res: - raise LookupError - elif node.getAttribute('type') in ("int", "list"): - return res[int(node.getAttribute('index'))] - else: - return res[node.getAttribute('index').encode("utf8")] - elif node.nodeName=="function": - args = [] - a_eval = node.getAttribute('eval') - if len(a_eval): - idref['ref'] = lambda x: self.id_get(cr, False, x) - args = eval(a_eval, idref) - for n in [i for i in node.childNodes if (i.nodeType == i.ELEMENT_NODE)]: - args.append(_eval_xml(self,n, pool, cr, uid, idref, context)) - model = pool.get(node.getAttribute('model')) - method = node.getAttribute('name') - res = getattr(model, method)(cr, uid, *args) - return res - elif node.nodeName=="test": - d = "" - for n in [i for i in node.childNodes]: - d+=str(_eval_xml(self,n,pool,cr,uid,idref, context=context)) - return d + if context is None: + context = {} + if node.nodeType == node.TEXT_NODE: + return node.data.encode("utf8") + elif node.nodeType == node.ELEMENT_NODE: + if node.nodeName in ('field','value'): + t = node.getAttribute('type') or 'char' + f_model = node.getAttribute("model").encode('ascii') + if len(node.getAttribute('search')): + f_search = node.getAttribute("search").encode('utf-8') + f_use = node.getAttribute("use").encode('ascii') + f_name = node.getAttribute("name").encode('utf-8') + if len(f_use)==0: + f_use = "id" + q = eval(f_search, idref) + ids = pool.get(f_model).search(cr, uid, q) + if f_use<>'id': + ids = map(lambda x: x[f_use], pool.get(f_model).read(cr, uid, ids, [f_use])) + _cols = pool.get(f_model)._columns + if (f_name in _cols) and _cols[f_name]._type=='many2many': + return ids + f_val = False + if len(ids): + f_val = ids[0] + if isinstance(f_val, tuple): + f_val = f_val[0] + return f_val + a_eval = node.getAttribute('eval') + if len(a_eval): + import time + idref['time'] = time + import release + idref['version'] = release.version.rsplit('.', 1)[0] + idref['ref'] = lambda x: self.id_get(cr, False, x) + if len(f_model): + idref['obj'] = _obj(self.pool, cr, uid, f_model, context=context) + try: + import pytz + except: + logger = netsvc.Logger() + logger.notifyChannel("init", netsvc.LOG_INFO, 'could not find pytz library') + class pytzclass(object): + all_timezones=[] + pytz=pytzclass() + idref['pytz'] = pytz + return eval(a_eval, idref) + if t == 'xml': + def _process(s, idref): + m = re.findall('[^%]%\((.*?)\)[ds]', s) + for id in m: + if not id in idref: + idref[id]=self.id_get(cr, False, id) + return s % idref + txt = '<?xml version="1.0"?>\n'+_process("".join([i.toxml().encode("utf8") for i in node.childNodes]), idref) +# txt = '<?xml version="1.0"?>\n'+"".join([i.toxml().encode("utf8") for i in node.childNodes]) % idref + + return txt + if t in ('char', 'int', 'float'): + d = "" + for n in [i for i in node.childNodes]: + d+=str(_eval_xml(self,n,pool,cr,uid,idref)) + if t == 'int': + d = d.strip() + if d=='None': + return None + else: + d=int(d.strip()) + elif t=='float': + d=float(d.strip()) + return d + elif t in ('list','tuple'): + res=[] + for n in [i for i in node.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=='value')]: + res.append(_eval_xml(self,n,pool,cr,uid,idref)) + if t=='tuple': + return tuple(res) + return res + elif node.nodeName=="getitem": + for n in [i for i in node.childNodes if (i.nodeType == i.ELEMENT_NODE)]: + res=_eval_xml(self,n,pool,cr,uid,idref) + if not res: + raise LookupError + elif node.getAttribute('type') in ("int", "list"): + return res[int(node.getAttribute('index'))] + else: + return res[node.getAttribute('index').encode("utf8")] + elif node.nodeName=="function": + args = [] + a_eval = node.getAttribute('eval') + if len(a_eval): + idref['ref'] = lambda x: self.id_get(cr, False, x) + args = eval(a_eval, idref) + for n in [i for i in node.childNodes if (i.nodeType == i.ELEMENT_NODE)]: + args.append(_eval_xml(self,n, pool, cr, uid, idref, context)) + model = pool.get(node.getAttribute('model')) + method = node.getAttribute('name') + res = getattr(model, method)(cr, uid, *args) + return res + elif node.nodeName=="test": + d = "" + for n in [i for i in node.childNodes]: + d+=str(_eval_xml(self,n,pool,cr,uid,idref, context=context)) + return d escape_re = re.compile(r'(?<!\\)/') def escape(x): - return x.replace('\\/', '/') + return x.replace('\\/', '/') class assertion_report(object): - def __init__(self): - self._report = {} - - def record_assertion(self, success, severity): - """ - Records the result of an assertion for the failed/success count - retrurns success - """ - if severity in self._report: - self._report[severity][success] += 1 - else: - self._report[severity] = {success:1, not success: 0} - return success - - def get_report(self): - return self._report - - def __str__(self): - res = '\nAssertions report:\nLevel\tsuccess\tfailed\n' - success = failed = 0 - for sev in self._report: - res += sev + '\t' + str(self._report[sev][True]) + '\t' + str(self._report[sev][False]) + '\n' - success += self._report[sev][True] - failed += self._report[sev][False] - res += 'total\t' + str(success) + '\t' + str(failed) + '\n' - res += 'end of report (' + str(success + failed) + ' assertion(s) checked)' - return res + def __init__(self): + self._report = {} + + def record_assertion(self, success, severity): + """ + Records the result of an assertion for the failed/success count + retrurns success + """ + if severity in self._report: + self._report[severity][success] += 1 + else: + self._report[severity] = {success:1, not success: 0} + return success + + def get_report(self): + return self._report + + def __str__(self): + res = '\nAssertions report:\nLevel\tsuccess\tfailed\n' + success = failed = 0 + for sev in self._report: + res += sev + '\t' + str(self._report[sev][True]) + '\t' + str(self._report[sev][False]) + '\n' + success += self._report[sev][True] + failed += self._report[sev][False] + res += 'total\t' + str(success) + '\t' + str(failed) + '\n' + res += 'end of report (' + str(success + failed) + ' assertion(s) checked)' + return res class xml_import(object): - def isnoupdate(self, data_node = None): - return self.noupdate or (data_node and data_node.getAttribute('noupdate')) + def isnoupdate(self, data_node = None): + return self.noupdate or (data_node and data_node.getAttribute('noupdate')) - def get_context(self, data_node, node, eval_dict): - data_node_context = (data_node and data_node.getAttribute('context').encode('utf8')) - if data_node_context: - context = eval(data_node_context, eval_dict) - else: - context = {} + def get_context(self, data_node, node, eval_dict): + data_node_context = (data_node and data_node.getAttribute('context').encode('utf8')) + if data_node_context: + context = eval(data_node_context, eval_dict) + else: + context = {} - node_context = node.getAttribute("context").encode('utf8') - if len(node_context): - context.update(eval(node_context, eval_dict)) + node_context = node.getAttribute("context").encode('utf8') + if len(node_context): + context.update(eval(node_context, eval_dict)) - return context + return context - def get_uid(self, cr, uid, data_node, node): - node_uid = node.getAttribute('uid') or (data_node and data_node.getAttribute('uid')) - if len(node_uid): - return self.id_get(cr, None, node_uid) - return uid + def get_uid(self, cr, uid, data_node, node): + node_uid = node.getAttribute('uid') or (data_node and data_node.getAttribute('uid')) + if len(node_uid): + return self.id_get(cr, None, node_uid) + return uid - def _test_xml_id(self, xml_id): - id = xml_id - if '.' in xml_id: - assert len(id.split('.'))==2, """The ID reference '"%s" must contains + def _test_xml_id(self, xml_id): + id = xml_id + if '.' in xml_id: + assert len(id.split('.'))==2, """The ID reference '"%s" must contains maximum one dot. They are used to refer to other modules ID, in the form: module.record_id""" % (xml_id,) - base, id = xml_id.split('.') - if len(id) > 64: - self.logger.notifyChannel('init', netsvc.LOG_ERROR, 'id: %s is to long (max: 64)'%xml_id) - - def _tag_delete(self, cr, rec, data_node=None): - d_model = rec.getAttribute("model") - d_search = rec.getAttribute("search") - d_id = rec.getAttribute("id") - ids = [] - if len(d_search): - ids = self.pool.get(d_model).search(cr,self.uid,eval(d_search)) - if len(d_id): - ids.append(self.id_get(cr, d_model, d_id)) - if len(ids): - self.pool.get(d_model).unlink(cr, self.uid, ids) - #self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids, direct=True) - return False - - def _tag_report(self, cr, rec, data_node=None): - res = {} - for dest,f in (('name','string'),('model','model'),('report_name','name')): - res[dest] = rec.getAttribute(f).encode('utf8') - assert res[dest], "Attribute %s of report is empty !" % (f,) - for field,dest in (('rml','report_rml'),('xml','report_xml'),('xsl','report_xsl')): - if rec.hasAttribute(field): - res[dest] = rec.getAttribute(field).encode('utf8') - if rec.hasAttribute('auto'): - res['auto'] = eval(rec.getAttribute('auto')) - if rec.hasAttribute('sxw'): - sxw_content = misc.file_open(rec.getAttribute('sxw')).read() - res['report_sxw_content'] = sxw_content - if rec.hasAttribute('header'): - res['header'] = eval(rec.getAttribute('header')) - res['multi'] = rec.hasAttribute('multi') and eval(rec.getAttribute('multi')) - xml_id = rec.getAttribute('id').encode('utf8') - self._test_xml_id(xml_id) - id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.report.xml", self.module, res, xml_id, mode=self.mode) - self.idref[xml_id] = int(id) - if not rec.hasAttribute('menu') or eval(rec.getAttribute('menu')): - keyword = str(rec.getAttribute('keyword') or 'client_print_multi') - keys = [('action',keyword),('res_model',res['model'])] - value = 'ir.actions.report.xml,'+str(id) - replace = rec.hasAttribute('replace') and rec.getAttribute("replace") - self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, res['name'], [res['model']], value, replace=replace, isobject=True, xml_id=xml_id) - return False - - def _tag_function(self, cr, rec, data_node=None): - if self.isnoupdate(data_node) and self.mode != 'init': - return - context = self.get_context(data_node, rec, {'ref': _ref(self, cr)}) - uid = self.get_uid(cr, self.uid, data_node, rec) - _eval_xml(self,rec, self.pool, cr, uid, self.idref, context=context) - return False - - def _tag_wizard(self, cr, rec, data_node=None): - string = rec.getAttribute("string").encode('utf8') - model = rec.getAttribute("model").encode('utf8') - name = rec.getAttribute("name").encode('utf8') - xml_id = rec.getAttribute('id').encode('utf8') - self._test_xml_id(xml_id) - multi = rec.hasAttribute('multi') and eval(rec.getAttribute('multi')) - res = {'name': string, 'wiz_name': name, 'multi':multi} - - id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.wizard", self.module, res, xml_id, mode=self.mode) - self.idref[xml_id] = int(id) - # ir_set - if (not rec.hasAttribute('menu') or eval(rec.getAttribute('menu'))) and id: - keyword = str(rec.getAttribute('keyword') or 'client_action_multi') - keys = [('action',keyword),('res_model',model)] - value = 'ir.actions.wizard,'+str(id) - replace = rec.hasAttribute('replace') and \ - rec.getAttribute("replace") or True - self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, string, [model], value, replace=replace, isobject=True, xml_id=xml_id) - return False - - def _tag_url(self, cr, rec, data_node=None): - url = rec.getAttribute("string").encode('utf8') - target = rec.getAttribute("target").encode('utf8') - name = rec.getAttribute("name").encode('utf8') - xml_id = rec.getAttribute('id').encode('utf8') - self._test_xml_id(xml_id) - - res = {'name': name, 'url': url, 'target':target} - - id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.url", self.module, res, xml_id, mode=self.mode) - self.idref[xml_id] = int(id) - # ir_set - if (not rec.hasAttribute('menu') or eval(rec.getAttribute('menu'))) and id: - keyword = str(rec.getAttribute('keyword') or 'client_action_multi') - keys = [('action',keyword)] - value = 'ir.actions.url,'+str(id) - replace = rec.hasAttribute('replace') and \ - rec.getAttribute("replace") or True - self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, url, ["ir.actions.url"], value, replace=replace, isobject=True, xml_id=xml_id) - return False - - def _tag_act_window(self, cr, rec, data_node=None): - name = rec.hasAttribute('name') and rec.getAttribute('name').encode('utf-8') - xml_id = rec.getAttribute('id').encode('utf8') - self._test_xml_id(xml_id) - type = rec.hasAttribute('type') and rec.getAttribute('type').encode('utf-8') or 'ir.actions.act_window' - view_id = False - if rec.hasAttribute('view'): - view_id = self.id_get(cr, 'ir.actions.act_window', rec.getAttribute('view').encode('utf-8')) - domain = rec.hasAttribute('domain') and rec.getAttribute('domain').encode('utf-8') - context = rec.hasAttribute('context') and rec.getAttribute('context').encode('utf-8') or '{}' - res_model = rec.getAttribute('res_model').encode('utf-8') - src_model = rec.hasAttribute('src_model') and rec.getAttribute('src_model').encode('utf-8') - view_type = rec.hasAttribute('view_type') and rec.getAttribute('view_type').encode('utf-8') or 'form' - view_mode = rec.hasAttribute('view_mode') and rec.getAttribute('view_mode').encode('utf-8') or 'tree,form' - usage = rec.hasAttribute('usage') and rec.getAttribute('usage').encode('utf-8') - limit = rec.hasAttribute('limit') and rec.getAttribute('limit').encode('utf-8') - auto_refresh = rec.hasAttribute('auto_refresh') \ - and rec.getAttribute('auto_refresh').encode('utf-8') - - res = { - 'name': name, - 'type': type, - 'view_id': view_id, - 'domain': domain, - 'context': context, - 'res_model': res_model, - 'src_model': src_model, - 'view_type': view_type, - 'view_mode': view_mode, - 'usage': usage, - 'limit': limit, - 'auto_refresh': auto_refresh, - } - if rec.hasAttribute('target'): - res['target'] = rec.getAttribute('target') - id = self.pool.get('ir.model.data')._update(cr, self.uid, 'ir.actions.act_window', self.module, res, xml_id, mode=self.mode) - self.idref[xml_id] = int(id) - - if src_model: - keyword = 'client_action_relate' - keys = [('action', keyword), ('res_model', res_model)] - value = 'ir.actions.act_window,'+str(id) - replace = rec.hasAttribute('replace') and rec.getAttribute('replace') - self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, xml_id, [src_model], value, replace=replace, isobject=True, xml_id=xml_id) - # TODO add remove ir.model.data - return False - - def _tag_ir_set(self, cr, rec, data_node=None): - if not self.mode=='init': - return False - res = {} - for field in [i for i in rec.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="field")]: - f_name = field.getAttribute("name").encode('utf-8') - f_val = _eval_xml(self,field,self.pool, cr, self.uid, self.idref) - res[f_name] = f_val - self.pool.get('ir.model.data').ir_set(cr, self.uid, res['key'], res['key2'], res['name'], res['models'], res['value'], replace=res.get('replace',True), isobject=res.get('isobject', False), meta=res.get('meta',None)) - return False - - def _tag_workflow(self, cr, rec, data_node=None): - if self.isnoupdate(data_node) and self.mode != 'init': - return - model = str(rec.getAttribute('model')) - w_ref = rec.getAttribute('ref') - if len(w_ref): - id = self.id_get(cr, model, w_ref) - else: - assert rec.childNodes, 'You must define a child node if you dont give a ref' - element_childs = [i for i in rec.childNodes if i.nodeType == i.ELEMENT_NODE] - assert len(element_childs) == 1, 'Only one child node is accepted (%d given)' % len(rec.childNodes) - id = _eval_xml(self, element_childs[0], self.pool, cr, self.uid, self.idref) - - uid = self.get_uid(cr, self.uid, data_node, rec) - wf_service = netsvc.LocalService("workflow") - wf_service.trg_validate(uid, model, - id, - str(rec.getAttribute('action')), cr) - return False - - # - # Support two types of notation: - # name="Inventory Control/Sending Goods" - # or - # action="action_id" - # parent="parent_id" - # - def _tag_menuitem(self, cr, rec, data_node=None): - rec_id = rec.getAttribute("id").encode('ascii') - self._test_xml_id(rec_id) - m_l = map(escape, escape_re.split(rec.getAttribute("name").encode('utf8'))) - - values = {'parent_id': False} - if not rec.hasAttribute('parent'): - pid = False - for idx, menu_elem in enumerate(m_l): - if pid: - cr.execute('select id from ir_ui_menu where parent_id=%d and name=%s', (pid, menu_elem)) - else: - cr.execute('select id from ir_ui_menu where parent_id is null and name=%s', (menu_elem,)) - res = cr.fetchone() - if idx==len(m_l)-1: - values = {'parent_id': pid,'name':menu_elem} - elif res: - pid = res[0] - xml_id = idx==len(m_l)-1 and rec.getAttribute('id').encode('utf8') - try: - npid = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, 'ir.ui.menu', self.module, xml_id, idx==len(m_l)-1) - except: - print 'Menu Error', self.module, xml_id, idx==len(m_l)-1 - else: - # the menuitem does't exist but we are in branch (not a leaf) - self.logger.notifyChannel("init", netsvc.LOG_INFO, 'Warning no ID for submenu %s of menu %s !' % (menu_elem, str(m_l))) - pid = self.pool.get('ir.ui.menu').create(cr, self.uid, {'parent_id' : pid, 'name' : menu_elem}) - else: - menu_parent_id = self.id_get(cr, 'ir.ui.menu', rec.getAttribute('parent')) - values = {'parent_id': menu_parent_id} - if rec.hasAttribute('name'): - values['name'] = rec.getAttribute('name') - try: - res = [ self.id_get(cr, 'ir.ui.menu', rec.getAttribute('id')) ] - except: - res = None - - if rec.hasAttribute('action'): - a_action = rec.getAttribute('action').encode('utf8') - a_type = rec.getAttribute('type').encode('utf8') or 'act_window' - icons = { - "act_window": 'STOCK_NEW', - "report.xml": 'STOCK_PASTE', - "wizard": 'STOCK_EXECUTE', - "url": 'STOCK_JUMP_TO' - } - values['icon'] = icons.get(a_type,'STOCK_NEW') - if a_type=='act_window': - a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action) - cr.execute('select view_type,view_mode,name,view_id,target from ir_act_window where id=%d', (int(a_id),)) - action_type,action_mode,action_name,view_id,target = cr.fetchone() - if view_id: - cr.execute('SELECT type FROM ir_ui_view WHERE id=%d', (int(view_id),)) - action_mode, = cr.fetchone() - cr.execute('SELECT view_mode FROM ir_act_window_view WHERE act_window_id=%d ORDER BY sequence LIMIT 1', (int(a_id),)) - if cr.rowcount: - action_mode, = cr.fetchone() - if action_type=='tree': - values['icon'] = 'STOCK_INDENT' - elif action_mode and action_mode.startswith('tree'): - values['icon'] = 'STOCK_JUSTIFY_FILL' - elif action_mode and action_mode.startswith('graph'): - values['icon'] = 'terp-graph' - elif action_mode and action_mode.startswith('calendar'): - values['icon'] = 'terp-calendar' - if target=='new': - values['icon'] = 'STOCK_EXECUTE' - if not values.get('name', False): - values['name'] = action_name - elif a_type=='wizard': - a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action) - cr.execute('select name from ir_act_wizard where id=%d', (int(a_id),)) - resw = cr.fetchone() - if (not values.get('name', False)) and resw: - values['name'] = resw[0] - if rec.hasAttribute('sequence'): - values['sequence'] = int(rec.getAttribute('sequence')) - if rec.hasAttribute('icon'): - values['icon'] = str(rec.getAttribute('icon')) - if rec.hasAttribute('groups'): - g_names = rec.getAttribute('groups').split(',') - groups_value = [] - groups_obj = self.pool.get('res.groups') - for group in g_names: - if group.startswith('-'): - group_id = self.id_get(cr, 'res.groups', group[1:]) - groups_value.append((3, group_id)) - else: - group_id = self.id_get(cr, 'res.groups', group) - groups_value.append((4, group_id)) - values['groups_id'] = groups_value - - xml_id = rec.getAttribute('id').encode('utf8') - self._test_xml_id(xml_id) - pid = self.pool.get('ir.model.data')._update(cr, self.uid, 'ir.ui.menu', self.module, values, xml_id, True, mode=self.mode, res_id=res and res[0] or False) - - if rec_id and pid: - self.idref[rec_id] = int(pid) - - if rec.hasAttribute('action') and pid: - a_action = rec.getAttribute('action').encode('utf8') - a_type = rec.getAttribute('type').encode('utf8') or 'act_window' - a_id = self.id_get(cr, 'ir.actions.%s' % a_type, a_action) - action = "ir.actions.%s,%d" % (a_type, a_id) - self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', 'tree_but_open', 'Menuitem', [('ir.ui.menu', int(pid))], action, True, True, xml_id=rec_id) - return ('ir.ui.menu', pid) - - def _assert_equals(self, f1, f2, prec = 4): - return not round(f1 - f2, prec) - - def _tag_assert(self, cr, rec, data_node=None): - if self.isnoupdate(data_node) and self.mode != 'init': - return - - rec_model = rec.getAttribute("model").encode('ascii') - model = self.pool.get(rec_model) - assert model, "The model %s does not exist !" % (rec_model,) - rec_id = rec.getAttribute("id").encode('ascii') - self._test_xml_id(rec_id) - rec_src = rec.getAttribute("search").encode('utf8') - rec_src_count = rec.getAttribute("count") - - severity = rec.getAttribute("severity").encode('ascii') or 'info' - - rec_string = rec.getAttribute("string").encode('utf8') or 'unknown' - - ids = None - eval_dict = {'ref': _ref(self, cr)} - context = self.get_context(data_node, rec, eval_dict) - uid = self.get_uid(cr, self.uid, data_node, rec) - if len(rec_id): - ids = [self.id_get(cr, rec_model, rec_id)] - elif len(rec_src): - q = eval(rec_src, eval_dict) - ids = self.pool.get(rec_model).search(cr, uid, q, context=context) - if len(rec_src_count): - count = int(rec_src_count) - if len(ids) != count: - self.assert_report.record_assertion(False, severity) - self.logger.notifyChannel('init', severity, 'assertion "' + rec_string + '" failed ! (search count is incorrect: ' + str(len(ids)) + ')' ) - sevval = getattr(logging, severity.upper()) - if sevval > config['assert_exit_level']: - # TODO: define a dedicated exception - raise Exception('Severe assertion failure') - return - - assert ids != None, 'You must give either an id or a search criteria' - - ref = _ref(self, cr) - for id in ids: - brrec = model.browse(cr, uid, id, context) - class d(dict): - def __getitem__(self2, key): - if key in brrec: - return brrec[key] - return dict.__getitem__(self2, key) - globals = d() - globals['floatEqual'] = self._assert_equals - globals['ref'] = ref - globals['_ref'] = ref - for test in [i for i in rec.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="test")]: - f_expr = test.getAttribute("expr").encode('utf-8') - f_val = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True - if eval(f_expr, globals) != f_val: # assertion failed - self.assert_report.record_assertion(False, severity) - self.logger.notifyChannel('init', severity, 'assertion "' + rec_string + '" failed ! (tag ' + test.toxml() + ')' ) - sevval = getattr(logging, severity.upper()) - if sevval > config['assert_exit_level']: - # TODO: define a dedicated exception - raise Exception('Severe assertion failure') - return - else: # all tests were successful for this assertion tag (no break) - self.assert_report.record_assertion(True, severity) - - def _tag_record(self, cr, rec, data_node=None): - rec_model = rec.getAttribute("model").encode('ascii') - model = self.pool.get(rec_model) - assert model, "The model %s does not exist !" % (rec_model,) - rec_id = rec.getAttribute("id").encode('ascii') - self._test_xml_id(rec_id) - -# if not rec_id and not self.isnoupdate(data_node): -# print "Warning", rec_model - - if self.isnoupdate(data_node) and not self.mode == 'init': - # check if the xml record has an id string - if rec_id: - id = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, rec_model, self.module, rec_id) - # check if the resource already existed at the last update - if id: - # if it existed, we don't update the data, but we need to - # know the id of the existing record anyway - self.idref[rec_id] = int(id) - return None - else: - # if the resource didn't exist - if rec.getAttribute("forcecreate"): - # we want to create it, so we let the normal "update" behavior happen - pass - else: - # otherwise do nothing - return None - else: - # otherwise it is skipped - return None - - res = {} - for field in [i for i in rec.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="field")]: + base, id = xml_id.split('.') + if len(id) > 64: + self.logger.notifyChannel('init', netsvc.LOG_ERROR, 'id: %s is to long (max: 64)'%xml_id) + + def _tag_delete(self, cr, rec, data_node=None): + d_model = rec.getAttribute("model") + d_search = rec.getAttribute("search") + d_id = rec.getAttribute("id") + ids = [] + if len(d_search): + ids = self.pool.get(d_model).search(cr,self.uid,eval(d_search)) + if len(d_id): + ids.append(self.id_get(cr, d_model, d_id)) + if len(ids): + self.pool.get(d_model).unlink(cr, self.uid, ids) + #self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids, direct=True) + return False + + def _tag_report(self, cr, rec, data_node=None): + res = {} + for dest,f in (('name','string'),('model','model'),('report_name','name')): + res[dest] = rec.getAttribute(f).encode('utf8') + assert res[dest], "Attribute %s of report is empty !" % (f,) + for field,dest in (('rml','report_rml'),('xml','report_xml'),('xsl','report_xsl')): + if rec.hasAttribute(field): + res[dest] = rec.getAttribute(field).encode('utf8') + if rec.hasAttribute('auto'): + res['auto'] = eval(rec.getAttribute('auto')) + if rec.hasAttribute('sxw'): + sxw_content = misc.file_open(rec.getAttribute('sxw')).read() + res['report_sxw_content'] = sxw_content + if rec.hasAttribute('header'): + res['header'] = eval(rec.getAttribute('header')) + res['multi'] = rec.hasAttribute('multi') and eval(rec.getAttribute('multi')) + xml_id = rec.getAttribute('id').encode('utf8') + self._test_xml_id(xml_id) + id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.report.xml", self.module, res, xml_id, mode=self.mode) + self.idref[xml_id] = int(id) + if not rec.hasAttribute('menu') or eval(rec.getAttribute('menu')): + keyword = str(rec.getAttribute('keyword') or 'client_print_multi') + keys = [('action',keyword),('res_model',res['model'])] + value = 'ir.actions.report.xml,'+str(id) + replace = rec.hasAttribute('replace') and rec.getAttribute("replace") + self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, res['name'], [res['model']], value, replace=replace, isobject=True, xml_id=xml_id) + return False + + def _tag_function(self, cr, rec, data_node=None): + if self.isnoupdate(data_node) and self.mode != 'init': + return + context = self.get_context(data_node, rec, {'ref': _ref(self, cr)}) + uid = self.get_uid(cr, self.uid, data_node, rec) + _eval_xml(self,rec, self.pool, cr, uid, self.idref, context=context) + return False + + def _tag_wizard(self, cr, rec, data_node=None): + string = rec.getAttribute("string").encode('utf8') + model = rec.getAttribute("model").encode('utf8') + name = rec.getAttribute("name").encode('utf8') + xml_id = rec.getAttribute('id').encode('utf8') + self._test_xml_id(xml_id) + multi = rec.hasAttribute('multi') and eval(rec.getAttribute('multi')) + res = {'name': string, 'wiz_name': name, 'multi':multi} + + id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.wizard", self.module, res, xml_id, mode=self.mode) + self.idref[xml_id] = int(id) + # ir_set + if (not rec.hasAttribute('menu') or eval(rec.getAttribute('menu'))) and id: + keyword = str(rec.getAttribute('keyword') or 'client_action_multi') + keys = [('action',keyword),('res_model',model)] + value = 'ir.actions.wizard,'+str(id) + replace = rec.hasAttribute('replace') and \ + rec.getAttribute("replace") or True + self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, string, [model], value, replace=replace, isobject=True, xml_id=xml_id) + return False + + def _tag_url(self, cr, rec, data_node=None): + url = rec.getAttribute("string").encode('utf8') + target = rec.getAttribute("target").encode('utf8') + name = rec.getAttribute("name").encode('utf8') + xml_id = rec.getAttribute('id').encode('utf8') + self._test_xml_id(xml_id) + + res = {'name': name, 'url': url, 'target':target} + + id = self.pool.get('ir.model.data')._update(cr, self.uid, "ir.actions.url", self.module, res, xml_id, mode=self.mode) + self.idref[xml_id] = int(id) + # ir_set + if (not rec.hasAttribute('menu') or eval(rec.getAttribute('menu'))) and id: + keyword = str(rec.getAttribute('keyword') or 'client_action_multi') + keys = [('action',keyword)] + value = 'ir.actions.url,'+str(id) + replace = rec.hasAttribute('replace') and \ + rec.getAttribute("replace") or True + self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, url, ["ir.actions.url"], value, replace=replace, isobject=True, xml_id=xml_id) + return False + + def _tag_act_window(self, cr, rec, data_node=None): + name = rec.hasAttribute('name') and rec.getAttribute('name').encode('utf-8') + xml_id = rec.getAttribute('id').encode('utf8') + self._test_xml_id(xml_id) + type = rec.hasAttribute('type') and rec.getAttribute('type').encode('utf-8') or 'ir.actions.act_window' + view_id = False + if rec.hasAttribute('view'): + view_id = self.id_get(cr, 'ir.actions.act_window', rec.getAttribute('view').encode('utf-8')) + domain = rec.hasAttribute('domain') and rec.getAttribute('domain').encode('utf-8') + context = rec.hasAttribute('context') and rec.getAttribute('context').encode('utf-8') or '{}' + res_model = rec.getAttribute('res_model').encode('utf-8') + src_model = rec.hasAttribute('src_model') and rec.getAttribute('src_model').encode('utf-8') + view_type = rec.hasAttribute('view_type') and rec.getAttribute('view_type').encode('utf-8') or 'form' + view_mode = rec.hasAttribute('view_mode') and rec.getAttribute('view_mode').encode('utf-8') or 'tree,form' + usage = rec.hasAttribute('usage') and rec.getAttribute('usage').encode('utf-8') + limit = rec.hasAttribute('limit') and rec.getAttribute('limit').encode('utf-8') + auto_refresh = rec.hasAttribute('auto_refresh') \ + and rec.getAttribute('auto_refresh').encode('utf-8') + + res = { + 'name': name, + 'type': type, + 'view_id': view_id, + 'domain': domain, + 'context': context, + 'res_model': res_model, + 'src_model': src_model, + 'view_type': view_type, + 'view_mode': view_mode, + 'usage': usage, + 'limit': limit, + 'auto_refresh': auto_refresh, + } + if rec.hasAttribute('target'): + res['target'] = rec.getAttribute('target') + id = self.pool.get('ir.model.data')._update(cr, self.uid, 'ir.actions.act_window', self.module, res, xml_id, mode=self.mode) + self.idref[xml_id] = int(id) + + if src_model: + keyword = 'client_action_relate' + keys = [('action', keyword), ('res_model', res_model)] + value = 'ir.actions.act_window,'+str(id) + replace = rec.hasAttribute('replace') and rec.getAttribute('replace') + self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', keyword, xml_id, [src_model], value, replace=replace, isobject=True, xml_id=xml_id) + # TODO add remove ir.model.data + return False + + def _tag_ir_set(self, cr, rec, data_node=None): + if not self.mode=='init': + return False + res = {} + for field in [i for i in rec.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="field")]: + f_name = field.getAttribute("name").encode('utf-8') + f_val = _eval_xml(self,field,self.pool, cr, self.uid, self.idref) + res[f_name] = f_val + self.pool.get('ir.model.data').ir_set(cr, self.uid, res['key'], res['key2'], res['name'], res['models'], res['value'], replace=res.get('replace',True), isobject=res.get('isobject', False), meta=res.get('meta',None)) + return False + + def _tag_workflow(self, cr, rec, data_node=None): + if self.isnoupdate(data_node) and self.mode != 'init': + return + model = str(rec.getAttribute('model')) + w_ref = rec.getAttribute('ref') + if len(w_ref): + id = self.id_get(cr, model, w_ref) + else: + assert rec.childNodes, 'You must define a child node if you dont give a ref' + element_childs = [i for i in rec.childNodes if i.nodeType == i.ELEMENT_NODE] + assert len(element_childs) == 1, 'Only one child node is accepted (%d given)' % len(rec.childNodes) + id = _eval_xml(self, element_childs[0], self.pool, cr, self.uid, self.idref) + + uid = self.get_uid(cr, self.uid, data_node, rec) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, model, + id, + str(rec.getAttribute('action')), cr) + return False + + # + # Support two types of notation: + # name="Inventory Control/Sending Goods" + # or + # action="action_id" + # parent="parent_id" + # + def _tag_menuitem(self, cr, rec, data_node=None): + rec_id = rec.getAttribute("id").encode('ascii') + self._test_xml_id(rec_id) + m_l = map(escape, escape_re.split(rec.getAttribute("name").encode('utf8'))) + + values = {'parent_id': False} + if not rec.hasAttribute('parent'): + pid = False + for idx, menu_elem in enumerate(m_l): + if pid: + cr.execute('select id from ir_ui_menu where parent_id=%d and name=%s', (pid, menu_elem)) + else: + cr.execute('select id from ir_ui_menu where parent_id is null and name=%s', (menu_elem,)) + res = cr.fetchone() + if idx==len(m_l)-1: + values = {'parent_id': pid,'name':menu_elem} + elif res: + pid = res[0] + xml_id = idx==len(m_l)-1 and rec.getAttribute('id').encode('utf8') + try: + npid = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, 'ir.ui.menu', self.module, xml_id, idx==len(m_l)-1) + except: + print 'Menu Error', self.module, xml_id, idx==len(m_l)-1 + else: + # the menuitem does't exist but we are in branch (not a leaf) + self.logger.notifyChannel("init", netsvc.LOG_INFO, 'Warning no ID for submenu %s of menu %s !' % (menu_elem, str(m_l))) + pid = self.pool.get('ir.ui.menu').create(cr, self.uid, {'parent_id' : pid, 'name' : menu_elem}) + else: + menu_parent_id = self.id_get(cr, 'ir.ui.menu', rec.getAttribute('parent')) + values = {'parent_id': menu_parent_id} + if rec.hasAttribute('name'): + values['name'] = rec.getAttribute('name') + try: + res = [ self.id_get(cr, 'ir.ui.menu', rec.getAttribute('id')) ] + except: + res = None + + if rec.hasAttribute('action'): + a_action = rec.getAttribute('action').encode('utf8') + a_type = rec.getAttribute('type').encode('utf8') or 'act_window' + icons = { + "act_window": 'STOCK_NEW', + "report.xml": 'STOCK_PASTE', + "wizard": 'STOCK_EXECUTE', + "url": 'STOCK_JUMP_TO' + } + values['icon'] = icons.get(a_type,'STOCK_NEW') + if a_type=='act_window': + a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action) + cr.execute('select view_type,view_mode,name,view_id,target from ir_act_window where id=%d', (int(a_id),)) + action_type,action_mode,action_name,view_id,target = cr.fetchone() + if view_id: + cr.execute('SELECT type FROM ir_ui_view WHERE id=%d', (int(view_id),)) + action_mode, = cr.fetchone() + cr.execute('SELECT view_mode FROM ir_act_window_view WHERE act_window_id=%d ORDER BY sequence LIMIT 1', (int(a_id),)) + if cr.rowcount: + action_mode, = cr.fetchone() + if action_type=='tree': + values['icon'] = 'STOCK_INDENT' + elif action_mode and action_mode.startswith('tree'): + values['icon'] = 'STOCK_JUSTIFY_FILL' + elif action_mode and action_mode.startswith('graph'): + values['icon'] = 'terp-graph' + elif action_mode and action_mode.startswith('calendar'): + values['icon'] = 'terp-calendar' + if target=='new': + values['icon'] = 'STOCK_EXECUTE' + if not values.get('name', False): + values['name'] = action_name + elif a_type=='wizard': + a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action) + cr.execute('select name from ir_act_wizard where id=%d', (int(a_id),)) + resw = cr.fetchone() + if (not values.get('name', False)) and resw: + values['name'] = resw[0] + if rec.hasAttribute('sequence'): + values['sequence'] = int(rec.getAttribute('sequence')) + if rec.hasAttribute('icon'): + values['icon'] = str(rec.getAttribute('icon')) + if rec.hasAttribute('groups'): + g_names = rec.getAttribute('groups').split(',') + groups_value = [] + groups_obj = self.pool.get('res.groups') + for group in g_names: + if group.startswith('-'): + group_id = self.id_get(cr, 'res.groups', group[1:]) + groups_value.append((3, group_id)) + else: + group_id = self.id_get(cr, 'res.groups', group) + groups_value.append((4, group_id)) + values['groups_id'] = groups_value + + xml_id = rec.getAttribute('id').encode('utf8') + self._test_xml_id(xml_id) + pid = self.pool.get('ir.model.data')._update(cr, self.uid, 'ir.ui.menu', self.module, values, xml_id, True, mode=self.mode, res_id=res and res[0] or False) + + if rec_id and pid: + self.idref[rec_id] = int(pid) + + if rec.hasAttribute('action') and pid: + a_action = rec.getAttribute('action').encode('utf8') + a_type = rec.getAttribute('type').encode('utf8') or 'act_window' + a_id = self.id_get(cr, 'ir.actions.%s' % a_type, a_action) + action = "ir.actions.%s,%d" % (a_type, a_id) + self.pool.get('ir.model.data').ir_set(cr, self.uid, 'action', 'tree_but_open', 'Menuitem', [('ir.ui.menu', int(pid))], action, True, True, xml_id=rec_id) + return ('ir.ui.menu', pid) + + def _assert_equals(self, f1, f2, prec = 4): + return not round(f1 - f2, prec) + + def _tag_assert(self, cr, rec, data_node=None): + if self.isnoupdate(data_node) and self.mode != 'init': + return + + rec_model = rec.getAttribute("model").encode('ascii') + model = self.pool.get(rec_model) + assert model, "The model %s does not exist !" % (rec_model,) + rec_id = rec.getAttribute("id").encode('ascii') + self._test_xml_id(rec_id) + rec_src = rec.getAttribute("search").encode('utf8') + rec_src_count = rec.getAttribute("count") + + severity = rec.getAttribute("severity").encode('ascii') or 'info' + + rec_string = rec.getAttribute("string").encode('utf8') or 'unknown' + + ids = None + eval_dict = {'ref': _ref(self, cr)} + context = self.get_context(data_node, rec, eval_dict) + uid = self.get_uid(cr, self.uid, data_node, rec) + if len(rec_id): + ids = [self.id_get(cr, rec_model, rec_id)] + elif len(rec_src): + q = eval(rec_src, eval_dict) + ids = self.pool.get(rec_model).search(cr, uid, q, context=context) + if len(rec_src_count): + count = int(rec_src_count) + if len(ids) != count: + self.assert_report.record_assertion(False, severity) + self.logger.notifyChannel('init', severity, 'assertion "' + rec_string + '" failed ! (search count is incorrect: ' + str(len(ids)) + ')' ) + sevval = getattr(logging, severity.upper()) + if sevval > config['assert_exit_level']: + # TODO: define a dedicated exception + raise Exception('Severe assertion failure') + return + + assert ids != None, 'You must give either an id or a search criteria' + + ref = _ref(self, cr) + for id in ids: + brrec = model.browse(cr, uid, id, context) + class d(dict): + def __getitem__(self2, key): + if key in brrec: + return brrec[key] + return dict.__getitem__(self2, key) + globals = d() + globals['floatEqual'] = self._assert_equals + globals['ref'] = ref + globals['_ref'] = ref + for test in [i for i in rec.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="test")]: + f_expr = test.getAttribute("expr").encode('utf-8') + f_val = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True + if eval(f_expr, globals) != f_val: # assertion failed + self.assert_report.record_assertion(False, severity) + self.logger.notifyChannel('init', severity, 'assertion "' + rec_string + '" failed ! (tag ' + test.toxml() + ')' ) + sevval = getattr(logging, severity.upper()) + if sevval > config['assert_exit_level']: + # TODO: define a dedicated exception + raise Exception('Severe assertion failure') + return + else: # all tests were successful for this assertion tag (no break) + self.assert_report.record_assertion(True, severity) + + def _tag_record(self, cr, rec, data_node=None): + rec_model = rec.getAttribute("model").encode('ascii') + model = self.pool.get(rec_model) + assert model, "The model %s does not exist !" % (rec_model,) + rec_id = rec.getAttribute("id").encode('ascii') + self._test_xml_id(rec_id) + +# if not rec_id and not self.isnoupdate(data_node): +# print "Warning", rec_model + + if self.isnoupdate(data_node) and not self.mode == 'init': + # check if the xml record has an id string + if rec_id: + id = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, rec_model, self.module, rec_id) + # check if the resource already existed at the last update + if id: + # if it existed, we don't update the data, but we need to + # know the id of the existing record anyway + self.idref[rec_id] = int(id) + return None + else: + # if the resource didn't exist + if rec.getAttribute("forcecreate"): + # we want to create it, so we let the normal "update" behavior happen + pass + else: + # otherwise do nothing + return None + else: + # otherwise it is skipped + return None + + res = {} + for field in [i for i in rec.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="field")]: #TODO: most of this code is duplicated above (in _eval_xml)... - f_name = field.getAttribute("name").encode('utf-8') - f_ref = field.getAttribute("ref").encode('ascii') - f_search = field.getAttribute("search").encode('utf-8') - f_model = field.getAttribute("model").encode('ascii') - if not f_model and model._columns.get(f_name,False): - f_model = model._columns[f_name]._obj - f_use = field.getAttribute("use").encode('ascii') or 'id' - f_val = False - - if len(f_search): - q = eval(f_search, self.idref) - field = [] - assert f_model, 'Define an attribute model="..." in your .XML file !' - f_obj = self.pool.get(f_model) - # browse the objects searched - s = f_obj.browse(cr, self.uid, f_obj.search(cr, self.uid, q)) - # column definitions of the "local" object - _cols = self.pool.get(rec_model)._columns - # if the current field is many2many - if (f_name in _cols) and _cols[f_name]._type=='many2many': - f_val = [(6, 0, map(lambda x: x[f_use], s))] - elif len(s): - # otherwise (we are probably in a many2one field), - # take the first element of the search - f_val = s[0][f_use] - elif len(f_ref): - if f_ref=="null": - f_val = False - else: - f_val = self.id_get(cr, f_model, f_ref) - else: - f_val = _eval_xml(self,field, self.pool, cr, self.uid, self.idref) - if model._columns.has_key(f_name): - if isinstance(model._columns[f_name], osv.fields.integer): - f_val = int(f_val) - res[f_name] = f_val - id = self.pool.get('ir.model.data')._update(cr, self.uid, rec_model, self.module, res, rec_id or False, not self.isnoupdate(data_node), noupdate=self.isnoupdate(data_node), mode=self.mode ) - if rec_id: - self.idref[rec_id] = int(id) - if config.get('commit_mode', False): - cr.commit() - return rec_model, id - - def id_get(self, cr, model, id_str): - if id_str in self.idref: - return self.idref[id_str] - mod = self.module - if '.' in id_str: - mod,id_str = id_str.split('.') - result = self.pool.get('ir.model.data')._get_id(cr, self.uid, mod, id_str) - return int(self.pool.get('ir.model.data').read(cr, self.uid, [result], ['res_id'])[0]['res_id']) - - def parse(self, xmlstr): - d = xml.dom.minidom.parseString(xmlstr) - de = d.documentElement - for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="data")]: - for rec in n.childNodes: - if rec.nodeType == rec.ELEMENT_NODE: - if rec.nodeName in self._tags: - try: - self._tags[rec.nodeName](self.cr, rec, n) - except: - self.logger.notifyChannel("init", netsvc.LOG_INFO, '\n'+rec.toxml()) - self.cr.rollback() - raise - return True - - def __init__(self, cr, module, idref, mode, report=assertion_report(), noupdate = False): - self.logger = netsvc.Logger() - self.mode = mode - self.module = module - self.cr = cr - self.idref = idref - self.pool = pooler.get_pool(cr.dbname) -# self.pool = osv.osv.FakePool(module) - self.uid = 1 - self.assert_report = report - self.noupdate = noupdate - self._tags = { - 'menuitem': self._tag_menuitem, - 'record': self._tag_record, - 'assert': self._tag_assert, - 'report': self._tag_report, - 'wizard': self._tag_wizard, - 'delete': self._tag_delete, - 'ir_set': self._tag_ir_set, - 'function': self._tag_function, - 'workflow': self._tag_workflow, - 'act_window': self._tag_act_window, - 'url': self._tag_url - } + f_name = field.getAttribute("name").encode('utf-8') + f_ref = field.getAttribute("ref").encode('ascii') + f_search = field.getAttribute("search").encode('utf-8') + f_model = field.getAttribute("model").encode('ascii') + if not f_model and model._columns.get(f_name,False): + f_model = model._columns[f_name]._obj + f_use = field.getAttribute("use").encode('ascii') or 'id' + f_val = False + + if len(f_search): + q = eval(f_search, self.idref) + field = [] + assert f_model, 'Define an attribute model="..." in your .XML file !' + f_obj = self.pool.get(f_model) + # browse the objects searched + s = f_obj.browse(cr, self.uid, f_obj.search(cr, self.uid, q)) + # column definitions of the "local" object + _cols = self.pool.get(rec_model)._columns + # if the current field is many2many + if (f_name in _cols) and _cols[f_name]._type=='many2many': + f_val = [(6, 0, map(lambda x: x[f_use], s))] + elif len(s): + # otherwise (we are probably in a many2one field), + # take the first element of the search + f_val = s[0][f_use] + elif len(f_ref): + if f_ref=="null": + f_val = False + else: + f_val = self.id_get(cr, f_model, f_ref) + else: + f_val = _eval_xml(self,field, self.pool, cr, self.uid, self.idref) + if model._columns.has_key(f_name): + if isinstance(model._columns[f_name], osv.fields.integer): + f_val = int(f_val) + res[f_name] = f_val + id = self.pool.get('ir.model.data')._update(cr, self.uid, rec_model, self.module, res, rec_id or False, not self.isnoupdate(data_node), noupdate=self.isnoupdate(data_node), mode=self.mode ) + if rec_id: + self.idref[rec_id] = int(id) + if config.get('commit_mode', False): + cr.commit() + return rec_model, id + + def id_get(self, cr, model, id_str): + if id_str in self.idref: + return self.idref[id_str] + mod = self.module + if '.' in id_str: + mod,id_str = id_str.split('.') + result = self.pool.get('ir.model.data')._get_id(cr, self.uid, mod, id_str) + return int(self.pool.get('ir.model.data').read(cr, self.uid, [result], ['res_id'])[0]['res_id']) + + def parse(self, xmlstr): + d = xml.dom.minidom.parseString(xmlstr) + de = d.documentElement + for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="data")]: + for rec in n.childNodes: + if rec.nodeType == rec.ELEMENT_NODE: + if rec.nodeName in self._tags: + try: + self._tags[rec.nodeName](self.cr, rec, n) + except: + self.logger.notifyChannel("init", netsvc.LOG_INFO, '\n'+rec.toxml()) + self.cr.rollback() + raise + return True + + def __init__(self, cr, module, idref, mode, report=assertion_report(), noupdate = False): + self.logger = netsvc.Logger() + self.mode = mode + self.module = module + self.cr = cr + self.idref = idref + self.pool = pooler.get_pool(cr.dbname) +# self.pool = osv.osv.FakePool(module) + self.uid = 1 + self.assert_report = report + self.noupdate = noupdate + self._tags = { + 'menuitem': self._tag_menuitem, + 'record': self._tag_record, + 'assert': self._tag_assert, + 'report': self._tag_report, + 'wizard': self._tag_wizard, + 'delete': self._tag_delete, + 'ir_set': self._tag_ir_set, + 'function': self._tag_function, + 'workflow': self._tag_workflow, + 'act_window': self._tag_act_window, + 'url': self._tag_url + } def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init', - noupdate=False): - '''Import csv file : - quote: " - delimiter: , - encoding: utf-8''' - if not idref: - idref={} - model = ('.'.join(fname.split('.')[:-1]).split('-'))[0] - #remove folder path from model - head, model = os.path.split(model) - - pool = pooler.get_pool(cr.dbname) - - input = StringIO.StringIO(csvcontent) - reader = csv.reader(input, quotechar='"', delimiter=',') - fields = reader.next() - - if not (mode == 'init' or 'id' in fields): - return - - uid = 1 - datas = [] - for line in reader: - if (not line) or not reduce(lambda x,y: x or y, line) : - continue - datas.append( map(lambda x:x.decode('utf8').encode('utf8'), line)) - pool.get(model).import_data(cr, uid, fields, datas,mode, module,noupdate) + noupdate=False): + '''Import csv file : + quote: " + delimiter: , + encoding: utf-8''' + if not idref: + idref={} + model = ('.'.join(fname.split('.')[:-1]).split('-'))[0] + #remove folder path from model + head, model = os.path.split(model) + + pool = pooler.get_pool(cr.dbname) + + input = StringIO.StringIO(csvcontent) + reader = csv.reader(input, quotechar='"', delimiter=',') + fields = reader.next() + + if not (mode == 'init' or 'id' in fields): + return + + uid = 1 + datas = [] + for line in reader: + if (not line) or not reduce(lambda x,y: x or y, line) : + continue + datas.append( map(lambda x:x.decode('utf8').encode('utf8'), line)) + pool.get(model).import_data(cr, uid, fields, datas,mode, module,noupdate) # # xml import/export # def convert_xml_import(cr, module, xmlfile, idref=None, mode='init', noupdate = False, report=None): - xmlstr = xmlfile.read() - xmlfile.seek(0) - relaxng_doc = etree.parse(file(os.path.join( config['root_path'], 'import_xml.rng' ))) - relaxng = etree.RelaxNG(relaxng_doc) - - doc = etree.parse(xmlfile) - try: - relaxng.assert_(doc) - except Exception, e: - logger = netsvc.Logger() - logger.notifyChannel('init', netsvc.LOG_ERROR, 'The XML file do not fit the required schema !') - logger.notifyChannel('init', netsvc.LOG_ERROR, relaxng.error_log.last_error) - raise - - if not idref: - idref={} - if report is None: - report=assertion_report() - obj = xml_import(cr, module, idref, mode, report=report, noupdate = noupdate) - obj.parse(xmlstr) - del obj - return True + xmlstr = xmlfile.read() + xmlfile.seek(0) + relaxng_doc = etree.parse(file(os.path.join( config['root_path'], 'import_xml.rng' ))) + relaxng = etree.RelaxNG(relaxng_doc) + + doc = etree.parse(xmlfile) + try: + relaxng.assert_(doc) + except Exception, e: + logger = netsvc.Logger() + logger.notifyChannel('init', netsvc.LOG_ERROR, 'The XML file do not fit the required schema !') + logger.notifyChannel('init', netsvc.LOG_ERROR, relaxng.error_log.last_error) + raise + + if not idref: + idref={} + if report is None: + report=assertion_report() + obj = xml_import(cr, module, idref, mode, report=report, noupdate = noupdate) + obj.parse(xmlstr) + del obj + return True def convert_xml_export(res): - uid=1 - pool=pooler.get_pool(cr.dbname) - cr=pooler.db.cursor() - idref = {} - d = xml.dom.minidom.getDOMImplementation().createDocument(None, "terp", None) - de = d.documentElement - data=d.createElement("data") - de.appendChild(data) - de.appendChild(d.createTextNode('Some textual content.')) - cr.commit() - cr.close() + uid=1 + pool=pooler.get_pool(cr.dbname) + cr=pooler.db.cursor() + idref = {} + d = xml.dom.minidom.getDOMImplementation().createDocument(None, "terp", None) + de = d.documentElement + data=d.createElement("data") + de.appendChild(data) + de.appendChild(d.createTextNode('Some textual content.')) + cr.commit() + cr.close() diff --git a/bin/tools/graph.py b/bin/tools/graph.py index c271726b79c..697bbd936d8 100644 --- a/bin/tools/graph.py +++ b/bin/tools/graph.py @@ -28,306 +28,306 @@ ############################################################################### class graph(object): - def __init__(self, nodes, transitions): - self.nodes = nodes - self.links = transitions - trans = {} - for t in transitions: - trans.setdefault(t[0], []) - trans[t[0]].append(t[1]) - self.transitions = trans - self.result = {} - self.levels = {} - - def get_parent(self,node): - count = 0 - for item in self.transitions: - if self.transitions[item].__contains__(node): - count +=1 - return count - - def init_rank(self): - self.temp = {} - for link in self.links: - self.temp[link] = self.result[link[1]]['y'] - self.result[link[0]]['y'] - - cnt = 0 - list_node = [] - list_edge = [] - - while self.tight_tree()<self.result.__len__(): - cnt+=1 - list_node = [] - - for node in self.nodes: - if node not in self.reachable_nodes: - list_node.append(node) - list_edge = [] - - for link in self.temp: - if link not in self.tree_edges: - list_edge.append(link) - - slack = 100 - - for edge in list_edge: - if (self.reachable_nodes.__contains__(edge[0]) and edge[1] not in self.reachable_nodes) or ( self.reachable_nodes.__contains__(edge[1]) and edge[0] not in self.reachable_nodes): - if(slack>self.temp[edge]-1): - slack = self.temp[edge]-1 - new_edge = edge - - if new_edge[0] not in self.reachable_nodes: - delta = -(self.temp[new_edge]-1) - else: - delta = self.temp[new_edge]-1 - - for node in self.result: - if node in self.reachable_nodes: - self.result[node]['y'] += delta - - for link in self.temp: - self.temp[link] = self.result[link[1]]['y'] - self.result[link[0]]['y'] - - self.init_cutvalues() - - - - - def tight_tree(self,): - self.reachable_nodes = [] - self.tree_edges = [] - self.reachable_node(self.start) - return self.reachable_nodes.__len__() - - def reachable_node(self,node): - if node not in self.reachable_nodes: - self.reachable_nodes.append(node) - for link in self.temp: - if link[0]==node: -# print link[0] - if self.temp[link]==1: - self.tree_edges.append(link) - if link[1] not in self.reachable_nodes: - self.reachable_nodes.append(link[1]) - self.reachable_node(link[1]) - - - def init_cutvalues(self): - self.cut_edges = {} - self.head_nodes = [] - i=0; - for edge in self.tree_edges: - self.head_nodes = [] - rest_edges = [] - rest_edges += self.tree_edges - rest_edges.__delitem__(i) - self.head_component(self.start,rest_edges) - i+=1 - positive = 0 - negative = 0 - for source_node in self.transitions: - if source_node in self.head_nodes: - for dest_node in self.transitions[source_node]: - if dest_node not in self.head_nodes: - negative+=1 - else: - for dest_node in self.transitions[source_node]: - if dest_node in self.head_nodes: - positive+=1 + def __init__(self, nodes, transitions): + self.nodes = nodes + self.links = transitions + trans = {} + for t in transitions: + trans.setdefault(t[0], []) + trans[t[0]].append(t[1]) + self.transitions = trans + self.result = {} + self.levels = {} + + def get_parent(self,node): + count = 0 + for item in self.transitions: + if self.transitions[item].__contains__(node): + count +=1 + return count + + def init_rank(self): + self.temp = {} + for link in self.links: + self.temp[link] = self.result[link[1]]['y'] - self.result[link[0]]['y'] + + cnt = 0 + list_node = [] + list_edge = [] + + while self.tight_tree()<self.result.__len__(): + cnt+=1 + list_node = [] + + for node in self.nodes: + if node not in self.reachable_nodes: + list_node.append(node) + list_edge = [] + + for link in self.temp: + if link not in self.tree_edges: + list_edge.append(link) + + slack = 100 + + for edge in list_edge: + if (self.reachable_nodes.__contains__(edge[0]) and edge[1] not in self.reachable_nodes) or ( self.reachable_nodes.__contains__(edge[1]) and edge[0] not in self.reachable_nodes): + if(slack>self.temp[edge]-1): + slack = self.temp[edge]-1 + new_edge = edge + + if new_edge[0] not in self.reachable_nodes: + delta = -(self.temp[new_edge]-1) + else: + delta = self.temp[new_edge]-1 + + for node in self.result: + if node in self.reachable_nodes: + self.result[node]['y'] += delta + + for link in self.temp: + self.temp[link] = self.result[link[1]]['y'] - self.result[link[0]]['y'] + + self.init_cutvalues() + + + + + def tight_tree(self,): + self.reachable_nodes = [] + self.tree_edges = [] + self.reachable_node(self.start) + return self.reachable_nodes.__len__() + + def reachable_node(self,node): + if node not in self.reachable_nodes: + self.reachable_nodes.append(node) + for link in self.temp: + if link[0]==node: +# print link[0] + if self.temp[link]==1: + self.tree_edges.append(link) + if link[1] not in self.reachable_nodes: + self.reachable_nodes.append(link[1]) + self.reachable_node(link[1]) + + + def init_cutvalues(self): + self.cut_edges = {} + self.head_nodes = [] + i=0; + for edge in self.tree_edges: + self.head_nodes = [] + rest_edges = [] + rest_edges += self.tree_edges + rest_edges.__delitem__(i) + self.head_component(self.start,rest_edges) + i+=1 + positive = 0 + negative = 0 + for source_node in self.transitions: + if source_node in self.head_nodes: + for dest_node in self.transitions[source_node]: + if dest_node not in self.head_nodes: + negative+=1 + else: + for dest_node in self.transitions[source_node]: + if dest_node in self.head_nodes: + positive+=1 - self.cut_edges[edge] = positive - negative - - + self.cut_edges[edge] = positive - negative + + - - def head_component(self, node, rest_edges): - if node not in self.head_nodes: - self.head_nodes.append(node) - for link in rest_edges: - if link[0]==node: - self.head_component(link[1],rest_edges) - + + def head_component(self, node, rest_edges): + if node not in self.head_nodes: + self.head_nodes.append(node) + for link in rest_edges: + if link[0]==node: + self.head_component(link[1],rest_edges) + - def process_ranking(self, node, level=0): - if node not in self.result: - self.result[node] = {'x': None, 'y':level, 'mark':0} - else: - if level > self.result[node]['y']: - self.result[node]['y'] = level - if self.result[node]['mark']==0: - self.result[node]['mark'] = 1 - for t in self.transitions.get(node, []): - self.process_ranking(t, level+1) - + def process_ranking(self, node, level=0): + if node not in self.result: + self.result[node] = {'x': None, 'y':level, 'mark':0} + else: + if level > self.result[node]['y']: + self.result[node]['y'] = level + if self.result[node]['mark']==0: + self.result[node]['mark'] = 1 + for t in self.transitions.get(node, []): + self.process_ranking(t, level+1) + - def preprocess_order(self): - levels = {} - for r in self.result: - l = self.result[r]['y'] - levels.setdefault(l,[]) - levels[l].append(r) - self.levels = levels + def preprocess_order(self): + levels = {} + for r in self.result: + l = self.result[r]['y'] + levels.setdefault(l,[]) + levels[l].append(r) + self.levels = levels - def process_order(self, level): - self.levels[level].sort(lambda x,y: cmp(self.result[x]['x'], self.result[y]['x'])) - for nodepos in range(len(self.levels[level])): - node = self.levels[level][nodepos] - if nodepos == 0: - left = self.result[node]['x']- 0.5 - else: - left = (self.result[node]['x'] + self.result[self.levels[level][nodepos-1]]['x']) / 2.0 + def process_order(self, level): + self.levels[level].sort(lambda x,y: cmp(self.result[x]['x'], self.result[y]['x'])) + for nodepos in range(len(self.levels[level])): + node = self.levels[level][nodepos] + if nodepos == 0: + left = self.result[node]['x']- 0.5 + else: + left = (self.result[node]['x'] + self.result[self.levels[level][nodepos-1]]['x']) / 2.0 - if nodepos == (len(self.levels[level])-1): - right = self.result[node]['x'] + 0.5 - else: - right = (self.result[node]['x'] + self.result[self.levels[level][nodepos+1]]['x']) / 2.0 + if nodepos == (len(self.levels[level])-1): + right = self.result[node]['x'] + 0.5 + else: + right = (self.result[node]['x'] + self.result[self.levels[level][nodepos+1]]['x']) / 2.0 - if self.transitions.get(node, False): - if len(self.transitions[node])==1: - pos = (left+right)/2.0 - step = 0 - else: - pos = left - step = (-left+right) / (len(self.transitions[node])-1) + if self.transitions.get(node, False): + if len(self.transitions[node])==1: + pos = (left+right)/2.0 + step = 0 + else: + pos = left + step = (-left+right) / (len(self.transitions[node])-1) - for n2 in self.transitions[node]: - self.result[n2]['x'] = pos - pos += step - - def exchange(self,e,f): - self.tree_edges.__delitem__(self.tree_edges.index(e)) - self.tree_edges.append(f) - self.init_cutvalues() - - - def enter_edge(self,edge): - self.head_nodes = [] - rest_edges = [] - rest_edges += self.tree_edges - rest_edges.__delitem__(rest_edges.index(edge)) - self.head_component(self.start,rest_edges) - slack = 100 - for source_node in self.transitions: - if source_node in self.head_nodes: - for dest_node in self.transitions[source_node]: - if dest_node not in self.head_nodes: - if(slack>(self.temp[edge]-1)): - slack = self.temp[edge]-1 - new_edge = (source_node,dest_node) - return new_edge - + for n2 in self.transitions[node]: + self.result[n2]['x'] = pos + pos += step + + def exchange(self,e,f): + self.tree_edges.__delitem__(self.tree_edges.index(e)) + self.tree_edges.append(f) + self.init_cutvalues() + + + def enter_edge(self,edge): + self.head_nodes = [] + rest_edges = [] + rest_edges += self.tree_edges + rest_edges.__delitem__(rest_edges.index(edge)) + self.head_component(self.start,rest_edges) + slack = 100 + for source_node in self.transitions: + if source_node in self.head_nodes: + for dest_node in self.transitions[source_node]: + if dest_node not in self.head_nodes: + if(slack>(self.temp[edge]-1)): + slack = self.temp[edge]-1 + new_edge = (source_node,dest_node) + return new_edge + - def leave_edge(self): - for edge in self.cut_edges: - if self.cut_edges[edge]<0: - return edge - return () + def leave_edge(self): + for edge in self.cut_edges: + if self.cut_edges[edge]<0: + return edge + return () - def process(self, starting_node): - pos = (len(starting_node) - 1.0)/2.0 - self.start = starting_node[0] - for s in starting_node: - self.process_ranking(s) - self.result[s]['x'] = pos - pos += 1.0 - self.init_rank() - #normalize - least_rank=100 - - #normalization - for node in self.result: - if least_rank>self.result[node]['y']: - least_rank = self.result[node]['y'] - - if(least_rank!=0): - diff = least_rank - for node in self.result: - self.result[node]['y']-=least_rank - - e = self.leave_edge() - #while e: - f = self.enter_edge(e) - self.exchange(e,f) - e = self.leave_edge() - - - - self.preprocess_order() - for n in self.levels: - self.process_order(n) - - - + def process(self, starting_node): + pos = (len(starting_node) - 1.0)/2.0 + self.start = starting_node[0] + for s in starting_node: + self.process_ranking(s) + self.result[s]['x'] = pos + pos += 1.0 + self.init_rank() + #normalize + least_rank=100 + + #normalization + for node in self.result: + if least_rank>self.result[node]['y']: + least_rank = self.result[node]['y'] + + if(least_rank!=0): + diff = least_rank + for node in self.result: + self.result[node]['y']-=least_rank + + e = self.leave_edge() + #while e: + f = self.enter_edge(e) + self.exchange(e,f) + e = self.leave_edge() + + + + self.preprocess_order() + for n in self.levels: + self.process_order(n) + + + - def __str__(self): - result = '' - for l in self.levels: - result += 'PosY: ' + str(l) + '\n' - for node in self.levels[l]: - result += '\tPosX: '+ str(self.result[node]['x']) + ' - Node:' + node + "\n" - return result + def __str__(self): + result = '' + for l in self.levels: + result += 'PosY: ' + str(l) + '\n' + for node in self.levels[l]: + result += '\tPosX: '+ str(self.result[node]['x']) + ' - Node:' + node + "\n" + return result - def scale(self, maxx, maxy, plusx2=0, plusy2=0): - plusx = - min(map(lambda x: x['x'],self.result.values())) - plusy = - min(map(lambda x: x['y'],self.result.values())) + def scale(self, maxx, maxy, plusx2=0, plusy2=0): + plusx = - min(map(lambda x: x['x'],self.result.values())) + plusy = - min(map(lambda x: x['y'],self.result.values())) - maxcurrent = 1.0 - diff = 1.0 - for l in self.levels: - for n in range(1, len(self.levels[l])): - n1 = self.levels[l][n] - n2 = self.levels[l][n-1] - diff = abs(self.result[n2]['x']-self.result[n1]['x']) - if diff<maxcurrent: - maxcurrent=diff - factor = maxx / diff - for r in self.result: - self.result[r]['x'] = (self.result[r]['x']+plusx) * factor + plusx2 - self.result[r]['y'] = (self.result[r]['y']+plusy) * maxy + plusy2 + maxcurrent = 1.0 + diff = 1.0 + for l in self.levels: + for n in range(1, len(self.levels[l])): + n1 = self.levels[l][n] + n2 = self.levels[l][n-1] + diff = abs(self.result[n2]['x']-self.result[n1]['x']) + if diff<maxcurrent: + maxcurrent=diff + factor = maxx / diff + for r in self.result: + self.result[r]['x'] = (self.result[r]['x']+plusx) * factor + plusx2 + self.result[r]['y'] = (self.result[r]['y']+plusy) * maxy + plusy2 - def result_get(self): - return self.result + def result_get(self): + return self.result if __name__=='__main__': - starting_node = ['profile'] # put here nodes with flow_start=True - nodes = ['project','account','hr','base','product','mrp','test','profile'] - transitions = [ - ('profile','mrp'), - ('mrp','project'), - ('project','product'), - ('mrp','hr'), - ('mrp','test'), - ('project','account'), - ('project','hr'), - ('product','base'), - ('account','product'), - ('account','test'), - ('account','base'), - ('hr','base'), - ('test','base') - ] + starting_node = ['profile'] # put here nodes with flow_start=True + nodes = ['project','account','hr','base','product','mrp','test','profile'] + transitions = [ + ('profile','mrp'), + ('mrp','project'), + ('project','product'), + ('mrp','hr'), + ('mrp','test'), + ('project','account'), + ('project','hr'), + ('product','base'), + ('account','product'), + ('account','test'), + ('account','base'), + ('hr','base'), + ('test','base') + ] - radius = 20 - g = graph(nodes, transitions) - g.process(starting_node) - g.scale(radius*3,radius*3, radius, radius) + radius = 20 + g = graph(nodes, transitions) + g.process(starting_node) + g.scale(radius*3,radius*3, radius, radius) - print g + print g - import Image - import ImageDraw - img = Image.new("RGB", (800, 600), "#ffffff") - draw = ImageDraw.Draw(img) + import Image + import ImageDraw + img = Image.new("RGB", (800, 600), "#ffffff") + draw = ImageDraw.Draw(img) - for name,node in g.result.items(): - draw.arc( (int(node['x']-radius), int(node['y']-radius),int(node['x']+radius), int(node['y']+radius) ), 0, 360, (128,128,128)) - draw.text( (int(node['x']), int(node['y'])), name, (128,128,128)) + for name,node in g.result.items(): + draw.arc( (int(node['x']-radius), int(node['y']-radius),int(node['x']+radius), int(node['y']+radius) ), 0, 360, (128,128,128)) + draw.text( (int(node['x']), int(node['y'])), name, (128,128,128)) - for nodefrom in g.transitions: - for nodeto in g.transitions[nodefrom]: - draw.line( (int(g.result[nodefrom]['x']), int(g.result[nodefrom]['y']),int(g.result[nodeto]['x']),int(g.result[nodeto]['y'])),(128,128,128) ) + for nodefrom in g.transitions: + for nodeto in g.transitions[nodefrom]: + draw.line( (int(g.result[nodefrom]['x']), int(g.result[nodefrom]['y']),int(g.result[nodeto]['x']),int(g.result[nodeto]['y'])),(128,128,128) ) - img.save("graph.png", "PNG") + img.save("graph.png", "PNG") diff --git a/bin/tools/import_email.py b/bin/tools/import_email.py index d09a3090e8d..e31d8e13800 100644 --- a/bin/tools/import_email.py +++ b/bin/tools/import_email.py @@ -43,131 +43,131 @@ Le message avec le sujet "%s" n'a pu être archivé dans l'ERP. class EmailParser(object): - def __init__(self, headers, dispatcher): - self.headers = headers - self.dispatcher = dispatcher - - def parse(self, msg): - dispatcher((self.headers, msg)) + def __init__(self, headers, dispatcher): + self.headers = headers + self.dispatcher = dispatcher + + def parse(self, msg): + dispatcher((self.headers, msg)) class CommandDispatcher(object): - def __init__(self, receiver): - self.receiver = receiver - - def __call__(self, request): - return self.receiver(request) + def __init__(self, receiver): + self.receiver = receiver + + def __call__(self, request): + return self.receiver(request) class RPCProxy(object): - def __init__(self, uid, passwd, host='localhost', port=8069, path='object'): - self.rpc = xmlrpclib.ServerProxy('http://%s:%s/%s' % (host, port, path)) - self.user_id = uid - self.passwd = passwd - - def __call__(self, request): - return self.rpc.execute(self.user_id, self.passwd, *request) + def __init__(self, uid, passwd, host='localhost', port=8069, path='object'): + self.rpc = xmlrpclib.ServerProxy('http://%s:%s/%s' % (host, port, path)) + self.user_id = uid + self.passwd = passwd + + def __call__(self, request): + return self.rpc.execute(self.user_id, self.passwd, *request) class ReceiverEmail2Event(object): - email_re = re.compile(r""" - ([a-zA-Z][\w\.-]*[a-zA-Z0-9] # username part - @ # mandatory @ sign - [a-zA-Z0-9][\w\.-]* # domain must start with a letter - \. - [a-z]{2,3} # TLD - ) - """, re.VERBOSE) - - project_re = re.compile(r"^ *\[?(\d{4}\.?\d{0,3})\]?", re.UNICODE) - - - def __init__(self, rpc): - self.rpc = rpc - - def get_addresses(self, headers, msg): - hcontent = '' - for header in [h for h in headers if msg.has_key(h)]: - hcontent += msg[header] - return self.email_re.findall(hcontent) - - def get_partners(self, headers, msg): - alladdresses = self.get_addresses(headers, msg) - address_ids = self.rpc(('res.partner.address', 'search', [('email', 'in', alladdresses)])) - addresses = self.rpc(('res.partner.address', 'read', address_ids)) - return [x['partner_id'][0] for x in addresses] - - def __call__(self, request): - headers, msg = request - partners = self.get_partners(headers, msg) - subject = u'' - for string, charset in decode_header(msg['Subject']): - if charset: - subject += string.decode(charset) - else: - subject += unicode(string) - if partners: - self.save_mail(msg, subject, partners) - else: - warning = MIMEText((warn_msg % (subject,)).encode('utf-8'), 'plain', 'utf-8') - warning['Subject'] = 'Message de TinyERP' - warning['From'] = 'erp@steel-sa.com' - warning['To'] = msg['From'] - s = smtplib.SMTP() - s.connect() - s.sendmail('erp@steel-sa.com', self.email_re.findall(msg['From']), warning.as_string()) - s.close() - - if msg.is_multipart(): - for message in [m for m in msg.get_payload() if m.get_content_type() == 'message/rfc822']: - self((headers, message.get_payload()[0])) - - def save_mail(self, msg, subject, partners): - counter, description = 1, u'' - if msg.is_multipart(): - for part in msg.get_payload(): - stockdir = os.path.join('emails', msg['Message-Id'][1:-1]) - newdir = os.path.join('/tmp', stockdir) - filename = part.get_filename() - if not filename: - ext = mimetypes.guess_extension(part.get_type()) - if not ext: - ext = '.bin' - filename = 'part-%03d%s' % (counter, ext) - - if part.get_content_maintype() == 'multipart': - continue - elif part.get_content_maintype() == 'text': - if part.get_content_subtype() == 'plain': - description += part.get_payload(decode=1).decode(part.get_charsets()[0]) - description += u'\n\nVous trouverez les éventuels fichiers dans le répertoire: %s' % stockdir - continue - else: - description += u'\n\nCe message est en "%s", vous trouverez ce texte dans le répertoire: %s' % (part.get_content_type(), stockdir) - elif part.get_content_type() == 'message/rfc822': - continue - if not os.path.isdir(newdir): - os.mkdir(newdir) - - counter += 1 - fd = file(os.path.join(newdir, filename), 'w') - fd.write(part.get_payload(decode=1)) - fd.close() - else: - description = msg.get_payload(decode=1).decode(msg.get_charsets()[0]) - - project = self.project_re.search(subject) - if project: - project = project.groups()[0] - else: - project = '' - - for partner in partners: - self.rpc(('res.partner.event', 'create', {'name' : subject, 'partner_id' : partner, 'description' : description, 'project' : project})) + email_re = re.compile(r""" + ([a-zA-Z][\w\.-]*[a-zA-Z0-9] # username part + @ # mandatory @ sign + [a-zA-Z0-9][\w\.-]* # domain must start with a letter + \. + [a-z]{2,3} # TLD + ) + """, re.VERBOSE) + + project_re = re.compile(r"^ *\[?(\d{4}\.?\d{0,3})\]?", re.UNICODE) + + + def __init__(self, rpc): + self.rpc = rpc + + def get_addresses(self, headers, msg): + hcontent = '' + for header in [h for h in headers if msg.has_key(h)]: + hcontent += msg[header] + return self.email_re.findall(hcontent) + + def get_partners(self, headers, msg): + alladdresses = self.get_addresses(headers, msg) + address_ids = self.rpc(('res.partner.address', 'search', [('email', 'in', alladdresses)])) + addresses = self.rpc(('res.partner.address', 'read', address_ids)) + return [x['partner_id'][0] for x in addresses] + + def __call__(self, request): + headers, msg = request + partners = self.get_partners(headers, msg) + subject = u'' + for string, charset in decode_header(msg['Subject']): + if charset: + subject += string.decode(charset) + else: + subject += unicode(string) + if partners: + self.save_mail(msg, subject, partners) + else: + warning = MIMEText((warn_msg % (subject,)).encode('utf-8'), 'plain', 'utf-8') + warning['Subject'] = 'Message de TinyERP' + warning['From'] = 'erp@steel-sa.com' + warning['To'] = msg['From'] + s = smtplib.SMTP() + s.connect() + s.sendmail('erp@steel-sa.com', self.email_re.findall(msg['From']), warning.as_string()) + s.close() + + if msg.is_multipart(): + for message in [m for m in msg.get_payload() if m.get_content_type() == 'message/rfc822']: + self((headers, message.get_payload()[0])) + + def save_mail(self, msg, subject, partners): + counter, description = 1, u'' + if msg.is_multipart(): + for part in msg.get_payload(): + stockdir = os.path.join('emails', msg['Message-Id'][1:-1]) + newdir = os.path.join('/tmp', stockdir) + filename = part.get_filename() + if not filename: + ext = mimetypes.guess_extension(part.get_type()) + if not ext: + ext = '.bin' + filename = 'part-%03d%s' % (counter, ext) + + if part.get_content_maintype() == 'multipart': + continue + elif part.get_content_maintype() == 'text': + if part.get_content_subtype() == 'plain': + description += part.get_payload(decode=1).decode(part.get_charsets()[0]) + description += u'\n\nVous trouverez les éventuels fichiers dans le répertoire: %s' % stockdir + continue + else: + description += u'\n\nCe message est en "%s", vous trouverez ce texte dans le répertoire: %s' % (part.get_content_type(), stockdir) + elif part.get_content_type() == 'message/rfc822': + continue + if not os.path.isdir(newdir): + os.mkdir(newdir) + + counter += 1 + fd = file(os.path.join(newdir, filename), 'w') + fd.write(part.get_payload(decode=1)) + fd.close() + else: + description = msg.get_payload(decode=1).decode(msg.get_charsets()[0]) + + project = self.project_re.search(subject) + if project: + project = project.groups()[0] + else: + project = '' + + for partner in partners: + self.rpc(('res.partner.event', 'create', {'name' : subject, 'partner_id' : partner, 'description' : description, 'project' : project})) if __name__ == '__main__': - rpc_dispatcher = CommandDispatcher(RPCProxy(4, 'admin')) - dispatcher = CommandDispatcher(ReceiverEmail2Event(rpc_dispatcher)) - parser = EmailParser(['To', 'Cc', 'From'], dispatcher) - parser.parse(email.message_from_file(sys.stdin)) + rpc_dispatcher = CommandDispatcher(RPCProxy(4, 'admin')) + dispatcher = CommandDispatcher(ReceiverEmail2Event(rpc_dispatcher)) + parser = EmailParser(['To', 'Cc', 'From'], dispatcher) + parser.parse(email.message_from_file(sys.stdin)) diff --git a/bin/tools/misc.py b/bin/tools/misc.py index 4523c762c0a..457d22596d9 100644 --- a/bin/tools/misc.py +++ b/bin/tools/misc.py @@ -44,123 +44,123 @@ import release import socket if sys.version_info[:2] < (2, 4): - from threadinglocal import local + from threadinglocal import local else: - from threading import local + from threading import local # initialize a database with base/base.sql def init_db(cr): - import addons - f = addons.get_module_resource('base', 'base.sql') - for line in file(f).read().split(';'): - if (len(line)>0) and (not line.isspace()): - cr.execute(line) - cr.commit() - - for i in addons.get_modules(): - terp_file = addons.get_module_resource(i, '__terp__.py') - mod_path = addons.get_module_path(i) - info = False - if os.path.isfile(terp_file) and not os.path.isfile(mod_path+'.zip'): - info = eval(file(terp_file).read()) - elif zipfile.is_zipfile(mod_path+'.zip'): - zfile = zipfile.ZipFile(mod_path+'.zip') - i = os.path.splitext(i)[0] - info = eval(zfile.read(os.path.join(i, '__terp__.py'))) - if info: - categs = info.get('category', 'Uncategorized').split('/') - p_id = None - while categs: - if p_id is not None: - cr.execute('select id \ - from ir_module_category \ - where name=%s and parent_id=%d', (categs[0], p_id)) - else: - cr.execute('select id \ - from ir_module_category \ - where name=%s and parent_id is NULL', (categs[0],)) - c_id = cr.fetchone() - if not c_id: - cr.execute('select nextval(\'ir_module_category_id_seq\')') - c_id = cr.fetchone()[0] - cr.execute('insert into ir_module_category \ - (id, name, parent_id) \ - values (%d, %s, %d)', (c_id, categs[0], p_id)) - else: - c_id = c_id[0] - p_id = c_id - categs = categs[1:] - - active = info.get('active', False) - installable = info.get('installable', True) - if installable: - if active: - state = 'to install' - else: - state = 'uninstalled' - else: - state = 'uninstallable' - cr.execute('select nextval(\'ir_module_module_id_seq\')') - id = cr.fetchone()[0] - cr.execute('insert into ir_module_module \ - (id, author, latest_version, website, name, shortdesc, description, \ - category_id, state) \ - values (%d, %s, %s, %s, %s, %s, %s, %d, %s)', ( - id, info.get('author', ''), - release.version.rsplit('.', 1)[0] + '.' + info.get('version', ''), - info.get('website', ''), i, info.get('name', False), - info.get('description', ''), p_id, state)) - dependencies = info.get('depends', []) - for d in dependencies: - cr.execute('insert into ir_module_module_dependency \ - (module_id,name) values (%s, %s)', (id, d)) - cr.commit() + import addons + f = addons.get_module_resource('base', 'base.sql') + for line in file(f).read().split(';'): + if (len(line)>0) and (not line.isspace()): + cr.execute(line) + cr.commit() + + for i in addons.get_modules(): + terp_file = addons.get_module_resource(i, '__terp__.py') + mod_path = addons.get_module_path(i) + info = False + if os.path.isfile(terp_file) and not os.path.isfile(mod_path+'.zip'): + info = eval(file(terp_file).read()) + elif zipfile.is_zipfile(mod_path+'.zip'): + zfile = zipfile.ZipFile(mod_path+'.zip') + i = os.path.splitext(i)[0] + info = eval(zfile.read(os.path.join(i, '__terp__.py'))) + if info: + categs = info.get('category', 'Uncategorized').split('/') + p_id = None + while categs: + if p_id is not None: + cr.execute('select id \ + from ir_module_category \ + where name=%s and parent_id=%d', (categs[0], p_id)) + else: + cr.execute('select id \ + from ir_module_category \ + where name=%s and parent_id is NULL', (categs[0],)) + c_id = cr.fetchone() + if not c_id: + cr.execute('select nextval(\'ir_module_category_id_seq\')') + c_id = cr.fetchone()[0] + cr.execute('insert into ir_module_category \ + (id, name, parent_id) \ + values (%d, %s, %d)', (c_id, categs[0], p_id)) + else: + c_id = c_id[0] + p_id = c_id + categs = categs[1:] + + active = info.get('active', False) + installable = info.get('installable', True) + if installable: + if active: + state = 'to install' + else: + state = 'uninstalled' + else: + state = 'uninstallable' + cr.execute('select nextval(\'ir_module_module_id_seq\')') + id = cr.fetchone()[0] + cr.execute('insert into ir_module_module \ + (id, author, latest_version, website, name, shortdesc, description, \ + category_id, state) \ + values (%d, %s, %s, %s, %s, %s, %s, %d, %s)', ( + id, info.get('author', ''), + release.version.rsplit('.', 1)[0] + '.' + info.get('version', ''), + info.get('website', ''), i, info.get('name', False), + info.get('description', ''), p_id, state)) + dependencies = info.get('depends', []) + for d in dependencies: + cr.execute('insert into ir_module_module_dependency \ + (module_id,name) values (%s, %s)', (id, d)) + cr.commit() def find_in_path(name): - if os.name == "nt": - sep = ';' - else: - sep = ':' - path = [dir for dir in os.environ['PATH'].split(sep) - if os.path.isdir(dir)] - for dir in path: - val = os.path.join(dir, name) - if os.path.isfile(val) or os.path.islink(val): - return val - return None + if os.name == "nt": + sep = ';' + else: + sep = ':' + path = [dir for dir in os.environ['PATH'].split(sep) + if os.path.isdir(dir)] + for dir in path: + val = os.path.join(dir, name) + if os.path.isfile(val) or os.path.islink(val): + return val + return None def find_pg_tool(name): - if config['pg_path'] and config['pg_path'] != 'None': - return os.path.join(config['pg_path'], name) - else: - return find_in_path(name) + if config['pg_path'] and config['pg_path'] != 'None': + return os.path.join(config['pg_path'], name) + else: + return find_in_path(name) def exec_pg_command(name, *args): - prog = find_pg_tool(name) - if not prog: - raise Exception('Couldn\'t find %s' % name) - args2 = (os.path.basename(prog),) + args - return os.spawnv(os.P_WAIT, prog, args2) + prog = find_pg_tool(name) + if not prog: + raise Exception('Couldn\'t find %s' % name) + args2 = (os.path.basename(prog),) + args + return os.spawnv(os.P_WAIT, prog, args2) def exec_pg_command_pipe(name, *args): - prog = find_pg_tool(name) - if not prog: - raise Exception('Couldn\'t find %s' % name) - if os.name == "nt": - cmd = '"' + prog + '" ' + ' '.join(args) - else: - cmd = prog + ' ' + ' '.join(args) - return os.popen2(cmd, 'b') + prog = find_pg_tool(name) + if not prog: + raise Exception('Couldn\'t find %s' % name) + if os.name == "nt": + cmd = '"' + prog + '" ' + ' '.join(args) + else: + cmd = prog + ' ' + ' '.join(args) + return os.popen2(cmd, 'b') def exec_command_pipe(name, *args): - prog = find_in_path(name) - if not prog: - raise Exception('Couldn\'t find %s' % name) - if os.name == "nt": - cmd = '"'+prog+'" '+' '.join(args) - else: - cmd = prog+' '+' '.join(args) - return os.popen2(cmd, 'b') + prog = find_in_path(name) + if not prog: + raise Exception('Couldn\'t find %s' % name) + if os.name == "nt": + cmd = '"'+prog+'" '+' '.join(args) + else: + cmd = prog+' '+' '.join(args) + return os.popen2(cmd, 'b') #---------------------------------------------------------- # File paths @@ -169,89 +169,89 @@ def exec_command_pipe(name, *args): #file_path_addons = os.path.join(file_path_root, 'addons') def file_open(name, mode="r", subdir='addons', pathinfo=False): - """Open a file from the Tiny ERP root, using a subdir folder. - - >>> file_open('hr/report/timesheer.xsl') - >>> file_open('addons/hr/report/timesheet.xsl') - >>> file_open('../../base/report/rml_template.xsl', subdir='addons/hr/report', pathinfo=True) - - @param name: name of the file - @param mode: file open mode - @param subdir: subdirectory - @param pathinfo: if True returns tupple (fileobject, filepath) - - @return: fileobject if pathinfo is False else (fileobject, filepath) - """ - - adp = os.path.normcase(os.path.abspath(config['addons_path'])) - rtp = os.path.normcase(os.path.abspath(config['root_path'])) - - if name.replace(os.path.sep, '/').startswith('addons/'): - subdir = 'addons' - name = name[7:] - - # First try to locate in addons_path - if subdir: - subdir2 = subdir - if subdir2.replace(os.path.sep, '/').startswith('addons/'): - subdir2 = subdir2[7:] - - subdir2 = (subdir2 != 'addons' or None) and subdir2 - - try: - if subdir2: - fn = os.path.join(adp, subdir2, name) - else: - fn = os.path.join(adp, name) - fn = os.path.normpath(fn) - fo = file_open(fn, mode=mode, subdir=None, pathinfo=pathinfo) - if pathinfo: - return fo, fn - return fo - except IOError, e: - pass - - if subdir: - name = os.path.join(rtp, subdir, name) - else: - name = os.path.join(rtp, name) - - name = os.path.normpath(name) - - # Check for a zipfile in the path - head = name - zipname = False - name2 = False - while True: - head, tail = os.path.split(head) - if not tail: - break - if zipname: - zipname = os.path.join(tail, zipname) - else: - zipname = tail - if zipfile.is_zipfile(head+'.zip'): - import StringIO - zfile = zipfile.ZipFile(head+'.zip') - try: - fo = StringIO.StringIO(zfile.read(os.path.join( - os.path.basename(head), zipname).replace( - os.sep, '/'))) - - if pathinfo: - return fo, name - return fo - except: - name2 = os.path.normpath(os.path.join(head + '.zip', zipname)) - pass - for i in (name2, name): - if i and os.path.isfile(i): - fo = file(i, mode) - if pathinfo: - return fo, i - return fo - - raise IOError, 'File not found : '+str(name) + """Open a file from the Tiny ERP root, using a subdir folder. + + >>> file_open('hr/report/timesheer.xsl') + >>> file_open('addons/hr/report/timesheet.xsl') + >>> file_open('../../base/report/rml_template.xsl', subdir='addons/hr/report', pathinfo=True) + + @param name: name of the file + @param mode: file open mode + @param subdir: subdirectory + @param pathinfo: if True returns tupple (fileobject, filepath) + + @return: fileobject if pathinfo is False else (fileobject, filepath) + """ + + adp = os.path.normcase(os.path.abspath(config['addons_path'])) + rtp = os.path.normcase(os.path.abspath(config['root_path'])) + + if name.replace(os.path.sep, '/').startswith('addons/'): + subdir = 'addons' + name = name[7:] + + # First try to locate in addons_path + if subdir: + subdir2 = subdir + if subdir2.replace(os.path.sep, '/').startswith('addons/'): + subdir2 = subdir2[7:] + + subdir2 = (subdir2 != 'addons' or None) and subdir2 + + try: + if subdir2: + fn = os.path.join(adp, subdir2, name) + else: + fn = os.path.join(adp, name) + fn = os.path.normpath(fn) + fo = file_open(fn, mode=mode, subdir=None, pathinfo=pathinfo) + if pathinfo: + return fo, fn + return fo + except IOError, e: + pass + + if subdir: + name = os.path.join(rtp, subdir, name) + else: + name = os.path.join(rtp, name) + + name = os.path.normpath(name) + + # Check for a zipfile in the path + head = name + zipname = False + name2 = False + while True: + head, tail = os.path.split(head) + if not tail: + break + if zipname: + zipname = os.path.join(tail, zipname) + else: + zipname = tail + if zipfile.is_zipfile(head+'.zip'): + import StringIO + zfile = zipfile.ZipFile(head+'.zip') + try: + fo = StringIO.StringIO(zfile.read(os.path.join( + os.path.basename(head), zipname).replace( + os.sep, '/'))) + + if pathinfo: + return fo, name + return fo + except: + name2 = os.path.normpath(os.path.join(head + '.zip', zipname)) + pass + for i in (name2, name): + if i and os.path.isfile(i): + fo = file(i, mode) + if pathinfo: + return fo, i + return fo + + raise IOError, 'File not found : '+str(name) #---------------------------------------------------------- @@ -294,244 +294,244 @@ def flatten(list): # Emails #---------------------------------------------------------- def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=None, on_error=False, reply_to=False, tinycrm=False): - """Send an email.""" - if not email_cc: - email_cc=[] - if not email_bcc: - email_bcc=[] - import smtplib - from email.MIMEText import MIMEText - from email.MIMEMultipart import MIMEMultipart - from email.Header import Header - from email.Utils import formatdate, COMMASPACE - - msg = MIMEText(body or '', _charset='utf-8') - msg['Subject'] = Header(subject.decode('utf8'), 'utf-8') - msg['From'] = email_from - del msg['Reply-To'] - if reply_to: - msg['Reply-To'] = msg['From']+', '+reply_to - msg['To'] = COMMASPACE.join(email_to) - if email_cc: - msg['Cc'] = COMMASPACE.join(email_cc) - if email_bcc: - msg['Bcc'] = COMMASPACE.join(email_bcc) - msg['Date'] = formatdate(localtime=True) - if tinycrm: - msg['Message-Id'] = '<'+str(time.time())+'-tinycrm-'+str(tinycrm)+'@'+socket.gethostname()+'>' - try: - s = smtplib.SMTP() - s.connect(config['smtp_server']) - if config['smtp_user'] or config['smtp_password']: - s.login(config['smtp_user'], config['smtp_password']) - s.sendmail(email_from, flatten([email_to, email_cc, email_bcc]), msg.as_string()) - s.quit() - except Exception, e: - import logging - logging.getLogger().error(str(e)) - return True + """Send an email.""" + if not email_cc: + email_cc=[] + if not email_bcc: + email_bcc=[] + import smtplib + from email.MIMEText import MIMEText + from email.MIMEMultipart import MIMEMultipart + from email.Header import Header + from email.Utils import formatdate, COMMASPACE + + msg = MIMEText(body or '', _charset='utf-8') + msg['Subject'] = Header(subject.decode('utf8'), 'utf-8') + msg['From'] = email_from + del msg['Reply-To'] + if reply_to: + msg['Reply-To'] = msg['From']+', '+reply_to + msg['To'] = COMMASPACE.join(email_to) + if email_cc: + msg['Cc'] = COMMASPACE.join(email_cc) + if email_bcc: + msg['Bcc'] = COMMASPACE.join(email_bcc) + msg['Date'] = formatdate(localtime=True) + if tinycrm: + msg['Message-Id'] = '<'+str(time.time())+'-tinycrm-'+str(tinycrm)+'@'+socket.gethostname()+'>' + try: + s = smtplib.SMTP() + s.connect(config['smtp_server']) + if config['smtp_user'] or config['smtp_password']: + s.login(config['smtp_user'], config['smtp_password']) + s.sendmail(email_from, flatten([email_to, email_cc, email_bcc]), msg.as_string()) + s.quit() + except Exception, e: + import logging + logging.getLogger().error(str(e)) + return True #---------------------------------------------------------- # Emails #---------------------------------------------------------- def email_send_attach(email_from, email_to, subject, body, email_cc=None, email_bcc=None, on_error=False, reply_to=False, attach=None, tinycrm=False): - """Send an email.""" - if not email_cc: - email_cc=[] - if not email_bcc: - email_bcc=[] - if not attach: - attach=[] - import smtplib - from email.MIMEText import MIMEText - from email.MIMEBase import MIMEBase - from email.MIMEMultipart import MIMEMultipart - from email.Header import Header - from email.Utils import formatdate, COMMASPACE - from email import Encoders - - msg = MIMEMultipart() - - msg['Subject'] = Header(subject.decode('utf8'), 'utf-8') - msg['From'] = email_from - del msg['Reply-To'] - if reply_to: - msg['Reply-To'] = reply_to - msg['To'] = COMMASPACE.join(email_to) - if email_cc: - msg['Cc'] = COMMASPACE.join(email_cc) - if email_bcc: - msg['Bcc'] = COMMASPACE.join(email_bcc) - if tinycrm: - msg['Message-Id'] = '<'+str(time.time())+'-tinycrm-'+str(tinycrm)+'@'+socket.gethostname()+'>' - msg['Date'] = formatdate(localtime=True) - msg.attach( MIMEText(body or '', _charset='utf-8') ) - for (fname,fcontent) in attach: - part = MIMEBase('application', "octet-stream") - part.set_payload( fcontent ) - Encoders.encode_base64(part) - part.add_header('Content-Disposition', 'attachment; filename="%s"' % (fname,)) - msg.attach(part) - try: - s = smtplib.SMTP() - s.connect(config['smtp_server']) - if config['smtp_user'] or config['smtp_password']: - s.login(config['smtp_user'], config['smtp_password']) - s.sendmail(email_from, flatten([email_to, email_cc, email_bcc]), msg.as_string()) - s.quit() - except Exception, e: - import logging - logging.getLogger().error(str(e)) - return True + """Send an email.""" + if not email_cc: + email_cc=[] + if not email_bcc: + email_bcc=[] + if not attach: + attach=[] + import smtplib + from email.MIMEText import MIMEText + from email.MIMEBase import MIMEBase + from email.MIMEMultipart import MIMEMultipart + from email.Header import Header + from email.Utils import formatdate, COMMASPACE + from email import Encoders + + msg = MIMEMultipart() + + msg['Subject'] = Header(subject.decode('utf8'), 'utf-8') + msg['From'] = email_from + del msg['Reply-To'] + if reply_to: + msg['Reply-To'] = reply_to + msg['To'] = COMMASPACE.join(email_to) + if email_cc: + msg['Cc'] = COMMASPACE.join(email_cc) + if email_bcc: + msg['Bcc'] = COMMASPACE.join(email_bcc) + if tinycrm: + msg['Message-Id'] = '<'+str(time.time())+'-tinycrm-'+str(tinycrm)+'@'+socket.gethostname()+'>' + msg['Date'] = formatdate(localtime=True) + msg.attach( MIMEText(body or '', _charset='utf-8') ) + for (fname,fcontent) in attach: + part = MIMEBase('application', "octet-stream") + part.set_payload( fcontent ) + Encoders.encode_base64(part) + part.add_header('Content-Disposition', 'attachment; filename="%s"' % (fname,)) + msg.attach(part) + try: + s = smtplib.SMTP() + s.connect(config['smtp_server']) + if config['smtp_user'] or config['smtp_password']: + s.login(config['smtp_user'], config['smtp_password']) + s.sendmail(email_from, flatten([email_to, email_cc, email_bcc]), msg.as_string()) + s.quit() + except Exception, e: + import logging + logging.getLogger().error(str(e)) + return True #---------------------------------------------------------- # SMS #---------------------------------------------------------- # text must be latin-1 encoded def sms_send(user, password, api_id, text, to): - import urllib - params = urllib.urlencode({'user': user, 'password': password, 'api_id': api_id, 'text': text, 'to':to}) - #f = urllib.urlopen("http://api.clickatell.com/http/sendmsg", params) - f = urllib.urlopen("http://196.7.150.220/http/sendmsg", params) - print f.read() - return True + import urllib + params = urllib.urlencode({'user': user, 'password': password, 'api_id': api_id, 'text': text, 'to':to}) + #f = urllib.urlopen("http://api.clickatell.com/http/sendmsg", params) + f = urllib.urlopen("http://196.7.150.220/http/sendmsg", params) + print f.read() + return True #--------------------------------------------------------- # Class that stores an updateable string (used in wizards) #--------------------------------------------------------- class UpdateableStr(local): - def __init__(self, string=''): - self.string = string - - def __str__(self): - return str(self.string) + def __init__(self, string=''): + self.string = string + + def __str__(self): + return str(self.string) - def __repr__(self): - return str(self.string) + def __repr__(self): + return str(self.string) - def __nonzero__(self): - return bool(self.string) + def __nonzero__(self): + return bool(self.string) class UpdateableDict(local): - '''Stores an updateable dict to use in wizards''' + '''Stores an updateable dict to use in wizards''' - def __init__(self, dict=None): - if dict is None: - dict = {} - self.dict = dict + def __init__(self, dict=None): + if dict is None: + dict = {} + self.dict = dict - def __str__(self): - return str(self.dict) + def __str__(self): + return str(self.dict) - def __repr__(self): - return str(self.dict) + def __repr__(self): + return str(self.dict) - def clear(self): - return self.dict.clear() + def clear(self): + return self.dict.clear() - def keys(self): - return self.dict.keys() + def keys(self): + return self.dict.keys() - def __setitem__(self, i, y): - self.dict.__setitem__(i, y) + def __setitem__(self, i, y): + self.dict.__setitem__(i, y) - def __getitem__(self, i): - return self.dict.__getitem__(i) + def __getitem__(self, i): + return self.dict.__getitem__(i) - def copy(self): - return self.dict.copy() + def copy(self): + return self.dict.copy() - def iteritems(self): - return self.dict.iteritems() + def iteritems(self): + return self.dict.iteritems() - def iterkeys(self): - return self.dict.iterkeys() + def iterkeys(self): + return self.dict.iterkeys() - def itervalues(self): - return self.dict.itervalues() + def itervalues(self): + return self.dict.itervalues() - def pop(self, k, d=None): - return self.dict.pop(k, d) + def pop(self, k, d=None): + return self.dict.pop(k, d) - def popitem(self): - return self.dict.popitem() + def popitem(self): + return self.dict.popitem() - def setdefault(self, k, d=None): - return self.dict.setdefault(k, d) + def setdefault(self, k, d=None): + return self.dict.setdefault(k, d) - def update(self, E, **F): - return self.dict.update(E, F) + def update(self, E, **F): + return self.dict.update(E, F) - def values(self): - return self.dict.values() + def values(self): + return self.dict.values() - def get(self, k, d=None): - return self.dict.get(k, d) + def get(self, k, d=None): + return self.dict.get(k, d) - def has_key(self, k): - return self.dict.has_key(k) + def has_key(self, k): + return self.dict.has_key(k) - def items(self): - return self.dict.items() + def items(self): + return self.dict.items() - def __cmp__(self, y): - return self.dict.__cmp__(y) + def __cmp__(self, y): + return self.dict.__cmp__(y) - def __contains__(self, k): - return self.dict.__contains__(k) + def __contains__(self, k): + return self.dict.__contains__(k) - def __delitem__(self, y): - return self.dict.__delitem__(y) + def __delitem__(self, y): + return self.dict.__delitem__(y) - def __eq__(self, y): - return self.dict.__eq__(y) + def __eq__(self, y): + return self.dict.__eq__(y) - def __ge__(self, y): - return self.dict.__ge__(y) + def __ge__(self, y): + return self.dict.__ge__(y) - def __getitem__(self, y): - return self.dict.__getitem__(y) + def __getitem__(self, y): + return self.dict.__getitem__(y) - def __gt__(self, y): - return self.dict.__gt__(y) + def __gt__(self, y): + return self.dict.__gt__(y) - def __hash__(self): - return self.dict.__hash__() + def __hash__(self): + return self.dict.__hash__() - def __iter__(self): - return self.dict.__iter__() + def __iter__(self): + return self.dict.__iter__() - def __le__(self, y): - return self.dict.__le__(y) + def __le__(self, y): + return self.dict.__le__(y) - def __len__(self): - return self.dict.__len__() + def __len__(self): + return self.dict.__len__() - def __lt__(self, y): - return self.dict.__lt__(y) + def __lt__(self, y): + return self.dict.__lt__(y) - def __ne__(self, y): - return self.dict.__ne__(y) + def __ne__(self, y): + return self.dict.__ne__(y) # Don't use ! Use res.currency.round() class currency(float): - def __init__(self, value, accuracy=2, rounding=None): - if rounding is None: - rounding=10**-accuracy - self.rounding=rounding - self.accuracy=accuracy + def __init__(self, value, accuracy=2, rounding=None): + if rounding is None: + rounding=10**-accuracy + self.rounding=rounding + self.accuracy=accuracy - def __new__(cls, value, accuracy=2, rounding=None): - return float.__new__(cls, round(value, accuracy)) + def __new__(cls, value, accuracy=2, rounding=None): + return float.__new__(cls, round(value, accuracy)) - #def __str__(self): - # display_value = int(self*(10**(-self.accuracy))/self.rounding)*self.rounding/(10**(-self.accuracy)) - # return str(display_value) + #def __str__(self): + # display_value = int(self*(10**(-self.accuracy))/self.rounding)*self.rounding/(10**(-self.accuracy)) + # return str(display_value) # @@ -539,99 +539,99 @@ class currency(float): # Timeout: 0 = no timeout, otherwise in seconds # class cache(object): - def __init__(self, timeout=10000, skiparg=2): - self.timeout = timeout - self.cache = {} - - def __call__(self, fn): - arg_names = inspect.getargspec(fn)[0][2:] - def cached_result(self2, cr=None, *args, **kwargs): - if cr is None: - self.cache = {} - return True - - # Update named arguments with positional argument values - kwargs.update(dict(zip(arg_names, args))) - kwargs = kwargs.items() - kwargs.sort() - - # Work out key as a tuple of ('argname', value) pairs - key = (('dbname', cr.dbname),) + tuple(kwargs) - - # Check cache and return cached value if possible - if key in self.cache: - (value, last_time) = self.cache[key] - mintime = time.time() - self.timeout - if self.timeout <= 0 or mintime <= last_time: - return value - - # Work out new value, cache it and return it - # Should copy() this value to avoid futur modf of the cacle ? - result = fn(self2,cr,**dict(kwargs)) - - self.cache[key] = (result, time.time()) - return result - return cached_result + def __init__(self, timeout=10000, skiparg=2): + self.timeout = timeout + self.cache = {} + + def __call__(self, fn): + arg_names = inspect.getargspec(fn)[0][2:] + def cached_result(self2, cr=None, *args, **kwargs): + if cr is None: + self.cache = {} + return True + + # Update named arguments with positional argument values + kwargs.update(dict(zip(arg_names, args))) + kwargs = kwargs.items() + kwargs.sort() + + # Work out key as a tuple of ('argname', value) pairs + key = (('dbname', cr.dbname),) + tuple(kwargs) + + # Check cache and return cached value if possible + if key in self.cache: + (value, last_time) = self.cache[key] + mintime = time.time() - self.timeout + if self.timeout <= 0 or mintime <= last_time: + return value + + # Work out new value, cache it and return it + # Should copy() this value to avoid futur modf of the cacle ? + result = fn(self2,cr,**dict(kwargs)) + + self.cache[key] = (result, time.time()) + return result + return cached_result def to_xml(s): - return s.replace('&','&').replace('<','<').replace('>','>') + return s.replace('&','&').replace('<','<').replace('>','>') def get_languages(): - languages={ - 'zh_CN': 'Chinese (CN)', - 'zh_TW': 'Chinese (TW)', - 'cs_CZ': 'Czech', - 'de_DE': 'Deutsch', - 'es_AR': 'Español (Argentina)', - 'es_ES': 'Español (España)', - 'fr_FR': 'Français', - 'fr_CH': 'Français (Suisse)', - 'en_EN': 'English (default)', - 'hu_HU': 'Hungarian', - 'it_IT': 'Italiano', - 'pt_BR': 'Portugese (Brasil)', - 'pt_PT': 'Portugese (Portugal)', - 'nl_NL': 'Nederlands', - 'ro_RO': 'Romanian', - 'ru_RU': 'Russian', - 'sv_SE': 'Swedish', - } - return languages + languages={ + 'zh_CN': 'Chinese (CN)', + 'zh_TW': 'Chinese (TW)', + 'cs_CZ': 'Czech', + 'de_DE': 'Deutsch', + 'es_AR': 'Español (Argentina)', + 'es_ES': 'Español (España)', + 'fr_FR': 'Français', + 'fr_CH': 'Français (Suisse)', + 'en_EN': 'English (default)', + 'hu_HU': 'Hungarian', + 'it_IT': 'Italiano', + 'pt_BR': 'Portugese (Brasil)', + 'pt_PT': 'Portugese (Portugal)', + 'nl_NL': 'Nederlands', + 'ro_RO': 'Romanian', + 'ru_RU': 'Russian', + 'sv_SE': 'Swedish', + } + return languages def scan_languages(): - import glob - file_list = [os.path.splitext(os.path.basename(f))[0] for f in glob.glob(os.path.join(config['root_path'], 'i18n', '*.csv'))] - lang_dict = get_languages() - return [(lang, lang_dict.get(lang, lang)) for lang in file_list] + import glob + file_list = [os.path.splitext(os.path.basename(f))[0] for f in glob.glob(os.path.join(config['root_path'], 'i18n', '*.csv'))] + lang_dict = get_languages() + return [(lang, lang_dict.get(lang, lang)) for lang in file_list] def get_user_companies(cr, user): - def _get_company_children(cr, ids): - if not ids: - return [] - cr.execute('SELECT id FROM res_company WHERE parent_id = any(array[%s])' %(','.join([str(x) for x in ids]),)) - res=[x[0] for x in cr.fetchall()] - res.extend(_get_company_children(cr, res)) - return res - cr.execute('SELECT comp.id FROM res_company AS comp, res_users AS u WHERE u.id = %d AND comp.id = u.company_id' % (user,)) - compids=[cr.fetchone()[0]] - compids.extend(_get_company_children(cr, compids)) - return compids + def _get_company_children(cr, ids): + if not ids: + return [] + cr.execute('SELECT id FROM res_company WHERE parent_id = any(array[%s])' %(','.join([str(x) for x in ids]),)) + res=[x[0] for x in cr.fetchall()] + res.extend(_get_company_children(cr, res)) + return res + cr.execute('SELECT comp.id FROM res_company AS comp, res_users AS u WHERE u.id = %d AND comp.id = u.company_id' % (user,)) + compids=[cr.fetchone()[0]] + compids.extend(_get_company_children(cr, compids)) + return compids def mod10r(number): - """ - Input number : account or invoice number - Output return: the same number completed with the recursive mod10 - key - """ - codec=[0,9,4,6,8,2,7,1,3,5] - report = 0 - result="" - for digit in number: - result += digit - if digit.isdigit(): - report = codec[ (int(digit) + report) % 10 ] - return result + str((10 - report) % 10) + """ + Input number : account or invoice number + Output return: the same number completed with the recursive mod10 + key + """ + codec=[0,9,4,6,8,2,7,1,3,5] + report = 0 + result="" + for digit in number: + result += digit + if digit.isdigit(): + report = codec[ (int(digit) + report) % 10 ] + return result + str((10 - report) % 10) diff --git a/bin/tools/translate.py b/bin/tools/translate.py index 123d673d238..8b9409dad1a 100644 --- a/bin/tools/translate.py +++ b/bin/tools/translate.py @@ -38,7 +38,7 @@ import inspect import mx.DateTime as mxdt class UNIX_LINE_TERMINATOR(csv.excel): - lineterminator = '\n' + lineterminator = '\n' csv.register_dialect("UNIX", UNIX_LINE_TERMINATOR) @@ -46,15 +46,15 @@ csv.register_dialect("UNIX", UNIX_LINE_TERMINATOR) # TODO: a caching method # def translate(cr, name, source_type, lang, source=None): - if source and name: - cr.execute('select value from ir_translation where lang=%s and type=%s and name=%s and src=%s', (lang, source_type, str(name), source)) - elif name: - cr.execute('select value from ir_translation where lang=%s and type=%s and name=%s', (lang, source_type, str(name))) - elif source: - cr.execute('select value from ir_translation where lang=%s and type=%s and src=%s', (lang, source_type, source)) - res_trans = cr.fetchone() - res = res_trans and res_trans[0] or False - return res + if source and name: + cr.execute('select value from ir_translation where lang=%s and type=%s and name=%s and src=%s', (lang, source_type, str(name), source)) + elif name: + cr.execute('select value from ir_translation where lang=%s and type=%s and name=%s', (lang, source_type, str(name))) + elif source: + cr.execute('select value from ir_translation where lang=%s and type=%s and src=%s', (lang, source_type, source)) + res_trans = cr.fetchone() + res = res_trans and res_trans[0] or False + return res class GettextAlias(object): def __call__(self, source): @@ -71,469 +71,469 @@ _ = GettextAlias() # class to handle po files class TinyPoFile(object): - def __init__(self, buffer): - self.buffer = buffer - - def __iter__(self): - self.buffer.seek(0) - self.lines = self.buffer.readlines() - self.first = True - return self - - def next(self): - def unquote(str): - return str[1:-1].replace("\\n", "\n") \ - .replace('\\"', "\"") - - type = name = res_id = source = trad = None - - line = None - while not line: - if 0 == len(self.lines): - raise StopIteration() - line = self.lines.pop(0).strip() - - while line.startswith('#'): - if line.startswith('#:'): - type, name, res_id = line[2:].strip().split(':') - line = self.lines.pop(0).strip() - if not line.startswith('msgid'): - raise Exception("malformed file") - source = line[7:-1] - line = self.lines.pop(0).strip() - if not source and self.first: - # if the source is "" and it's the first msgid, it's the special - # msgstr with the informations about the traduction and the - # traductor; we skip it - while line: - line = self.lines.pop(0).strip() - return next() - - while not line.startswith('msgstr'): - if not line: - raise Exception('malformed file') - source += unquote(line) - line = self.lines.pop(0).strip() - - trad = line[8:-1] - line = self.lines.pop(0).strip() - while line: - trad += unquote(line) - line = self.lines.pop(0).strip() - - self.first = False - return type, name, res_id, source, trad - - def write_infos(self, modules): - import release - self.buffer.write("# Translation of %(project)s.\n" \ - "# This file containt the translation of the following modules:\n" \ - "%(modules)s" \ - "#\n" \ - "msgid \"\"\n" \ - "msgstr \"\"\n" \ - "\"Project-Id-Version: %(project)s %(version)s\"\n" \ - "\"Report-Msgid-Bugs-To: %(bugmail)s\"\n" \ - "\"POT-Creation-Date: %(now)s\"\n" \ - "\"PO-Revision-Date: %(now)s\"\n" \ - "\"Last-Translator: <>\"\n" \ - "\"Language-Team: \"\n" \ - "\"MIME-Version: 1.0\"\n" \ - "\"Content-Type: text/plain; charset=UTF-8\"\n" \ - "\"Content-Transfer-Encoding: \"\n" \ - "\"Plural-Forms: \"\n" \ - "\n" - - % { 'project': release.description, - 'version': release.version, - 'modules': reduce(lambda s, m: s + "#\t* %s\n" % m, modules, ""), - 'bugmail': 'support@tinyerp.com', #TODO: use variable from release - 'now': mxdt.ISO.strUTC(mxdt.ISO.DateTime.utc()), - } - ) - - def write(self, module, type, name, res_id, source, trad): - def quote(str): - return '"%s"' % str.replace('"','\\"') \ - .replace('\n', '\\n"\n"') - - self.buffer.write("#. module: %s\n" \ - "#, python-format\n" \ - "#: %s:%s:%s\n" \ - "msgid %s\n" \ - "msgstr %s\n\n" \ - % (module, type, name, str(res_id), quote(source), quote(trad)) - ) - + def __init__(self, buffer): + self.buffer = buffer + + def __iter__(self): + self.buffer.seek(0) + self.lines = self.buffer.readlines() + self.first = True + return self + + def next(self): + def unquote(str): + return str[1:-1].replace("\\n", "\n") \ + .replace('\\"', "\"") + + type = name = res_id = source = trad = None + + line = None + while not line: + if 0 == len(self.lines): + raise StopIteration() + line = self.lines.pop(0).strip() + + while line.startswith('#'): + if line.startswith('#:'): + type, name, res_id = line[2:].strip().split(':') + line = self.lines.pop(0).strip() + if not line.startswith('msgid'): + raise Exception("malformed file") + source = line[7:-1] + line = self.lines.pop(0).strip() + if not source and self.first: + # if the source is "" and it's the first msgid, it's the special + # msgstr with the informations about the traduction and the + # traductor; we skip it + while line: + line = self.lines.pop(0).strip() + return next() + + while not line.startswith('msgstr'): + if not line: + raise Exception('malformed file') + source += unquote(line) + line = self.lines.pop(0).strip() + + trad = line[8:-1] + line = self.lines.pop(0).strip() + while line: + trad += unquote(line) + line = self.lines.pop(0).strip() + + self.first = False + return type, name, res_id, source, trad + + def write_infos(self, modules): + import release + self.buffer.write("# Translation of %(project)s.\n" \ + "# This file containt the translation of the following modules:\n" \ + "%(modules)s" \ + "#\n" \ + "msgid \"\"\n" \ + "msgstr \"\"\n" \ + "\"Project-Id-Version: %(project)s %(version)s\"\n" \ + "\"Report-Msgid-Bugs-To: %(bugmail)s\"\n" \ + "\"POT-Creation-Date: %(now)s\"\n" \ + "\"PO-Revision-Date: %(now)s\"\n" \ + "\"Last-Translator: <>\"\n" \ + "\"Language-Team: \"\n" \ + "\"MIME-Version: 1.0\"\n" \ + "\"Content-Type: text/plain; charset=UTF-8\"\n" \ + "\"Content-Transfer-Encoding: \"\n" \ + "\"Plural-Forms: \"\n" \ + "\n" + + % { 'project': release.description, + 'version': release.version, + 'modules': reduce(lambda s, m: s + "#\t* %s\n" % m, modules, ""), + 'bugmail': 'support@tinyerp.com', #TODO: use variable from release + 'now': mxdt.ISO.strUTC(mxdt.ISO.DateTime.utc()), + } + ) + + def write(self, module, type, name, res_id, source, trad): + def quote(str): + return '"%s"' % str.replace('"','\\"') \ + .replace('\n', '\\n"\n"') + + self.buffer.write("#. module: %s\n" \ + "#, python-format\n" \ + "#: %s:%s:%s\n" \ + "msgid %s\n" \ + "msgstr %s\n\n" \ + % (module, type, name, str(res_id), quote(source), quote(trad)) + ) + # Methods to export the translation file def trans_export(lang, modules, buffer, format, dbname=None): - trans = trans_generate(lang, modules, dbname) - if format == 'csv': - writer=csv.writer(buffer, 'UNIX') - for row in trans: - writer.writerow(row) - elif format == 'po': - trans.pop(0) - writer = tools.TinyPoFile(buffer) - writer.write_infos(modules) - for module, type, name, res_id, src, trad in trans: - writer.write(module, type, name, res_id, src, trad) - else: - raise Exception(_('Bad file format')) - del trans - + trans = trans_generate(lang, modules, dbname) + if format == 'csv': + writer=csv.writer(buffer, 'UNIX') + for row in trans: + writer.writerow(row) + elif format == 'po': + trans.pop(0) + writer = tools.TinyPoFile(buffer) + writer.write_infos(modules) + for module, type, name, res_id, src, trad in trans: + writer.write(module, type, name, res_id, src, trad) + else: + raise Exception(_('Bad file format')) + del trans + def trans_parse_xsl(de): - res = [] - for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]: - if n.hasAttribute("t"): - for m in [j for j in n.childNodes if (j.nodeType == j.TEXT_NODE)]: - l = m.data.strip().replace('\n',' ') - if len(l): - res.append(l.encode("utf8")) - res.extend(trans_parse_xsl(n)) - return res + res = [] + for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]: + if n.hasAttribute("t"): + for m in [j for j in n.childNodes if (j.nodeType == j.TEXT_NODE)]: + l = m.data.strip().replace('\n',' ') + if len(l): + res.append(l.encode("utf8")) + res.extend(trans_parse_xsl(n)) + return res def trans_parse_rml(de): - res = [] - for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]: - for m in [j for j in n.childNodes if (j.nodeType == j.TEXT_NODE)]: - string_list = [s.replace('\n', ' ').strip() for s in re.split('\[\[.+?\]\]', m.data)] - for s in string_list: - if s: - res.append(s.encode("utf8")) - res.extend(trans_parse_rml(n)) - return res + res = [] + for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]: + for m in [j for j in n.childNodes if (j.nodeType == j.TEXT_NODE)]: + string_list = [s.replace('\n', ' ').strip() for s in re.split('\[\[.+?\]\]', m.data)] + for s in string_list: + if s: + res.append(s.encode("utf8")) + res.extend(trans_parse_rml(n)) + return res def trans_parse_view(de): - res = [] - if de.hasAttribute("string"): - s = de.getAttribute('string') - if s: - res.append(s.encode("utf8")) - if de.hasAttribute("sum"): - s = de.getAttribute('sum') - if s: - res.append(s.encode("utf8")) - for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]: - res.extend(trans_parse_view(n)) - return res + res = [] + if de.hasAttribute("string"): + s = de.getAttribute('string') + if s: + res.append(s.encode("utf8")) + if de.hasAttribute("sum"): + s = de.getAttribute('sum') + if s: + res.append(s.encode("utf8")) + for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE)]: + res.extend(trans_parse_view(n)) + return res # tests whether an object is in a list of modules def in_modules(object_name, modules): - if 'all' in modules: - return True - - module_dict = { - 'ir': 'base', - 'res': 'base', - 'workflow': 'base', - } - module = object_name.split('.')[0] - module = module_dict.get(module, module) - return module in modules + if 'all' in modules: + return True + + module_dict = { + 'ir': 'base', + 'res': 'base', + 'workflow': 'base', + } + module = object_name.split('.')[0] + module = module_dict.get(module, module) + return module in modules def trans_generate(lang, modules, dbname=None): - logger = netsvc.Logger() - if not dbname: - dbname=tools.config['db_name'] - pool = pooler.get_pool(dbname) - trans_obj = pool.get('ir.translation') - model_data_obj = pool.get('ir.model.data') - cr = pooler.get_db(dbname).cursor() - uid = 1 - l = pool.obj_pool.items() - l.sort() - - query = 'SELECT name, model, res_id, module' \ - ' FROM ir_model_data' - if not 'all' in modules: - query += ' WHERE module IN (%s)' % ','.join(['%s']*len(modules)) - query += ' ORDER BY module, model, name' - - query_param = not 'all' in modules and modules or None - cr.execute(query, query_param) - - #if 'all' in modules: - # cr.execute('select name,model,res_id,module from ir_model_data') - #else: - # cr.execute('select name,model,res_id,module from ir_model_data where module in ('+','.join(['%s']*len(modules))+')', modules) - - - _to_translate = [] - def push_translation(module, type, name, id, source): - tuple = (module, type, name, id, source) - if not tuple in _to_translate: - _to_translate.append(tuple) - - - for (xml_name,model,res_id,module) in cr.fetchall(): - xml_name = module+'.'+xml_name - obj = pool.get(model).browse(cr, uid, res_id) - if model=='ir.ui.view': - d = xml.dom.minidom.parseString(obj.arch) - for t in trans_parse_view(d.documentElement): - push_translation(module, 'view', obj.model, 0, t) - elif model=='ir.actions.wizard': - service_name = 'wizard.'+obj.wiz_name - obj2 = netsvc._service[service_name] - for state_name, state_def in obj2.states.iteritems(): - if 'result' in state_def: - result = state_def['result'] - if result['type'] != 'form': - continue - name = obj.wiz_name + ',' + state_name - - # export fields - for field_name, field_def in result['fields'].iteritems(): - if 'string' in field_def: - source = field_def['string'] - res_name = name + ',' + field_name - push_translation(module, 'wizard_field', res_name, 0, source) - - # export arch - arch = result['arch'] - if not isinstance(arch, UpdateableStr): - d = xml.dom.minidom.parseString(arch) - for t in trans_parse_view(d.documentElement): - push_translation(module, 'wizard_view', name, 0, t) - - # export button labels - for but_args in result['state']: - button_name = but_args[0] - button_label = but_args[1] - res_name = name + ',' + button_name - push_translation(module, 'wizard_button', res_name, 0, button_label) - - elif model=='ir.model.fields': - field_name = obj.name - field_def = pool.get(obj.model)._columns[field_name] - - name = obj.model + "," + field_name - push_translation(module, 'field', name, 0, field_def.string.encode('utf8')) - - if field_def.help: - push_translation(module, 'help', name, 0, field_def.help.encode('utf8')) - - if field_def.translate: - ids = pool.get(obj.model).search(cr, uid, []) - obj_values = pool.get(obj.model).read(cr, uid, ids, [field_name]) - for obj_value in obj_values: - res_id = obj_value['id'] - if obj.name in ('ir.model', 'ir.ui.menu'): - res_id = 0 - model_data_ids = model_data_obj.search(cr, uid, [ - ('model', '=', model), - ('res_id', '=', res_id), - ]) - if not model_data_ids: - push_translation(module, 'model', name, 0, obj_value[field_name]) - - if hasattr(field_def, 'selection') and isinstance(field_def.selection, (list, tuple)): - for key, val in field_def.selection: - push_translation(module, 'selection', name, 0, val.encode('utf8')) - - elif model=='ir.actions.report.xml': - name = obj.report_name - fname = "" - if obj.report_rml: - fname = obj.report_rml - parse_func = trans_parse_rml - report_type = "rml" - elif obj.report_xsl: - fname = obj.report_xsl - parse_func = trans_parse_xsl - report_type = "xsl" - try: - xmlstr = tools.file_open(fname).read() - d = xml.dom.minidom.parseString(xmlstr) - for t in parse_func(d.documentElement): - push_translation(module, report_type, name, 0, t) - except IOError: - if fname: - logger.notifyChannel("init", netsvc.LOG_WARNING, "couldn't export translation for report %s %s %s" % (name, report_type, fname)) - - for constraint in pool.get(model)._constraints: - msg = constraint[1] - push_translation(module, 'constraint', model, 0, msg.encode('utf8')) - - for field_name,field_def in pool.get(model)._columns.items(): - if field_def.translate: - name = model + "," + field_name - trad = getattr(obj, field_name) or '' - push_translation(module, 'model', name, xml_name, trad) - - # parse source code for _() calls - def get_module_from_path(path): - relative_addons_path = tools.config['addons_path'][len(tools.config['root_path'])+1:] - if path.startswith(relative_addons_path): - path = path[len(relative_addons_path)+1:] - return path.split(os.path.sep)[0] - return 'base' # files that are not in a module are considered as being in 'base' module - - modobj = pool.get('ir.module.module') - for root, dirs, files in os.walk(tools.config['root_path']): - for fname in fnmatch.filter(files, '*.py'): - fabsolutepath = join(root, fname) - frelativepath = fabsolutepath[len(tools.config['root_path'])+1:] - module = get_module_from_path(frelativepath) - is_mod_installed = modobj.search(cr, uid, [('state', '=', 'installed'), ('name', '=', module)]) <> [] - if (('all' in modules) or (module in modules)) and is_mod_installed: - code_string = tools.file_open(fabsolutepath, subdir='').read() - iter = re.finditer( - '[^a-zA-Z0-9_]_\([\s]*["\'](.+?)["\'][\s]*\)', - code_string) - for i in iter: - push_translation(module, 'code', frelativepath, 0, i.group(1).encode('utf8')) - - - out = [["module","type","name","res_id","src","value"]] # header - # translate strings marked as to be translated - for module, type, name, id, source in _to_translate: - trans = trans_obj._get_source(cr, uid, name, type, lang, source) - out.append([module, type, name, id, source, trans or '']) - - cr.close() - return out + logger = netsvc.Logger() + if not dbname: + dbname=tools.config['db_name'] + pool = pooler.get_pool(dbname) + trans_obj = pool.get('ir.translation') + model_data_obj = pool.get('ir.model.data') + cr = pooler.get_db(dbname).cursor() + uid = 1 + l = pool.obj_pool.items() + l.sort() + + query = 'SELECT name, model, res_id, module' \ + ' FROM ir_model_data' + if not 'all' in modules: + query += ' WHERE module IN (%s)' % ','.join(['%s']*len(modules)) + query += ' ORDER BY module, model, name' + + query_param = not 'all' in modules and modules or None + cr.execute(query, query_param) + + #if 'all' in modules: + # cr.execute('select name,model,res_id,module from ir_model_data') + #else: + # cr.execute('select name,model,res_id,module from ir_model_data where module in ('+','.join(['%s']*len(modules))+')', modules) + + + _to_translate = [] + def push_translation(module, type, name, id, source): + tuple = (module, type, name, id, source) + if not tuple in _to_translate: + _to_translate.append(tuple) + + + for (xml_name,model,res_id,module) in cr.fetchall(): + xml_name = module+'.'+xml_name + obj = pool.get(model).browse(cr, uid, res_id) + if model=='ir.ui.view': + d = xml.dom.minidom.parseString(obj.arch) + for t in trans_parse_view(d.documentElement): + push_translation(module, 'view', obj.model, 0, t) + elif model=='ir.actions.wizard': + service_name = 'wizard.'+obj.wiz_name + obj2 = netsvc._service[service_name] + for state_name, state_def in obj2.states.iteritems(): + if 'result' in state_def: + result = state_def['result'] + if result['type'] != 'form': + continue + name = obj.wiz_name + ',' + state_name + + # export fields + for field_name, field_def in result['fields'].iteritems(): + if 'string' in field_def: + source = field_def['string'] + res_name = name + ',' + field_name + push_translation(module, 'wizard_field', res_name, 0, source) + + # export arch + arch = result['arch'] + if not isinstance(arch, UpdateableStr): + d = xml.dom.minidom.parseString(arch) + for t in trans_parse_view(d.documentElement): + push_translation(module, 'wizard_view', name, 0, t) + + # export button labels + for but_args in result['state']: + button_name = but_args[0] + button_label = but_args[1] + res_name = name + ',' + button_name + push_translation(module, 'wizard_button', res_name, 0, button_label) + + elif model=='ir.model.fields': + field_name = obj.name + field_def = pool.get(obj.model)._columns[field_name] + + name = obj.model + "," + field_name + push_translation(module, 'field', name, 0, field_def.string.encode('utf8')) + + if field_def.help: + push_translation(module, 'help', name, 0, field_def.help.encode('utf8')) + + if field_def.translate: + ids = pool.get(obj.model).search(cr, uid, []) + obj_values = pool.get(obj.model).read(cr, uid, ids, [field_name]) + for obj_value in obj_values: + res_id = obj_value['id'] + if obj.name in ('ir.model', 'ir.ui.menu'): + res_id = 0 + model_data_ids = model_data_obj.search(cr, uid, [ + ('model', '=', model), + ('res_id', '=', res_id), + ]) + if not model_data_ids: + push_translation(module, 'model', name, 0, obj_value[field_name]) + + if hasattr(field_def, 'selection') and isinstance(field_def.selection, (list, tuple)): + for key, val in field_def.selection: + push_translation(module, 'selection', name, 0, val.encode('utf8')) + + elif model=='ir.actions.report.xml': + name = obj.report_name + fname = "" + if obj.report_rml: + fname = obj.report_rml + parse_func = trans_parse_rml + report_type = "rml" + elif obj.report_xsl: + fname = obj.report_xsl + parse_func = trans_parse_xsl + report_type = "xsl" + try: + xmlstr = tools.file_open(fname).read() + d = xml.dom.minidom.parseString(xmlstr) + for t in parse_func(d.documentElement): + push_translation(module, report_type, name, 0, t) + except IOError: + if fname: + logger.notifyChannel("init", netsvc.LOG_WARNING, "couldn't export translation for report %s %s %s" % (name, report_type, fname)) + + for constraint in pool.get(model)._constraints: + msg = constraint[1] + push_translation(module, 'constraint', model, 0, msg.encode('utf8')) + + for field_name,field_def in pool.get(model)._columns.items(): + if field_def.translate: + name = model + "," + field_name + trad = getattr(obj, field_name) or '' + push_translation(module, 'model', name, xml_name, trad) + + # parse source code for _() calls + def get_module_from_path(path): + relative_addons_path = tools.config['addons_path'][len(tools.config['root_path'])+1:] + if path.startswith(relative_addons_path): + path = path[len(relative_addons_path)+1:] + return path.split(os.path.sep)[0] + return 'base' # files that are not in a module are considered as being in 'base' module + + modobj = pool.get('ir.module.module') + for root, dirs, files in os.walk(tools.config['root_path']): + for fname in fnmatch.filter(files, '*.py'): + fabsolutepath = join(root, fname) + frelativepath = fabsolutepath[len(tools.config['root_path'])+1:] + module = get_module_from_path(frelativepath) + is_mod_installed = modobj.search(cr, uid, [('state', '=', 'installed'), ('name', '=', module)]) <> [] + if (('all' in modules) or (module in modules)) and is_mod_installed: + code_string = tools.file_open(fabsolutepath, subdir='').read() + iter = re.finditer( + '[^a-zA-Z0-9_]_\([\s]*["\'](.+?)["\'][\s]*\)', + code_string) + for i in iter: + push_translation(module, 'code', frelativepath, 0, i.group(1).encode('utf8')) + + + out = [["module","type","name","res_id","src","value"]] # header + # translate strings marked as to be translated + for module, type, name, id, source in _to_translate: + trans = trans_obj._get_source(cr, uid, name, type, lang, source) + out.append([module, type, name, id, source, trans or '']) + + cr.close() + return out def trans_load(db_name, filename, lang, strict=False): - logger = netsvc.Logger() - try: - fileobj = open(filename,'r') - fileformat = os.path.splitext(filename)[-1][1:].lower() - r = trans_load_data(db_name, fileobj, fileformat, lang, strict=False) - fileobj.close() - return r - except IOError: - logger.notifyChannel("init", netsvc.LOG_ERROR, "couldn't read file") - return None + logger = netsvc.Logger() + try: + fileobj = open(filename,'r') + fileformat = os.path.splitext(filename)[-1][1:].lower() + r = trans_load_data(db_name, fileobj, fileformat, lang, strict=False) + fileobj.close() + return r + except IOError: + logger.notifyChannel("init", netsvc.LOG_ERROR, "couldn't read file") + return None def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name=None): - logger = netsvc.Logger() - logger.notifyChannel("init", netsvc.LOG_INFO, - 'loading translation file for language %s' % (lang)) - pool = pooler.get_pool(db_name) - lang_obj = pool.get('res.lang') - trans_obj = pool.get('ir.translation') - model_data_obj = pool.get('ir.model.data') - try: - uid = 1 - cr = pooler.get_db(db_name).cursor() - - ids = lang_obj.search(cr, uid, [('code','=',lang)]) - if not ids: - if not lang_name: - lang_name=lang - languages=tools.get_languages() - if lang in languages: - lang_name=languages[lang] - ids = lang_obj.create(cr, uid, { - 'code': lang, - 'name': lang_name, - 'translatable': 1, - }) - else: - lang_obj.write(cr, uid, ids, {'translatable':1}) - lang_ids = lang_obj.search(cr, uid, []) - langs = lang_obj.read(cr, uid, lang_ids) - ls = map(lambda x: (x['code'],x['name']), langs) - - fileobj.seek(0) - - if fileformat == 'csv': - reader = csv.reader(fileobj, quotechar='"', delimiter=',') - # read the first line of the file (it contains columns titles) - for row in reader: - f = row - break - elif fileformat == 'po': - reader = TinyPoFile(fileobj) - f = ['type', 'name', 'res_id', 'src', 'value'] - else: - raise Exception(_('Bad file format')) - - # read the rest of the file - line = 1 - for row in reader: - line += 1 - #try: - # skip empty rows and rows where the translation field (=last fiefd) is empty - if (not row) or (not row[-1]): - #print "translate: skip %s" % repr(row) - continue - - # dictionary which holds values for this line of the csv file - # {'lang': ..., 'type': ..., 'name': ..., 'res_id': ..., - # 'src': ..., 'value': ...} - dic = {'lang': lang} - for i in range(len(f)): - if f[i] in ('module',): - continue - dic[f[i]] = row[i] - - try: - dic['res_id'] = int(dic['res_id']) - except: - model_data_ids = model_data_obj.search(cr, uid, [ - ('model', '=', dic['name'].split(',')[0]), - ('module', '=', dic['res_id'].split('.', 1)[0]), - ('name', '=', dic['res_id'].split('.', 1)[1]), - ]) - if model_data_ids: - dic['res_id'] = model_data_obj.browse(cr, uid, - model_data_ids[0]).res_id - else: - dic['res_id'] = False - - if dic['type'] == 'model' and not strict: - (model, field) = dic['name'].split(',') - - # get the ids of the resources of this model which share - # the same source - obj = pool.get(model) - if obj: - ids = obj.search(cr, uid, [(field, '=', dic['src'])]) - - # if the resource id (res_id) is in that list, use it, - # otherwise use the whole list - ids = (dic['res_id'] in ids) and [dic['res_id']] or ids - for id in ids: - dic['res_id'] = id - ids = trans_obj.search(cr, uid, [ - ('lang', '=', lang), - ('type', '=', dic['type']), - ('name', '=', dic['name']), - ('src', '=', dic['src']), - ('res_id', '=', dic['res_id']) - ]) - if ids: - trans_obj.write(cr, uid, ids, {'value': dic['value']}) - else: - trans_obj.create(cr, uid, dic) - else: - ids = trans_obj.search(cr, uid, [ - ('lang', '=', lang), - ('type', '=', dic['type']), - ('name', '=', dic['name']), - ('src', '=', dic['src']) - ]) - if ids: - trans_obj.write(cr, uid, ids, {'value': dic['value']}) - else: - trans_obj.create(cr, uid, dic) - cr.commit() - #except Exception, e: - # logger.notifyChannel('init', netsvc.LOG_ERROR, - # 'Import error: %s on line %d: %s!' % (str(e), line, row)) - # cr.rollback() - # cr.close() - # cr = pooler.get_db(db_name).cursor() - cr.close() - logger.notifyChannel("init", netsvc.LOG_INFO, - "translation file loaded succesfully") - except IOError: - logger.notifyChannel("init", netsvc.LOG_ERROR, "couldn't read file") + logger = netsvc.Logger() + logger.notifyChannel("init", netsvc.LOG_INFO, + 'loading translation file for language %s' % (lang)) + pool = pooler.get_pool(db_name) + lang_obj = pool.get('res.lang') + trans_obj = pool.get('ir.translation') + model_data_obj = pool.get('ir.model.data') + try: + uid = 1 + cr = pooler.get_db(db_name).cursor() + + ids = lang_obj.search(cr, uid, [('code','=',lang)]) + if not ids: + if not lang_name: + lang_name=lang + languages=tools.get_languages() + if lang in languages: + lang_name=languages[lang] + ids = lang_obj.create(cr, uid, { + 'code': lang, + 'name': lang_name, + 'translatable': 1, + }) + else: + lang_obj.write(cr, uid, ids, {'translatable':1}) + lang_ids = lang_obj.search(cr, uid, []) + langs = lang_obj.read(cr, uid, lang_ids) + ls = map(lambda x: (x['code'],x['name']), langs) + + fileobj.seek(0) + + if fileformat == 'csv': + reader = csv.reader(fileobj, quotechar='"', delimiter=',') + # read the first line of the file (it contains columns titles) + for row in reader: + f = row + break + elif fileformat == 'po': + reader = TinyPoFile(fileobj) + f = ['type', 'name', 'res_id', 'src', 'value'] + else: + raise Exception(_('Bad file format')) + + # read the rest of the file + line = 1 + for row in reader: + line += 1 + #try: + # skip empty rows and rows where the translation field (=last fiefd) is empty + if (not row) or (not row[-1]): + #print "translate: skip %s" % repr(row) + continue + + # dictionary which holds values for this line of the csv file + # {'lang': ..., 'type': ..., 'name': ..., 'res_id': ..., + # 'src': ..., 'value': ...} + dic = {'lang': lang} + for i in range(len(f)): + if f[i] in ('module',): + continue + dic[f[i]] = row[i] + + try: + dic['res_id'] = int(dic['res_id']) + except: + model_data_ids = model_data_obj.search(cr, uid, [ + ('model', '=', dic['name'].split(',')[0]), + ('module', '=', dic['res_id'].split('.', 1)[0]), + ('name', '=', dic['res_id'].split('.', 1)[1]), + ]) + if model_data_ids: + dic['res_id'] = model_data_obj.browse(cr, uid, + model_data_ids[0]).res_id + else: + dic['res_id'] = False + + if dic['type'] == 'model' and not strict: + (model, field) = dic['name'].split(',') + + # get the ids of the resources of this model which share + # the same source + obj = pool.get(model) + if obj: + ids = obj.search(cr, uid, [(field, '=', dic['src'])]) + + # if the resource id (res_id) is in that list, use it, + # otherwise use the whole list + ids = (dic['res_id'] in ids) and [dic['res_id']] or ids + for id in ids: + dic['res_id'] = id + ids = trans_obj.search(cr, uid, [ + ('lang', '=', lang), + ('type', '=', dic['type']), + ('name', '=', dic['name']), + ('src', '=', dic['src']), + ('res_id', '=', dic['res_id']) + ]) + if ids: + trans_obj.write(cr, uid, ids, {'value': dic['value']}) + else: + trans_obj.create(cr, uid, dic) + else: + ids = trans_obj.search(cr, uid, [ + ('lang', '=', lang), + ('type', '=', dic['type']), + ('name', '=', dic['name']), + ('src', '=', dic['src']) + ]) + if ids: + trans_obj.write(cr, uid, ids, {'value': dic['value']}) + else: + trans_obj.create(cr, uid, dic) + cr.commit() + #except Exception, e: + # logger.notifyChannel('init', netsvc.LOG_ERROR, + # 'Import error: %s on line %d: %s!' % (str(e), line, row)) + # cr.rollback() + # cr.close() + # cr = pooler.get_db(db_name).cursor() + cr.close() + logger.notifyChannel("init", netsvc.LOG_INFO, + "translation file loaded succesfully") + except IOError: + logger.notifyChannel("init", netsvc.LOG_ERROR, "couldn't read file") diff --git a/bin/tools/upgrade.py b/bin/tools/upgrade.py index 167ee7eaa6d..2d9c39ece2c 100644 --- a/bin/tools/upgrade.py +++ b/bin/tools/upgrade.py @@ -36,60 +36,60 @@ import tools # remove an existing version of modules if it exist def remove(name): - adp = tools.config['addons_path'] - addons = os.listdir(adp) - if name in addons: - try: - shutil.rmtree(os.path.join(adp, name)) - except: - print "Unable to remove module %s !" % name + adp = tools.config['addons_path'] + addons = os.listdir(adp) + if name in addons: + try: + shutil.rmtree(os.path.join(adp, name)) + except: + print "Unable to remove module %s !" % name def install(name, url): - tar = tarfile.open(mode="r|gz", fileobj=urllib2.urlopen(url)) - for tarinfo in tar: - tar.extract(tarinfo, tools.config['addons_path']) + tar = tarfile.open(mode="r|gz", fileobj=urllib2.urlopen(url)) + for tarinfo in tar: + tar.extract(tarinfo, tools.config['addons_path']) def upgrade(): - import pooler - cr = pooler.db.cursor() + import pooler + cr = pooler.db.cursor() - toinit = [] - toupdate = [] + toinit = [] + toupdate = [] -# print 'Check for correct rights (create and unlink on addons)...' - # todo: touch addons/test.txt - # todo: rm addons/test.txt +# print 'Check for correct rights (create and unlink on addons)...' + # todo: touch addons/test.txt + # todo: rm addons/test.txt - print 'Check for modules to remove...' - cr.execute('select id,name,url from ir_module_module where state=%s', ('to remove',)) - for module_id,name,url in cr.fetchall(): - print '\tremoving module %s' % name - remove(name) - cr.execute('update ir_module_module set state=%s where id=%d', ('uninstalled', module_id)) - cr.commit() + print 'Check for modules to remove...' + cr.execute('select id,name,url from ir_module_module where state=%s', ('to remove',)) + for module_id,name,url in cr.fetchall(): + print '\tremoving module %s' % name + remove(name) + cr.execute('update ir_module_module set state=%s where id=%d', ('uninstalled', module_id)) + cr.commit() - print 'Check for modules to upgrade...' - cr.execute('select id,name,url from ir_module_module where state=%s', ('to upgrade',)) - for module_id,name,url in cr.fetchall(): - print '\tupgrading module %s' % name - remove(name) - install(name, url) - cr.execute('update ir_module_module set state=%s where id=%d', ('installed', module_id)) - cr.commit() - toupdate.append(name) + print 'Check for modules to upgrade...' + cr.execute('select id,name,url from ir_module_module where state=%s', ('to upgrade',)) + for module_id,name,url in cr.fetchall(): + print '\tupgrading module %s' % name + remove(name) + install(name, url) + cr.execute('update ir_module_module set state=%s where id=%d', ('installed', module_id)) + cr.commit() + toupdate.append(name) - print 'Check for modules to install...' - cr.execute('select id,name,url from ir_module_module where state=%s', ('to install',)) - for module_id,name,url in cr.fetchall(): - print '\tinstalling module %s' % name - install(name, url) - cr.execute('update ir_module_module set state=%s where id=%d', ('installed', module_id)) - cr.commit() - toinit.append(name) + print 'Check for modules to install...' + cr.execute('select id,name,url from ir_module_module where state=%s', ('to install',)) + for module_id,name,url in cr.fetchall(): + print '\tinstalling module %s' % name + install(name, url) + cr.execute('update ir_module_module set state=%s where id=%d', ('installed', module_id)) + cr.commit() + toinit.append(name) - print 'Initializing all datas...' + print 'Initializing all datas...' - cr.commit() - cr.close() - return (toinit, toupdate) + cr.commit() + cr.close() + return (toinit, toupdate) diff --git a/bin/wizard/__init__.py b/bin/wizard/__init__.py index 10e57e93585..adeaee803d0 100644 --- a/bin/wizard/__init__.py +++ b/bin/wizard/__init__.py @@ -41,138 +41,138 @@ from netsvc import Logger, LOG_ERROR import sys class except_wizard(Exception): - def __init__(self, name, value): - self.name = name - self.value = value - self.args = (name, value) + def __init__(self, name, value): + self.name = name + self.value = value + self.args = (name, value) class interface(netsvc.Service): - states = {} - - def __init__(self, name): - assert not netsvc.service_exist('wizard.'+name), 'The wizard "%s" already exists!'%name - super(interface, self).__init__('wizard.'+name) - self.exportMethod(self.execute) - self.wiz_name = name - - def translate_view(self, cr, node, state, lang): - if node.nodeType == node.ELEMENT_NODE: - if node.hasAttribute('string') and node.getAttribute('string'): - trans = translate(cr, self.wiz_name+','+state, 'wizard_view', lang, node.getAttribute('string').encode('utf8')) - if trans: - node.setAttribute('string', trans.decode('utf8')) - for n in node.childNodes: - self.translate_view(cr, n, state, lang) + states = {} + + def __init__(self, name): + assert not netsvc.service_exist('wizard.'+name), 'The wizard "%s" already exists!'%name + super(interface, self).__init__('wizard.'+name) + self.exportMethod(self.execute) + self.wiz_name = name + + def translate_view(self, cr, node, state, lang): + if node.nodeType == node.ELEMENT_NODE: + if node.hasAttribute('string') and node.getAttribute('string'): + trans = translate(cr, self.wiz_name+','+state, 'wizard_view', lang, node.getAttribute('string').encode('utf8')) + if trans: + node.setAttribute('string', trans.decode('utf8')) + for n in node.childNodes: + self.translate_view(cr, n, state, lang) - def execute_cr(self, cr, uid, data, state='init', context=None): - if not context: - context={} - res = {} - try: - state_def = self.states[state] - result_def = state_def.get('result', {}) - - actions_res = {} - # iterate through the list of actions defined for this state - for action in state_def.get('actions', []): - # execute them - action_res = action(self, cr, uid, data, context) - assert isinstance(action_res, dict), 'The return value of wizard actions should be a dictionary' - actions_res.update(action_res) - - res = copy.copy(result_def) - res['datas'] = actions_res - - lang = context.get('lang', False) - if result_def['type'] == 'action': - res['action'] = result_def['action'](self, cr, uid, data, context) - elif result_def['type'] == 'form': - fields = copy.copy(result_def['fields']) - arch = copy.copy(result_def['arch']) - button_list = copy.copy(result_def['state']) + def execute_cr(self, cr, uid, data, state='init', context=None): + if not context: + context={} + res = {} + try: + state_def = self.states[state] + result_def = state_def.get('result', {}) + + actions_res = {} + # iterate through the list of actions defined for this state + for action in state_def.get('actions', []): + # execute them + action_res = action(self, cr, uid, data, context) + assert isinstance(action_res, dict), 'The return value of wizard actions should be a dictionary' + actions_res.update(action_res) + + res = copy.copy(result_def) + res['datas'] = actions_res + + lang = context.get('lang', False) + if result_def['type'] == 'action': + res['action'] = result_def['action'](self, cr, uid, data, context) + elif result_def['type'] == 'form': + fields = copy.copy(result_def['fields']) + arch = copy.copy(result_def['arch']) + button_list = copy.copy(result_def['state']) - if isinstance(fields, UpdateableDict): - fields = fields.dict - if isinstance(arch, UpdateableStr): - arch = arch.string + if isinstance(fields, UpdateableDict): + fields = fields.dict + if isinstance(arch, UpdateableStr): + arch = arch.string - # fetch user-set defaut values for the field... shouldn't we pass it the uid? - defaults = ir.ir_get(cr, uid, 'default', False, [('wizard.'+self.wiz_name, False)]) - default_values = dict([(x[1], x[2]) for x in defaults]) - for val in fields.keys(): - if 'default' in fields[val]: - # execute default method for this field - if callable(fields[val]['default']): - fields[val]['value'] = fields[val]['default'](uid, data, state) - else: - fields[val]['value'] = fields[val]['default'] - del fields[val]['default'] - else: - # if user has set a default value for the field, use it - if val in default_values: - fields[val]['value'] = default_values[val] - if 'selection' in fields[val]: - if not isinstance(fields[val]['selection'], (tuple, list)): - fields[val] = copy.copy(fields[val]) - fields[val]['selection'] = fields[val]['selection'](self, cr, uid, context) + # fetch user-set defaut values for the field... shouldn't we pass it the uid? + defaults = ir.ir_get(cr, uid, 'default', False, [('wizard.'+self.wiz_name, False)]) + default_values = dict([(x[1], x[2]) for x in defaults]) + for val in fields.keys(): + if 'default' in fields[val]: + # execute default method for this field + if callable(fields[val]['default']): + fields[val]['value'] = fields[val]['default'](uid, data, state) + else: + fields[val]['value'] = fields[val]['default'] + del fields[val]['default'] + else: + # if user has set a default value for the field, use it + if val in default_values: + fields[val]['value'] = default_values[val] + if 'selection' in fields[val]: + if not isinstance(fields[val]['selection'], (tuple, list)): + fields[val] = copy.copy(fields[val]) + fields[val]['selection'] = fields[val]['selection'](self, cr, uid, context) - if lang: - # translate fields - for field in fields: - trans = translate(cr, self.wiz_name+','+state+','+field, 'wizard_field', lang) - if trans: - fields[field]['string'] = trans + if lang: + # translate fields + for field in fields: + trans = translate(cr, self.wiz_name+','+state+','+field, 'wizard_field', lang) + if trans: + fields[field]['string'] = trans - # translate arch - if not isinstance(arch, UpdateableStr): - doc = dom.minidom.parseString(arch) - self.translate_view(cr, doc, state, lang) - arch = doc.toxml() + # translate arch + if not isinstance(arch, UpdateableStr): + doc = dom.minidom.parseString(arch) + self.translate_view(cr, doc, state, lang) + arch = doc.toxml() - # translate buttons - button_list = list(button_list) - for i, aa in enumerate(button_list): - button_name = aa[0] - trans = translate(cr, self.wiz_name+','+state+','+button_name, 'wizard_button', lang) - if trans: - aa = list(aa) - aa[1] = trans - button_list[i] = aa - - res['fields'] = fields - res['arch'] = arch - res['state'] = button_list + # translate buttons + button_list = list(button_list) + for i, aa in enumerate(button_list): + button_name = aa[0] + trans = translate(cr, self.wiz_name+','+state+','+button_name, 'wizard_button', lang) + if trans: + aa = list(aa) + aa[1] = trans + button_list[i] = aa + + res['fields'] = fields + res['arch'] = arch + res['state'] = button_list - except Exception, e: - if isinstance(e, except_wizard) \ - or isinstance(e, except_osv) \ - or isinstance(e, except_orm): - self.abortResponse(2, e.name, 'warning', e.value) - else: - import traceback - tb_s = reduce(lambda x, y: x+y, traceback.format_exception( - sys.exc_type, sys.exc_value, sys.exc_traceback)) - logger = Logger() - logger.notifyChannel("web-services", LOG_ERROR, - 'Exception in call: ' + tb_s) - raise + except Exception, e: + if isinstance(e, except_wizard) \ + or isinstance(e, except_osv) \ + or isinstance(e, except_orm): + self.abortResponse(2, e.name, 'warning', e.value) + else: + import traceback + tb_s = reduce(lambda x, y: x+y, traceback.format_exception( + sys.exc_type, sys.exc_value, sys.exc_traceback)) + logger = Logger() + logger.notifyChannel("web-services", LOG_ERROR, + 'Exception in call: ' + tb_s) + raise - if result_def['type'] == 'choice': - next_state = result_def['next_state'](self, cr, uid, data, context) - return self.execute_cr(cr, uid, data, next_state, context) - return res + if result_def['type'] == 'choice': + next_state = result_def['next_state'](self, cr, uid, data, context) + return self.execute_cr(cr, uid, data, next_state, context) + return res - def execute(self, db, uid, data, state='init', context=None): - if not context: - context={} - cr = pooler.get_db(db).cursor() - try: - try: - res = self.execute_cr(cr, uid, data, state, context) - cr.commit() - except Exception: - cr.rollback() - raise - finally: - cr.close() - return res + def execute(self, db, uid, data, state='init', context=None): + if not context: + context={} + cr = pooler.get_db(db).cursor() + try: + try: + res = self.execute_cr(cr, uid, data, state, context) + cr.commit() + except Exception: + cr.rollback() + raise + finally: + cr.close() + return res diff --git a/bin/workflow/common.py b/bin/workflow/common.py index 51d902996b0..9eba6b62926 100644 --- a/bin/workflow/common.py +++ b/bin/workflow/common.py @@ -26,6 +26,6 @@ ############################################################################## class except_wkf(Exception): - def __init__(self, name, value): - self.name = name - self.value = value + def __init__(self, name, value): + self.name = name + self.value = value diff --git a/bin/workflow/instance.py b/bin/workflow/instance.py index d25bc367f4c..82071e462f7 100644 --- a/bin/workflow/instance.py +++ b/bin/workflow/instance.py @@ -33,54 +33,54 @@ import netsvc import pooler def create(cr, ident, wkf_id): - (uid,res_type,res_id) = ident - cr.execute("select nextval('wkf_instance_id_seq')") - id_new = cr.fetchone()[0] - cr.execute('insert into wkf_instance (id,res_type,res_id,uid,wkf_id) values (%d,%s,%s,%s,%s)', (id_new,res_type,res_id,uid,wkf_id)) - cr.execute('select * from wkf_activity where flow_start=True and wkf_id=%d', (wkf_id,)) - res = cr.dictfetchall() - stack = [] - workitem.create(cr, res, id_new, ident, stack=stack) - update(cr, id_new, ident) - return id_new + (uid,res_type,res_id) = ident + cr.execute("select nextval('wkf_instance_id_seq')") + id_new = cr.fetchone()[0] + cr.execute('insert into wkf_instance (id,res_type,res_id,uid,wkf_id) values (%d,%s,%s,%s,%s)', (id_new,res_type,res_id,uid,wkf_id)) + cr.execute('select * from wkf_activity where flow_start=True and wkf_id=%d', (wkf_id,)) + res = cr.dictfetchall() + stack = [] + workitem.create(cr, res, id_new, ident, stack=stack) + update(cr, id_new, ident) + return id_new def delete(cr, ident): - (uid,res_type,res_id) = ident - cr.execute('delete from wkf_instance where res_id=%d and res_type=%s', (res_id,res_type)) + (uid,res_type,res_id) = ident + cr.execute('delete from wkf_instance where res_id=%d and res_type=%s', (res_id,res_type)) def validate(cr, inst_id, ident, signal, force_running=False): - cr.execute("select * from wkf_workitem where inst_id=%d", (inst_id,)) - for witem in cr.dictfetchall(): - stack = [] - workitem.process(cr, witem, ident, signal, force_running, stack=stack) - # An action is returned - _update_end(cr, inst_id, ident) - return stack and stack[0] or False + cr.execute("select * from wkf_workitem where inst_id=%d", (inst_id,)) + for witem in cr.dictfetchall(): + stack = [] + workitem.process(cr, witem, ident, signal, force_running, stack=stack) + # An action is returned + _update_end(cr, inst_id, ident) + return stack and stack[0] or False def update(cr, inst_id, ident): - cr.execute("select * from wkf_workitem where inst_id=%d", (inst_id,)) - for witem in cr.dictfetchall(): - stack = [] - workitem.process(cr, witem, ident, stack=stack) - return _update_end(cr, inst_id, ident) + cr.execute("select * from wkf_workitem where inst_id=%d", (inst_id,)) + for witem in cr.dictfetchall(): + stack = [] + workitem.process(cr, witem, ident, stack=stack) + return _update_end(cr, inst_id, ident) def _update_end(cr, inst_id, ident): - cr.execute('select wkf_id from wkf_instance where id=%d', (inst_id,)) - wkf_id = cr.fetchone()[0] - cr.execute('select state,flow_stop from wkf_workitem w left join wkf_activity a on (a.id=w.act_id) where w.inst_id=%d', (inst_id,)) - ok=True - for r in cr.fetchall(): - if (r[0]<>'complete') or not r[1]: - ok=False - break - if ok: - cr.execute('select distinct a.name from wkf_activity a left join wkf_workitem w on (a.id=w.act_id) where w.inst_id=%d', (inst_id,)) - act_names = cr.fetchall() - cr.execute("update wkf_instance set state='complete' where id=%d", (inst_id,)) - cr.execute("update wkf_workitem set state='complete' where subflow_id=%d", (inst_id,)) - cr.execute("select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id in (select inst_id from wkf_workitem where subflow_id=%d)", (inst_id,)) - for i in cr.fetchall(): - for act_name in act_names: - validate(cr, i[0], (ident[0],i[1],i[2]), 'subflow.'+act_name[0]) - return ok + cr.execute('select wkf_id from wkf_instance where id=%d', (inst_id,)) + wkf_id = cr.fetchone()[0] + cr.execute('select state,flow_stop from wkf_workitem w left join wkf_activity a on (a.id=w.act_id) where w.inst_id=%d', (inst_id,)) + ok=True + for r in cr.fetchall(): + if (r[0]<>'complete') or not r[1]: + ok=False + break + if ok: + cr.execute('select distinct a.name from wkf_activity a left join wkf_workitem w on (a.id=w.act_id) where w.inst_id=%d', (inst_id,)) + act_names = cr.fetchall() + cr.execute("update wkf_instance set state='complete' where id=%d", (inst_id,)) + cr.execute("update wkf_workitem set state='complete' where subflow_id=%d", (inst_id,)) + cr.execute("select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id in (select inst_id from wkf_workitem where subflow_id=%d)", (inst_id,)) + for i in cr.fetchall(): + for act_name in act_names: + validate(cr, i[0], (ident[0],i[1],i[2]), 'subflow.'+act_name[0]) + return ok diff --git a/bin/workflow/wkf_expr.py b/bin/workflow/wkf_expr.py index f1476cc8188..dca3e666efe 100644 --- a/bin/workflow/wkf_expr.py +++ b/bin/workflow/wkf_expr.py @@ -34,77 +34,77 @@ import pooler class EnvCall(object): - def __init__(self,wf_service,d_arg): - self.wf_service=wf_service - self.d_arg=d_arg + def __init__(self,wf_service,d_arg): + self.wf_service=wf_service + self.d_arg=d_arg - def __call__(self,*args): - arg=self.d_arg+args - return self.wf_service.execute_cr(*arg) + def __call__(self,*args): + arg=self.d_arg+args + return self.wf_service.execute_cr(*arg) class Env(dict): - def __init__(self, wf_service, cr, uid, model, ids): - self.wf_service = wf_service - self.cr = cr - self.uid = uid - self.model = model - self.ids = ids - self.obj = pooler.get_pool(cr.dbname).get(model) - self.columns = self.obj._columns.keys() + self.obj._inherit_fields.keys() + def __init__(self, wf_service, cr, uid, model, ids): + self.wf_service = wf_service + self.cr = cr + self.uid = uid + self.model = model + self.ids = ids + self.obj = pooler.get_pool(cr.dbname).get(model) + self.columns = self.obj._columns.keys() + self.obj._inherit_fields.keys() - def __getitem__(self, key): - if (key in self.columns) or (key in dir(self.obj)): - res = self.obj.browse(self.cr, self.uid, self.ids[0]) - return res[key] - #res=self.wf_service.execute_cr(self.cr, self.uid, self.model, 'read',\ - # self.ids, [key])[0][key] - #super(Env, self).__setitem__(key, res) - #return res - #elif key in dir(self.obj): - # return EnvCall(self.wf_service, (self.cr, self.uid, self.model, key,\ - # self.ids)) - else: - return super(Env, self).__getitem__(key) + def __getitem__(self, key): + if (key in self.columns) or (key in dir(self.obj)): + res = self.obj.browse(self.cr, self.uid, self.ids[0]) + return res[key] + #res=self.wf_service.execute_cr(self.cr, self.uid, self.model, 'read',\ + # self.ids, [key])[0][key] + #super(Env, self).__setitem__(key, res) + #return res + #elif key in dir(self.obj): + # return EnvCall(self.wf_service, (self.cr, self.uid, self.model, key,\ + # self.ids)) + else: + return super(Env, self).__getitem__(key) def _eval_expr(cr, ident, workitem, action): - ret=False - assert action, 'You used a NULL action in a workflow, use dummy node instead.' - for line in action.split('\n'): - uid=ident[0] - model=ident[1] - ids=[ident[2]] - if line =='True': - ret=True - elif line =='False': - ret=False - else: - wf_service = netsvc.LocalService("object_proxy") - env = Env(wf_service, cr, uid, model, ids) - ret = eval(line, env) - return ret + ret=False + assert action, 'You used a NULL action in a workflow, use dummy node instead.' + for line in action.split('\n'): + uid=ident[0] + model=ident[1] + ids=[ident[2]] + if line =='True': + ret=True + elif line =='False': + ret=False + else: + wf_service = netsvc.LocalService("object_proxy") + env = Env(wf_service, cr, uid, model, ids) + ret = eval(line, env) + return ret def execute_action(cr, ident, workitem, activity): - wf_service = netsvc.LocalService("object_proxy") - obj = pooler.get_pool(cr.dbname).get('ir.actions.server') - ctx = {'active_id':ident[2], 'active_ids':[ident[2]]} - result = obj.run(cr, ident[0], [activity['action_id']], ctx) - return result + wf_service = netsvc.LocalService("object_proxy") + obj = pooler.get_pool(cr.dbname).get('ir.actions.server') + ctx = {'active_id':ident[2], 'active_ids':[ident[2]]} + result = obj.run(cr, ident[0], [activity['action_id']], ctx) + return result def execute(cr, ident, workitem, activity): - return _eval_expr(cr, ident, workitem, activity['action']) + return _eval_expr(cr, ident, workitem, activity['action']) def check(cr, workitem, ident, transition, signal): - ok = True - if transition['signal']: - ok = (signal==transition['signal']) + ok = True + if transition['signal']: + ok = (signal==transition['signal']) - if transition['role_id']: - uid = ident[0] - serv = netsvc.LocalService('object_proxy') - user_roles = serv.execute_cr(cr, uid, 'res.users', 'read', [uid], ['roles_id'])[0]['roles_id'] - ok = ok and serv.execute_cr(cr, uid, 'res.roles', 'check', user_roles, transition['role_id']) - ok = ok and _eval_expr(cr, ident, workitem, transition['condition']) - return ok + if transition['role_id']: + uid = ident[0] + serv = netsvc.LocalService('object_proxy') + user_roles = serv.execute_cr(cr, uid, 'res.users', 'read', [uid], ['roles_id'])[0]['roles_id'] + ok = ok and serv.execute_cr(cr, uid, 'res.roles', 'check', user_roles, transition['role_id']) + ok = ok and _eval_expr(cr, ident, workitem, transition['condition']) + return ok diff --git a/bin/workflow/wkf_logs.py b/bin/workflow/wkf_logs.py index 0ec3ded3e2d..f8d68c1f37a 100644 --- a/bin/workflow/wkf_logs.py +++ b/bin/workflow/wkf_logs.py @@ -31,5 +31,5 @@ # def log(cr,ident,act_id,info=''): - pass - #cr.execute('insert into wkf_logs (res_type, res_id, uid, act_id, time, info) values (%s,%d,%d,%d,current_time,%s)', (ident[1],int(ident[2]),int(ident[0]),int(act_id),info)) + pass + #cr.execute('insert into wkf_logs (res_type, res_id, uid, act_id, time, info) values (%s,%d,%d,%d,current_time,%s)', (ident[1],int(ident[2]),int(ident[0]),int(act_id),info)) diff --git a/bin/workflow/wkf_service.py b/bin/workflow/wkf_service.py index f1c103ccfe0..e980c47af90 100644 --- a/bin/workflow/wkf_service.py +++ b/bin/workflow/wkf_service.py @@ -34,79 +34,79 @@ import netsvc import pooler class workflow_service(netsvc.Service): - def __init__(self, name='workflow', audience='*'): - netsvc.Service.__init__(self, name, audience) - self.exportMethod(self.trg_write) - self.exportMethod(self.trg_delete) - self.exportMethod(self.trg_create) - self.exportMethod(self.trg_validate) - self.exportMethod(self.trg_redirect) - self.exportMethod(self.trg_trigger) - self.exportMethod(self.clear_cache) - self.wkf_on_create_cache={} + def __init__(self, name='workflow', audience='*'): + netsvc.Service.__init__(self, name, audience) + self.exportMethod(self.trg_write) + self.exportMethod(self.trg_delete) + self.exportMethod(self.trg_create) + self.exportMethod(self.trg_validate) + self.exportMethod(self.trg_redirect) + self.exportMethod(self.trg_trigger) + self.exportMethod(self.clear_cache) + self.wkf_on_create_cache={} - def clear_cache(self, cr, uid): - self.wkf_on_create_cache[cr.dbname]={} + def clear_cache(self, cr, uid): + self.wkf_on_create_cache[cr.dbname]={} - def trg_write(self, uid, res_type, res_id, cr): - ident = (uid,res_type,res_id) - cr.execute('select id from wkf_instance where res_id=%d and res_type=%s and state=%s', (res_id,res_type, 'active')) - for (id,) in cr.fetchall(): - instance.update(cr, id, ident) + def trg_write(self, uid, res_type, res_id, cr): + ident = (uid,res_type,res_id) + cr.execute('select id from wkf_instance where res_id=%d and res_type=%s and state=%s', (res_id,res_type, 'active')) + for (id,) in cr.fetchall(): + instance.update(cr, id, ident) - def trg_trigger(self, uid, res_type, res_id, cr): - cr.execute('select instance_id from wkf_triggers where res_id=%d and model=%s', (res_id,res_type)) - res = cr.fetchall() - for (instance_id,) in res: - cr.execute('select uid,res_type,res_id from wkf_instance where id=%d', (instance_id,)) - ident = cr.fetchone() - instance.update(cr, instance_id, ident) + def trg_trigger(self, uid, res_type, res_id, cr): + cr.execute('select instance_id from wkf_triggers where res_id=%d and model=%s', (res_id,res_type)) + res = cr.fetchall() + for (instance_id,) in res: + cr.execute('select uid,res_type,res_id from wkf_instance where id=%d', (instance_id,)) + ident = cr.fetchone() + instance.update(cr, instance_id, ident) - def trg_delete(self, uid, res_type, res_id, cr): - ident = (uid,res_type,res_id) - instance.delete(cr, ident) + def trg_delete(self, uid, res_type, res_id, cr): + ident = (uid,res_type,res_id) + instance.delete(cr, ident) - def trg_create(self, uid, res_type, res_id, cr): - ident = (uid,res_type,res_id) - self.wkf_on_create_cache.setdefault(cr.dbname, {}) - if res_type in self.wkf_on_create_cache[cr.dbname]: - wkf_ids = self.wkf_on_create_cache[cr.dbname][res_type] - else: - cr.execute('select id from wkf where osv=%s and on_create=True', (res_type,)) - wkf_ids = cr.fetchall() - self.wkf_on_create_cache[cr.dbname][res_type] = wkf_ids - for (wkf_id,) in wkf_ids: - instance.create(cr, ident, wkf_id) + def trg_create(self, uid, res_type, res_id, cr): + ident = (uid,res_type,res_id) + self.wkf_on_create_cache.setdefault(cr.dbname, {}) + if res_type in self.wkf_on_create_cache[cr.dbname]: + wkf_ids = self.wkf_on_create_cache[cr.dbname][res_type] + else: + cr.execute('select id from wkf where osv=%s and on_create=True', (res_type,)) + wkf_ids = cr.fetchall() + self.wkf_on_create_cache[cr.dbname][res_type] = wkf_ids + for (wkf_id,) in wkf_ids: + instance.create(cr, ident, wkf_id) - def trg_validate(self, uid, res_type, res_id, signal, cr): - result = False - ident = (uid,res_type,res_id) - # ids of all active workflow instances for a corresponding resource (id, model_nam) - cr.execute('select id from wkf_instance where res_id=%d and res_type=%s and state=%s', (res_id, res_type, 'active')) - for (id,) in cr.fetchall(): - res2 = instance.validate(cr, id, ident, signal) - result = result or res2 - return result + def trg_validate(self, uid, res_type, res_id, signal, cr): + result = False + ident = (uid,res_type,res_id) + # ids of all active workflow instances for a corresponding resource (id, model_nam) + cr.execute('select id from wkf_instance where res_id=%d and res_type=%s and state=%s', (res_id, res_type, 'active')) + for (id,) in cr.fetchall(): + res2 = instance.validate(cr, id, ident, signal) + result = result or res2 + return result - # make all workitems which are waiting for a (subflow) workflow instance - # for the old resource point to the (first active) workflow instance for - # the new resource - def trg_redirect(self, uid, res_type, res_id, new_rid, cr): - # get ids of wkf instances for the old resource (res_id) + # make all workitems which are waiting for a (subflow) workflow instance + # for the old resource point to the (first active) workflow instance for + # the new resource + def trg_redirect(self, uid, res_type, res_id, new_rid, cr): + # get ids of wkf instances for the old resource (res_id) #CHECKME: shouldn't we get only active instances? - cr.execute('select id, wkf_id from wkf_instance where res_id=%d and res_type=%s', (res_id, res_type)) - for old_inst_id, wkf_id in cr.fetchall(): - # first active instance for new resource (new_rid), using same wkf - cr.execute( - 'SELECT id '\ - 'FROM wkf_instance '\ - 'WHERE res_id=%d AND res_type=%s AND wkf_id=%d AND state=%s', - (new_rid, res_type, wkf_id, 'active')) - new_id = cr.fetchone() - if new_id: - # select all workitems which "wait" for the old instance - cr.execute('select id from wkf_workitem where subflow_id=%d', (old_inst_id,)) - for (item_id,) in cr.fetchall(): - # redirect all those workitems to the wkf instance of the new resource - cr.execute('update wkf_workitem set subflow_id=%d where id=%d', (new_id[0], item_id)) + cr.execute('select id, wkf_id from wkf_instance where res_id=%d and res_type=%s', (res_id, res_type)) + for old_inst_id, wkf_id in cr.fetchall(): + # first active instance for new resource (new_rid), using same wkf + cr.execute( + 'SELECT id '\ + 'FROM wkf_instance '\ + 'WHERE res_id=%d AND res_type=%s AND wkf_id=%d AND state=%s', + (new_rid, res_type, wkf_id, 'active')) + new_id = cr.fetchone() + if new_id: + # select all workitems which "wait" for the old instance + cr.execute('select id from wkf_workitem where subflow_id=%d', (old_inst_id,)) + for (item_id,) in cr.fetchall(): + # redirect all those workitems to the wkf instance of the new resource + cr.execute('update wkf_workitem set subflow_id=%d where id=%d', (new_id[0], item_id)) workflow_service() diff --git a/bin/workflow/workitem.py b/bin/workflow/workitem.py index 2dbc3600bd9..54076a9e729 100644 --- a/bin/workflow/workitem.py +++ b/bin/workflow/workitem.py @@ -38,158 +38,158 @@ import wkf_expr import wkf_logs def create(cr, act_datas, inst_id, ident, stack): - for act in act_datas: - cr.execute("select nextval('wkf_workitem_id_seq')") - id_new = cr.fetchone()[0] - cr.execute("insert into wkf_workitem (id,act_id,inst_id,state) values (%d,%s,%s,'active')", (id_new, act['id'], inst_id)) - cr.execute('select * from wkf_workitem where id=%d',(id_new,)) - res = cr.dictfetchone() - wkf_logs.log(cr,ident,act['id'],'active') - process(cr, res, ident, stack=stack) + for act in act_datas: + cr.execute("select nextval('wkf_workitem_id_seq')") + id_new = cr.fetchone()[0] + cr.execute("insert into wkf_workitem (id,act_id,inst_id,state) values (%d,%s,%s,'active')", (id_new, act['id'], inst_id)) + cr.execute('select * from wkf_workitem where id=%d',(id_new,)) + res = cr.dictfetchone() + wkf_logs.log(cr,ident,act['id'],'active') + process(cr, res, ident, stack=stack) def process(cr, workitem, ident, signal=None, force_running=False, stack=None): - if stack is None: - raise 'Error !!!' - result = True - cr.execute('select * from wkf_activity where id=%d', (workitem['act_id'],)) - activity = cr.dictfetchone() - - triggers = False - if workitem['state']=='active': - triggers = True - result = _execute(cr, workitem, activity, ident, stack) - if not result: - return False - - if workitem['state']=='running': - pass - - if workitem['state']=='complete' or force_running: - ok = _split_test(cr, workitem, activity['split_mode'], ident, signal, stack) - triggers = triggers and not ok - - if triggers: - cr.execute('select * from wkf_transition where act_from=%d', (workitem['act_id'],)) - alltrans = cr.dictfetchall() - for trans in alltrans: - if trans['trigger_model']: - ids = wkf_expr._eval_expr(cr,ident,workitem,trans['trigger_expr_id']) - for res_id in ids: - cr.execute('select nextval(\'wkf_triggers_id_seq\')') - id =cr.fetchone()[0] - cr.execute('insert into wkf_triggers (model,res_id,instance_id,workitem_id,id) values (%s,%d,%d,%d,%d)', (trans['trigger_model'],res_id,workitem['inst_id'], workitem['id'], id)) - - return result + if stack is None: + raise 'Error !!!' + result = True + cr.execute('select * from wkf_activity where id=%d', (workitem['act_id'],)) + activity = cr.dictfetchone() + + triggers = False + if workitem['state']=='active': + triggers = True + result = _execute(cr, workitem, activity, ident, stack) + if not result: + return False + + if workitem['state']=='running': + pass + + if workitem['state']=='complete' or force_running: + ok = _split_test(cr, workitem, activity['split_mode'], ident, signal, stack) + triggers = triggers and not ok + + if triggers: + cr.execute('select * from wkf_transition where act_from=%d', (workitem['act_id'],)) + alltrans = cr.dictfetchall() + for trans in alltrans: + if trans['trigger_model']: + ids = wkf_expr._eval_expr(cr,ident,workitem,trans['trigger_expr_id']) + for res_id in ids: + cr.execute('select nextval(\'wkf_triggers_id_seq\')') + id =cr.fetchone()[0] + cr.execute('insert into wkf_triggers (model,res_id,instance_id,workitem_id,id) values (%s,%d,%d,%d,%d)', (trans['trigger_model'],res_id,workitem['inst_id'], workitem['id'], id)) + + return result # ---------------------- PRIVATE FUNCS -------------------------------- def _state_set(cr, workitem, activity, state, ident): - cr.execute('update wkf_workitem set state=%s where id=%d', (state,workitem['id'])) - workitem['state'] = state - wkf_logs.log(cr,ident,activity['id'],state) + cr.execute('update wkf_workitem set state=%s where id=%d', (state,workitem['id'])) + workitem['state'] = state + wkf_logs.log(cr,ident,activity['id'],state) def _execute(cr, workitem, activity, ident, stack): - result = True - # - # send a signal to parent workflow (signal: subflow.signal_name) - # - if (workitem['state']=='active') and activity['signal_send']: - cr.execute("select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id in (select inst_id from wkf_workitem where subflow_id=%d)", (workitem['inst_id'],)) - for i in cr.fetchall(): - instance.validate(cr, i[0], (ident[0],i[1],i[2]), activity['signal_send'], force_running=True) - - if activity['kind']=='dummy': - if workitem['state']=='active': - _state_set(cr, workitem, activity, 'complete', ident) - elif activity['kind']=='function': - if workitem['state']=='active': - _state_set(cr, workitem, activity, 'running', ident) - wkf_expr.execute(cr, ident, workitem, activity) - if activity['action_id']: - res2 = wkf_expr.execute_action(cr, ident, workitem, activity) - # A client action has been returned - if res2: - stack.append(res2) - result=res2 - _state_set(cr, workitem, activity, 'complete', ident) - elif activity['kind']=='stopall': - if workitem['state']=='active': - _state_set(cr, workitem, activity, 'running', ident) - cr.execute('delete from wkf_workitem where inst_id=%d and id<>%d', (workitem['inst_id'], workitem['id'])) - if activity['action']: - wkf_expr.execute(cr, ident, workitem, activity) - _state_set(cr, workitem, activity, 'complete', ident) - elif activity['kind']=='subflow': - if workitem['state']=='active': - _state_set(cr, workitem, activity, 'running', ident) - if activity.get('action', False): - id_new = wkf_expr.execute(cr, ident, workitem, activity) - if not (id_new): - cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],)) - return False - assert type(id_new)==type(1) or type(id_new)==type(1L), 'Wrong return value: '+str(id_new)+' '+str(type(id_new)) - cr.execute('select id from wkf_instance where res_id=%d and wkf_id=%d', (id_new,activity['subflow_id'])) - id_new = cr.fetchone()[0] - else: - id_new = instance.create(cr, ident, activity['subflow_id']) - cr.execute('update wkf_workitem set subflow_id=%d where id=%s', (id_new, workitem['id'])) - workitem['subflow_id'] = id_new - if workitem['state']=='running': - cr.execute("select state from wkf_instance where id=%d", (workitem['subflow_id'],)) - state= cr.fetchone()[0] - if state=='complete': - _state_set(cr, workitem, activity, 'complete', ident) - return result + result = True + # + # send a signal to parent workflow (signal: subflow.signal_name) + # + if (workitem['state']=='active') and activity['signal_send']: + cr.execute("select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id in (select inst_id from wkf_workitem where subflow_id=%d)", (workitem['inst_id'],)) + for i in cr.fetchall(): + instance.validate(cr, i[0], (ident[0],i[1],i[2]), activity['signal_send'], force_running=True) + + if activity['kind']=='dummy': + if workitem['state']=='active': + _state_set(cr, workitem, activity, 'complete', ident) + elif activity['kind']=='function': + if workitem['state']=='active': + _state_set(cr, workitem, activity, 'running', ident) + wkf_expr.execute(cr, ident, workitem, activity) + if activity['action_id']: + res2 = wkf_expr.execute_action(cr, ident, workitem, activity) + # A client action has been returned + if res2: + stack.append(res2) + result=res2 + _state_set(cr, workitem, activity, 'complete', ident) + elif activity['kind']=='stopall': + if workitem['state']=='active': + _state_set(cr, workitem, activity, 'running', ident) + cr.execute('delete from wkf_workitem where inst_id=%d and id<>%d', (workitem['inst_id'], workitem['id'])) + if activity['action']: + wkf_expr.execute(cr, ident, workitem, activity) + _state_set(cr, workitem, activity, 'complete', ident) + elif activity['kind']=='subflow': + if workitem['state']=='active': + _state_set(cr, workitem, activity, 'running', ident) + if activity.get('action', False): + id_new = wkf_expr.execute(cr, ident, workitem, activity) + if not (id_new): + cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],)) + return False + assert type(id_new)==type(1) or type(id_new)==type(1L), 'Wrong return value: '+str(id_new)+' '+str(type(id_new)) + cr.execute('select id from wkf_instance where res_id=%d and wkf_id=%d', (id_new,activity['subflow_id'])) + id_new = cr.fetchone()[0] + else: + id_new = instance.create(cr, ident, activity['subflow_id']) + cr.execute('update wkf_workitem set subflow_id=%d where id=%s', (id_new, workitem['id'])) + workitem['subflow_id'] = id_new + if workitem['state']=='running': + cr.execute("select state from wkf_instance where id=%d", (workitem['subflow_id'],)) + state= cr.fetchone()[0] + if state=='complete': + _state_set(cr, workitem, activity, 'complete', ident) + return result def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None): - if stack is None: - raise 'Error !!!' - cr.execute('select * from wkf_transition where act_from=%d', (workitem['act_id'],)) - test = False - transitions = [] - alltrans = cr.dictfetchall() - if split_mode=='XOR' or split_mode=='OR': - for transition in alltrans: - if wkf_expr.check(cr, workitem, ident, transition,signal): - test = True - transitions.append((transition['id'], workitem['inst_id'])) - if split_mode=='XOR': - break - else: - test = True - for transition in alltrans: - if not wkf_expr.check(cr, workitem, ident, transition,signal): - test = False - break - cr.execute('select count(*) from wkf_witm_trans where trans_id=%d and inst_id=%d', (transition['id'], workitem['inst_id'])) - if not cr.fetchone()[0]: - transitions.append((transition['id'], workitem['inst_id'])) - if test and len(transitions): - cr.executemany('insert into wkf_witm_trans (trans_id,inst_id) values (%d,%d)', transitions) - cr.execute('delete from wkf_workitem where id=%d', (workitem['id'],)) - for t in transitions: - _join_test(cr, t[0], t[1], ident, stack) - return True - return False + if stack is None: + raise 'Error !!!' + cr.execute('select * from wkf_transition where act_from=%d', (workitem['act_id'],)) + test = False + transitions = [] + alltrans = cr.dictfetchall() + if split_mode=='XOR' or split_mode=='OR': + for transition in alltrans: + if wkf_expr.check(cr, workitem, ident, transition,signal): + test = True + transitions.append((transition['id'], workitem['inst_id'])) + if split_mode=='XOR': + break + else: + test = True + for transition in alltrans: + if not wkf_expr.check(cr, workitem, ident, transition,signal): + test = False + break + cr.execute('select count(*) from wkf_witm_trans where trans_id=%d and inst_id=%d', (transition['id'], workitem['inst_id'])) + if not cr.fetchone()[0]: + transitions.append((transition['id'], workitem['inst_id'])) + if test and len(transitions): + cr.executemany('insert into wkf_witm_trans (trans_id,inst_id) values (%d,%d)', transitions) + cr.execute('delete from wkf_workitem where id=%d', (workitem['id'],)) + for t in transitions: + _join_test(cr, t[0], t[1], ident, stack) + return True + return False def _join_test(cr, trans_id, inst_id, ident, stack): - cr.execute('select * from wkf_activity where id=(select act_to from wkf_transition where id=%d)', (trans_id,)) - activity = cr.dictfetchone() - if activity['join_mode']=='XOR': - create(cr,[activity], inst_id, ident, stack) - cr.execute('delete from wkf_witm_trans where inst_id=%d and trans_id=%d', (inst_id,trans_id)) - else: - cr.execute('select id from wkf_transition where act_to=%d', (activity['id'],)) - trans_ids = cr.fetchall() - ok = True - for (id,) in trans_ids: - cr.execute('select count(*) from wkf_witm_trans where trans_id=%d and inst_id=%d', (id,inst_id)) - res = cr.fetchone()[0] - if not res: - ok = False - break - if ok: - for (id,) in trans_ids: - cr.execute('delete from wkf_witm_trans where trans_id=%d and inst_id=%d', (id,inst_id)) - create(cr, [activity], inst_id, ident, stack) + cr.execute('select * from wkf_activity where id=(select act_to from wkf_transition where id=%d)', (trans_id,)) + activity = cr.dictfetchone() + if activity['join_mode']=='XOR': + create(cr,[activity], inst_id, ident, stack) + cr.execute('delete from wkf_witm_trans where inst_id=%d and trans_id=%d', (inst_id,trans_id)) + else: + cr.execute('select id from wkf_transition where act_to=%d', (activity['id'],)) + trans_ids = cr.fetchall() + ok = True + for (id,) in trans_ids: + cr.execute('select count(*) from wkf_witm_trans where trans_id=%d and inst_id=%d', (id,inst_id)) + res = cr.fetchone()[0] + if not res: + ok = False + break + if ok: + for (id,) in trans_ids: + cr.execute('delete from wkf_witm_trans where trans_id=%d and inst_id=%d', (id,inst_id)) + create(cr, [activity], inst_id, ident, stack) diff --git a/doc/migrate/3.3.0-3.4.0/post.py b/doc/migrate/3.3.0-3.4.0/post.py index cc99e3732c2..6983bfad519 100644 --- a/doc/migrate/3.3.0-3.4.0/post.py +++ b/doc/migrate/3.3.0-3.4.0/post.py @@ -53,15 +53,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) # ----- @@ -91,23 +91,23 @@ cr.execute("SELECT c.relname FROM pg_class c, pg_attribute a WHERE c.relname='re partners=[] drop_payment_term=False if cr.rowcount: - drop_payment_term=True - cr.execute("select id, payment_term from res_partner where payment_term is not null") - partners = cr.dictfetchall() + drop_payment_term=True + cr.execute("select id, payment_term from res_partner where payment_term is not null") + partners = cr.dictfetchall() # loop over them for partner in partners: - value = 'account.payment.term,%d' % partner['payment_term'] - res_id = 'res.partner,%d' % partner['id'] - cr.execute( - "insert into ir_property(name, value, res_id, company_id, fields_id) "\ - "values(%s, %s, %s, %d, %d)", - ('property_payment_term', value, res_id, company_id, fields_id)) + value = 'account.payment.term,%d' % partner['payment_term'] + res_id = 'res.partner,%d' % partner['id'] + cr.execute( + "insert into ir_property(name, value, res_id, company_id, fields_id) "\ + "values(%s, %s, %s, %d, %d)", + ('property_payment_term', value, res_id, company_id, fields_id)) # remove the field if drop_payment_term: - cr.execute("alter table res_partner drop column payment_term") + cr.execute("alter table res_partner drop column payment_term") cr.execute("delete from ir_model_fields where model = 'res.partner' and name = 'payment_term'") cr.commit() @@ -124,10 +124,10 @@ registered_reports = cr.fetchall() reg_reports_ids = ','.join([str(id) for (id,) in registered_reports]) for report in reports_wh_duplicates: - cr.execute("select id from ir_act_report_xml where model=%s and report_name=%s and id not in ("+reg_reports_ids+")", (report['model'], report['report_name'])) - (id,) = cr.fetchone() - cr.execute("delete from ir_act_report_xml where id=%d", (id,)) - cr.execute("delete from ir_values where value='ir.actions.report.xml,%d'", (id,)) + cr.execute("select id from ir_act_report_xml where model=%s and report_name=%s and id not in ("+reg_reports_ids+")", (report['model'], report['report_name'])) + (id,) = cr.fetchone() + cr.execute("delete from ir_act_report_xml where id=%d", (id,)) + cr.execute("delete from ir_values where value='ir.actions.report.xml,%d'", (id,)) cr.commit() diff --git a/doc/migrate/3.3.0-3.4.0/pre.py b/doc/migrate/3.3.0-3.4.0/pre.py index c2cab1b9e0a..df8e4a7d487 100644 --- a/doc/migrate/3.3.0-3.4.0/pre.py +++ b/doc/migrate/3.3.0-3.4.0/pre.py @@ -53,15 +53,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) # ----- @@ -79,18 +79,18 @@ cr = db.cursor() # ------------------------- # def change_column(cr, table, column, new_type, copy): - commands = [ - "ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column), - "ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type), - "ALTER TABLE %s DROP COLUMN temp_column" % table - ] - if copy: - commands.insert( - 2, - "UPDATE %s SET %s=temp_column::%s" % (table, column, new_type)) - - for command in commands: - cr.execute(command) + commands = [ + "ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column), + "ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type), + "ALTER TABLE %s DROP COLUMN temp_column" % table + ] + if copy: + commands.insert( + 2, + "UPDATE %s SET %s=temp_column::%s" % (table, column, new_type)) + + for command in commands: + cr.execute(command) change_column(cr, 'account_account_type', 'code_from', 'varchar(10)', False) change_column(cr, 'account_account_type', 'code_to', 'varchar(10)', False) @@ -101,17 +101,17 @@ cr.commit() # ----------------------------------------------------- # for line in ( - "alter table ir_model_fields add group_name varchar(64)", - "alter table ir_model_fields add view_load boolean", - "alter table ir_model_fields alter group_name set default ''", - "alter table ir_model_fields alter view_load set default False", - "delete from ir_values where value like '%,False'", - ): - try: - cr.execute(line) - except psycopg.ProgrammingError, e: - cr.commit() - print e + "alter table ir_model_fields add group_name varchar(64)", + "alter table ir_model_fields add view_load boolean", + "alter table ir_model_fields alter group_name set default ''", + "alter table ir_model_fields alter view_load set default False", + "delete from ir_values where value like '%,False'", + ): + try: + cr.execute(line) + except psycopg.ProgrammingError, e: + cr.commit() + print e cr.commit() cr.close() diff --git a/doc/migrate/3.4.0-4.0.0/post-tiny.py b/doc/migrate/3.4.0-4.0.0/post-tiny.py index ff47b041398..5c3646de8a0 100644 --- a/doc/migrate/3.4.0-4.0.0/post-tiny.py +++ b/doc/migrate/3.4.0-4.0.0/post-tiny.py @@ -53,15 +53,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) # ----- diff --git a/doc/migrate/3.4.0-4.0.0/post.py b/doc/migrate/3.4.0-4.0.0/post.py index 9afdd89c038..c90da55750a 100644 --- a/doc/migrate/3.4.0-4.0.0/post.py +++ b/doc/migrate/3.4.0-4.0.0/post.py @@ -53,15 +53,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) # ----- @@ -86,10 +86,10 @@ cr.commit() # --------------- # while True: - cr.execute("select id from ir_ui_menu where (id not in (select parent_id from ir_ui_menu where parent_id is not null)) and (id not in (select res_id from ir_values where model='ir.ui.menu'))") - if not cr.rowcount: - break - cr.execute("delete from ir_ui_menu where (id not in (select parent_id from ir_ui_menu where parent_id is not null)) and (id not in (select res_id from ir_values where model='ir.ui.menu'))") + cr.execute("select id from ir_ui_menu where (id not in (select parent_id from ir_ui_menu where parent_id is not null)) and (id not in (select res_id from ir_values where model='ir.ui.menu'))") + if not cr.rowcount: + break + cr.execute("delete from ir_ui_menu where (id not in (select parent_id from ir_ui_menu where parent_id is not null)) and (id not in (select res_id from ir_values where model='ir.ui.menu'))") cr.commit() # ----------------------------------------- # @@ -114,7 +114,7 @@ It is not possible to migrate the data automatically so you need to create the o And then update the field uos_id of the table account_invoice to match the new id of product_uom. EXAMPLE: - UPDATE account_invoice SET uos_id = new_id WHERE uos_id = old_id; + UPDATE account_invoice SET uos_id = new_id WHERE uos_id = old_id; """ cr.close() diff --git a/doc/migrate/3.4.0-4.0.0/pre-tiny.py b/doc/migrate/3.4.0-4.0.0/pre-tiny.py index fadcb2bc37e..c76e3520f5b 100644 --- a/doc/migrate/3.4.0-4.0.0/pre-tiny.py +++ b/doc/migrate/3.4.0-4.0.0/pre-tiny.py @@ -53,15 +53,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) # ----- @@ -79,18 +79,18 @@ cr = db.cursor() # ------------------------- # def change_column(cr, table, column, new_type, copy): - commands = [ - "ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column), - "ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type), - "ALTER TABLE %s DROP COLUMN temp_column" % table - ] - if copy: - commands.insert( - 2, - "UPDATE %s SET %s=temp_column::%s" % (table, column, new_type)) - - for command in commands: - cr.execute(command) + commands = [ + "ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column), + "ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type), + "ALTER TABLE %s DROP COLUMN temp_column" % table + ] + if copy: + commands.insert( + 2, + "UPDATE %s SET %s=temp_column::%s" % (table, column, new_type)) + + for command in commands: + cr.execute(command) #change_column(cr, 'crm_case', 'date_closed', 'timestamp', True) cr.commit() @@ -101,7 +101,7 @@ cr.commit() cr.execute("SELECT name FROM ir_module_module") if not cr.rowcount: - for module in set(['base', 'marketing', 'subscription', 'account', 'base_partner_relation', 'audittrail', 'account_followup', 'product', 'hr', 'l10n_simple', 'crm', 'stock', 'hr_timesheet', 'purchase', 'report_purchase', 'mrp', 'sale', 'report_sale', 'delivery', 'project', 'sale_crm', 'hr_timesheet_project', 'scrum', 'report_project', + for module in set(['base', 'marketing', 'subscription', 'account', 'base_partner_relation', 'audittrail', 'account_followup', 'product', 'hr', 'l10n_simple', 'crm', 'stock', 'hr_timesheet', 'purchase', 'report_purchase', 'mrp', 'sale', 'report_sale', 'delivery', 'project', 'sale_crm', 'hr_timesheet_project', 'scrum', 'report_project', 'account_followup', 'account', 'audittrail', @@ -131,8 +131,8 @@ if not cr.rowcount: 'sandwich', 'scrum', 'stock']): - cr.execute("INSERT INTO ir_module_module (name, state) VALUES ('%s', 'installed')" % module) - cr.commit() + cr.execute("INSERT INTO ir_module_module (name, state) VALUES ('%s', 'installed')" % module) + cr.commit() # ----------------------------------------------------- # @@ -140,11 +140,11 @@ if not cr.rowcount: # ----------------------------------------------------- # for line in ( - "ALTER TABLE ir_module_module ADD demo BOOLEAN DEFAULT False", - "delete from ir_values where value like '%,False'", - """UPDATE ir_ui_view set arch='<?xml version="1.0"?><tree string="Menu" toolbar="1"><field icon="icon" name="name"/></tree>' where name='ir.ui.menu.tree' and type='tree' and field_parent='child_id'""", - ): - cr.execute(line) + "ALTER TABLE ir_module_module ADD demo BOOLEAN DEFAULT False", + "delete from ir_values where value like '%,False'", + """UPDATE ir_ui_view set arch='<?xml version="1.0"?><tree string="Menu" toolbar="1"><field icon="icon" name="name"/></tree>' where name='ir.ui.menu.tree' and type='tree' and field_parent='child_id'""", + ): + cr.execute(line) cr.commit() cr.close() diff --git a/doc/migrate/3.4.0-4.0.0/pre.py b/doc/migrate/3.4.0-4.0.0/pre.py index 36cd4c17b97..ceb3683d1bc 100644 --- a/doc/migrate/3.4.0-4.0.0/pre.py +++ b/doc/migrate/3.4.0-4.0.0/pre.py @@ -53,15 +53,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) # ----- @@ -79,18 +79,18 @@ cr = db.cursor() # ------------------------- # def change_column(cr, table, column, new_type, copy): - commands = [ - "ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column), - "ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type), - "ALTER TABLE %s DROP COLUMN temp_column" % table - ] - if copy: - commands.insert( - 2, - "UPDATE %s SET %s=temp_column::%s" % (table, column, new_type)) - - for command in commands: - cr.execute(command) + commands = [ + "ALTER TABLE %s RENAME COLUMN %s TO temp_column" % (table, column), + "ALTER TABLE %s ADD COLUMN %s %s" % (table, column, new_type), + "ALTER TABLE %s DROP COLUMN temp_column" % table + ] + if copy: + commands.insert( + 2, + "UPDATE %s SET %s=temp_column::%s" % (table, column, new_type)) + + for command in commands: + cr.execute(command) change_column(cr, 'crm_case', 'date_closed', 'timestamp', True) cr.commit() @@ -101,19 +101,19 @@ cr.commit() cr.execute("SELECT name FROM ir_module_module") if not cr.rowcount: - for module in ('base', 'marketing', 'subscription', 'account', 'base_partner_relation', 'audittrail', 'account_followup', 'product', 'hr', 'l10n_simple', 'crm', 'stock', 'hr_timesheet', 'purchase', 'report_purchase', 'mrp', 'sale', 'report_sale', 'delivery', 'project', 'sale_crm', 'hr_timesheet_project', 'scrum', 'report_project'): - cr.execute("INSERT INTO ir_module_module (name, state) VALUES ('%s', 'installed')" % module) - cr.commit() + for module in ('base', 'marketing', 'subscription', 'account', 'base_partner_relation', 'audittrail', 'account_followup', 'product', 'hr', 'l10n_simple', 'crm', 'stock', 'hr_timesheet', 'purchase', 'report_purchase', 'mrp', 'sale', 'report_sale', 'delivery', 'project', 'sale_crm', 'hr_timesheet_project', 'scrum', 'report_project'): + cr.execute("INSERT INTO ir_module_module (name, state) VALUES ('%s', 'installed')" % module) + cr.commit() # --------------- # # remove old menu # # --------------- # while True: - cr.execute("select id from ir_ui_menu where id not in (select parent_id from ir_ui_menu where parent_id is not null) and id not in (select res_id from ir_model_data where model='ir.ui.menu')") - if not cr.rowcount: - break - cr.execute("delete from ir_ui_menu where id not in (select parent_id from ir_ui_menu where parent_id is not null) and id not in (select res_id from ir_model_data where model='ir.ui.menu')") + cr.execute("select id from ir_ui_menu where id not in (select parent_id from ir_ui_menu where parent_id is not null) and id not in (select res_id from ir_model_data where model='ir.ui.menu')") + if not cr.rowcount: + break + cr.execute("delete from ir_ui_menu where id not in (select parent_id from ir_ui_menu where parent_id is not null) and id not in (select res_id from ir_model_data where model='ir.ui.menu')") cr.commit() # ----------------------------------------------------- # @@ -121,12 +121,12 @@ cr.commit() # ----------------------------------------------------- # for line in ( - "ALTER TABLE ir_module_module ADD demo BOOLEAN", - "ALTER TABLE ir_module_module SET demo DEFAULT False", - "DELETE FROM ir_values WHERE VALUE LIKE '%,False'", - """UPDATE ir_ui_view set arch='<?xml version="1.0"?><tree string="Menu" toolbar="1"><field icon="icon" name="name"/></tree>' where name='ir.ui.menu.tree' and type='tree' and field_parent='child_id'""", - ): - cr.execute(line) + "ALTER TABLE ir_module_module ADD demo BOOLEAN", + "ALTER TABLE ir_module_module SET demo DEFAULT False", + "DELETE FROM ir_values WHERE VALUE LIKE '%,False'", + """UPDATE ir_ui_view set arch='<?xml version="1.0"?><tree string="Menu" toolbar="1"><field icon="icon" name="name"/></tree>' where name='ir.ui.menu.tree' and type='tree' and field_parent='child_id'""", + ): + cr.execute(line) cr.commit() cr.close() diff --git a/doc/migrate/4.0.0-4.2.0/pre.py b/doc/migrate/4.0.0-4.2.0/pre.py index cfa9b011aa9..e3f6d4905c3 100644 --- a/doc/migrate/4.0.0-4.2.0/pre.py +++ b/doc/migrate/4.0.0-4.2.0/pre.py @@ -54,15 +54,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) # ----- @@ -82,13 +82,13 @@ cr = db.cursor() cr.execute("""SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size FROM pg_class c,pg_attribute a,pg_type t WHERE c.relname='res_currency' AND a.attname='rounding' AND c.oid=a.attrelid AND a.atttypid=t.oid""") res = cr.dictfetchall() if res[0]['typname'] != 'numeric': - for line in ( - "ALTER TABLE res_currency RENAME rounding TO rounding_bak", - "ALTER TABLE res_currency ADD rounding NUMERIC(12,6)", - "UPDATE res_currency SET rounding = power(10, - rounding_bak)", - "ALTER TABLE res_currency DROP rounding_bak", - ): - cr.execute(line) + for line in ( + "ALTER TABLE res_currency RENAME rounding TO rounding_bak", + "ALTER TABLE res_currency ADD rounding NUMERIC(12,6)", + "UPDATE res_currency SET rounding = power(10, - rounding_bak)", + "ALTER TABLE res_currency DROP rounding_bak", + ): + cr.execute(line) cr.commit() # ----------------------------- # @@ -97,7 +97,7 @@ cr.commit() cr.execute('SELECT conname FROM pg_constraint where conname = \'ir_ui_view_type\'') if cr.fetchall(): - cr.execute('ALTER TABLE ir_ui_view DROP CONSTRAINT ir_ui_view_type') + cr.execute('ALTER TABLE ir_ui_view DROP CONSTRAINT ir_ui_view_type') cr.commit() # ------------------------ # @@ -106,7 +106,7 @@ cr.commit() cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a WHERE c.relname = \'res_partner_bank\' AND a.attname = \'iban\' AND c.oid = a.attrelid') if cr.fetchall(): - cr.execute('ALTER TABLE res_partner_bank RENAME iban TO acc_number') + cr.execute('ALTER TABLE res_partner_bank RENAME iban TO acc_number') cr.commit() # ------------------------------------------- # @@ -115,12 +115,12 @@ cr.commit() cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a WHERE c.relname = \'ir_model\' AND a.attname = \'perm_id\' AND c.oid = a.attrelid') if not cr.fetchall(): - cr.execute("ALTER TABLE ir_model ADD perm_id int references perm on delete set null") + cr.execute("ALTER TABLE ir_model ADD perm_id int references perm on delete set null") cr.commit() cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a WHERE c.relname = \'ir_model_fields\' AND a.attname = \'perm_id\' AND c.oid = a.attrelid') if not cr.fetchall(): - cr.execute("ALTER TABLE ir_model_fields ADD perm_id int references perm on delete set null") + cr.execute("ALTER TABLE ir_model_fields ADD perm_id int references perm on delete set null") cr.commit() @@ -138,9 +138,9 @@ cr.commit() cr.execute('SELECT model_id FROM ir_model_access') res= cr.fetchall() for r in res: - cr.execute('SELECT id FROM ir_model_access WHERE model_id = %d AND group_id IS NULL', (r[0],)) - if not cr.fetchall(): - cr.execute("INSERT into ir_model_access (name,model_id,group_id) VALUES ('Auto-generated access by migration',%d,NULL)",(r[0],)) + cr.execute('SELECT id FROM ir_model_access WHERE model_id = %d AND group_id IS NULL', (r[0],)) + if not cr.fetchall(): + cr.execute("INSERT into ir_model_access (name,model_id,group_id) VALUES ('Auto-generated access by migration',%d,NULL)",(r[0],)) cr.commit() # ------------------------------------------------- # @@ -149,7 +149,7 @@ cr.commit() cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_line_to_invoice\'') if cr.fetchall(): - cr.execute('DROP VIEW report_account_analytic_line_to_invoice') + cr.execute('DROP VIEW report_account_analytic_line_to_invoice') cr.commit() # --------------------------- # @@ -158,7 +158,7 @@ cr.commit() cr.execute('SELECT * FROM pg_class c, pg_attribute a WHERE c.relname=\'hr_employee\' AND a.attname=\'state\' AND c.oid=a.attrelid') if cr.fetchall(): - cr.execute('ALTER TABLE hr_employee DROP state') + cr.execute('ALTER TABLE hr_employee DROP state') cr.commit() # ------------ # @@ -167,10 +167,10 @@ cr.commit() cr.execute('SELECT id FROM ir_values where model=\'res.users\' AND key=\'meta\' AND name=\'tz\'') if not cr.fetchall(): - import pytz, pickle - meta = pickle.dumps({'type':'selection', 'string':'Timezone', 'selection': [(x, x) for x in pytz.all_timezones]}) - value = pickle.dumps(False) - cr.execute('INSERT INTO ir_values (name, key, model, meta, key2, object, value) VALUES (\'tz\', \'meta\', \'res.users\', %s, \'tz\', %s, %s)', (meta,False, value)) + import pytz, pickle + meta = pickle.dumps({'type':'selection', 'string':'Timezone', 'selection': [(x, x) for x in pytz.all_timezones]}) + value = pickle.dumps(False) + cr.execute('INSERT INTO ir_values (name, key, model, meta, key2, object, value) VALUES (\'tz\', \'meta\', \'res.users\', %s, \'tz\', %s, %s)', (meta,False, value)) cr.commit() # ------------------------- # @@ -179,36 +179,36 @@ cr.commit() cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a, pg_type t WHERE c.relname = \'product_uom\' AND a.attname = \'factor\' AND c.oid = a.attrelid AND a.atttypid = t.oid AND t.typname = \'float8\'') if cr.fetchall(): - cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat_account\'') - if cr.fetchall(): - cr.execute('DROP VIEW report_account_analytic_planning_stat_account') - cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat\'') - if cr.fetchall(): - cr.execute('DROP VIEW report_account_analytic_planning_stat') - cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat_user\'') - if cr.fetchall(): - cr.execute('DROP VIEW report_account_analytic_planning_stat_user') - cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_product\'') - if cr.fetchall(): - cr.execute('DROP VIEW report_purchase_order_product') - cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_category\'') - if cr.fetchall(): - cr.execute('DROP VIEW report_purchase_order_category') - cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_product\'') - if cr.fetchall(): - cr.execute('DROP VIEW report_sale_order_product') - cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_category\'') - if cr.fetchall(): - cr.execute('DROP VIEW report_sale_order_category') - cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_hr_timesheet_invoice_journal\'') - if cr.fetchall(): - cr.execute('DROP VIEW report_hr_timesheet_invoice_journal') - - cr.execute('ALTER TABLE product_uom RENAME COLUMN factor to temp_column') - cr.execute('ALTER TABLE product_uom ADD COLUMN factor NUMERIC(12,6)') - cr.execute('UPDATE product_uom SET factor = temp_column') - cr.execute('ALTER TABLE product_uom ALTER factor SET NOT NULL') - cr.execute('ALTER TABLE product_uom DROP COLUMN temp_column') + cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat_account\'') + if cr.fetchall(): + cr.execute('DROP VIEW report_account_analytic_planning_stat_account') + cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat\'') + if cr.fetchall(): + cr.execute('DROP VIEW report_account_analytic_planning_stat') + cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat_user\'') + if cr.fetchall(): + cr.execute('DROP VIEW report_account_analytic_planning_stat_user') + cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_product\'') + if cr.fetchall(): + cr.execute('DROP VIEW report_purchase_order_product') + cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_category\'') + if cr.fetchall(): + cr.execute('DROP VIEW report_purchase_order_category') + cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_product\'') + if cr.fetchall(): + cr.execute('DROP VIEW report_sale_order_product') + cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_category\'') + if cr.fetchall(): + cr.execute('DROP VIEW report_sale_order_category') + cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_hr_timesheet_invoice_journal\'') + if cr.fetchall(): + cr.execute('DROP VIEW report_hr_timesheet_invoice_journal') + + cr.execute('ALTER TABLE product_uom RENAME COLUMN factor to temp_column') + cr.execute('ALTER TABLE product_uom ADD COLUMN factor NUMERIC(12,6)') + cr.execute('UPDATE product_uom SET factor = temp_column') + cr.execute('ALTER TABLE product_uom ALTER factor SET NOT NULL') + cr.execute('ALTER TABLE product_uom DROP COLUMN temp_column') cr.commit() @@ -218,7 +218,7 @@ cr.commit() cr.execute('SELECT conname FROM pg_constraint where conname = \'stock_production_lot_name_uniq\'') if cr.fetchall(): - cr.execute('ALTER TABLE stock_production_lot DROP CONSTRAINT stock_production_lot_name_uniq') + cr.execute('ALTER TABLE stock_production_lot DROP CONSTRAINT stock_production_lot_name_uniq') cr.commit() # ------------------------------------ # @@ -235,19 +235,19 @@ cr.commit() cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_report_xml_pkey\' and tablename = \'ir_act_report_xml\'') if not cr.fetchall(): - cr.execute('ALTER TABLE ir_act_report_xml ADD PRIMARY KEY (id)') + cr.execute('ALTER TABLE ir_act_report_xml ADD PRIMARY KEY (id)') cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_report_custom_pkey\' and tablename = \'ir_act_report_custom\'') if not cr.fetchall(): - cr.execute('ALTER TABLE ir_act_report_custom ADD PRIMARY KEY (id)') + cr.execute('ALTER TABLE ir_act_report_custom ADD PRIMARY KEY (id)') cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_group_pkey\' and tablename = \'ir_act_group\'') if not cr.fetchall(): - cr.execute('ALTER TABLE ir_act_group ADD PRIMARY KEY (id)') + cr.execute('ALTER TABLE ir_act_group ADD PRIMARY KEY (id)') cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_execute_pkey\' and tablename = \'ir_act_execute\'') if not cr.fetchall(): - cr.execute('ALTER TABLE ir_act_execute ADD PRIMARY KEY (id)') + cr.execute('ALTER TABLE ir_act_execute ADD PRIMARY KEY (id)') cr.execute('SELECT indexname FROm pg_indexes WHERE indexname = \'ir_act_wizard_pkey\' and tablename = \'ir_act_wizard\'') if not cr.fetchall(): - cr.execute('ALTER TABLE ir_act_wizard ADD PRIMARY KEY (id)') + cr.execute('ALTER TABLE ir_act_wizard ADD PRIMARY KEY (id)') cr.commit() cr.close diff --git a/doc/migrate/4.0.0-4.2.0/tiny/pre-tiny.py b/doc/migrate/4.0.0-4.2.0/tiny/pre-tiny.py index c341461ab81..b63fff6dda6 100644 --- a/doc/migrate/4.0.0-4.2.0/tiny/pre-tiny.py +++ b/doc/migrate/4.0.0-4.2.0/tiny/pre-tiny.py @@ -54,15 +54,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) raise Exception('This script is provided as an example, you must custom it before') @@ -84,18 +84,18 @@ cr.execute('SELECT code from res_country where code is not null group by code') res = cr.fetchall() for c in res: - cr.execute('SELECT max(id) from res_country where code = %s group by code', (c[0],)) - res2 = cr.fetchone() - cr.execute('SELECT id from res_country where code = %s', (c[0],)) - ids = ','.join(map(lambda x: str(x[0]), cr.fetchall())) - cr.execute('UPDATE res_partner_address set country_id = %d where country_id in ('+ids+')', (res2[0],)) - cr.execute('DELETE FROM res_country WHERE code = %s and id <> %d', (c[0], res2[0],)) + cr.execute('SELECT max(id) from res_country where code = %s group by code', (c[0],)) + res2 = cr.fetchone() + cr.execute('SELECT id from res_country where code = %s', (c[0],)) + ids = ','.join(map(lambda x: str(x[0]), cr.fetchall())) + cr.execute('UPDATE res_partner_address set country_id = %d where country_id in ('+ids+')', (res2[0],)) + cr.execute('DELETE FROM res_country WHERE code = %s and id <> %d', (c[0], res2[0],)) cr.commit() cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_planning_stat\'') if cr.fetchall(): - cr.execute('DROP VIEW report_account_analytic_planning_stat') + cr.execute('DROP VIEW report_account_analytic_planning_stat') cr.commit() @@ -104,59 +104,59 @@ res = cr.fetchall() for p in res: - cr.execute('SELECT max(id) FROM res_partner WHERE name = %s GROUP BY name', (p[0],)) - res2 = cr.fetchone() - cr.execute('UPDATE res_partner set active = False WHERE name = %s and id <> %d', (p[0], res2[0],)) - cr.execute('SELECT id FROM res_partner WHERE name = %s AND id <> %d', (p[0], res2[0],)) - res3 = cr.fetchall() - i = 0 - for id in res3: - name = p[0]+' old' - if i: - name = name + ' ' + str(i) - cr.execute('UPDATE res_partner set name = %s WHERE id = %d', (name, id[0])) - i += 1 + cr.execute('SELECT max(id) FROM res_partner WHERE name = %s GROUP BY name', (p[0],)) + res2 = cr.fetchone() + cr.execute('UPDATE res_partner set active = False WHERE name = %s and id <> %d', (p[0], res2[0],)) + cr.execute('SELECT id FROM res_partner WHERE name = %s AND id <> %d', (p[0], res2[0],)) + res3 = cr.fetchall() + i = 0 + for id in res3: + name = p[0]+' old' + if i: + name = name + ' ' + str(i) + cr.execute('UPDATE res_partner set name = %s WHERE id = %d', (name, id[0])) + i += 1 cr.commit() cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_account_analytic_line_to_invoice\'') if cr.fetchall(): - cr.execute('DROP VIEW report_account_analytic_line_to_invoice') + cr.execute('DROP VIEW report_account_analytic_line_to_invoice') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_timesheet_invoice\'') if cr.fetchall(): - cr.execute('drop VIEW report_timesheet_invoice') + cr.execute('drop VIEW report_timesheet_invoice') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_category\'') if cr.fetchall(): - cr.execute('drop VIEW report_purchase_order_category') + cr.execute('drop VIEW report_purchase_order_category') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_purchase_order_product\'') if cr.fetchall(): - cr.execute('drop VIEW report_purchase_order_product') + cr.execute('drop VIEW report_purchase_order_product') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_category\'') if cr.fetchall(): - cr.execute('drop VIEW report_sale_order_category') + cr.execute('drop VIEW report_sale_order_category') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_sale_order_product\'') if cr.fetchall(): - cr.execute('drop VIEW report_sale_order_product') + cr.execute('drop VIEW report_sale_order_product') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_timesheet_user\'') if cr.fetchall(): - cr.execute('drop VIEW report_timesheet_user') + cr.execute('drop VIEW report_timesheet_user') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'report_task_user_pipeline_open\'') if cr.fetchall(): - cr.execute('drop VIEW report_task_user_pipeline_open') + cr.execute('drop VIEW report_task_user_pipeline_open') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'hr_timesheet_sheet_sheet_day\'') if cr.fetchall(): - cr.execute('drop VIEW hr_timesheet_sheet_sheet_day') + cr.execute('drop VIEW hr_timesheet_sheet_sheet_day') cr.execute('SELECT viewname FROM pg_views WHERE viewname = \'hr_timesheet_sheet_sheet_account\'') if cr.fetchall(): - cr.execute('drop VIEW hr_timesheet_sheet_sheet_account') + cr.execute('drop VIEW hr_timesheet_sheet_sheet_account') cr.execute('SELECT viewname from pg_views where viewname = \'sale_journal_sale_stats\'') if cr.fetchall(): - cr.execute('drop VIEW sale_journal_sale_stats') + cr.execute('drop VIEW sale_journal_sale_stats') cr.execute('SELECT viewname from pg_views where viewname = \'sale_journal_picking_stats\'') if cr.fetchall(): - cr.execute('drop VIEW sale_journal_picking_stats') + cr.execute('drop VIEW sale_journal_picking_stats') cr.execute('SELECT viewname from pg_views where viewname = \'sale_journal_invoice_type_stats\'') if cr.fetchall(): - cr.execute('drop VIEW sale_journal_invoice_type_stats') + cr.execute('drop VIEW sale_journal_invoice_type_stats') cr.execute('ALTER TABLE product_template ALTER list_price TYPE numeric(16,2)') cr.execute('ALTER TABLE product_template ALTER standard_price TYPE numeric(16,2)') @@ -171,13 +171,13 @@ cr.commit() cr.execute('SELECT tablename FROM pg_tables WHERE tablename = \'subscription_document_fields\'') if cr.fetchall(): - cr.execute('DROP TABLE subscription_document_fields') + cr.execute('DROP TABLE subscription_document_fields') cr.execute('SELECT tablename FROM pg_tables WHERE tablename = \'subscription_document\'') if cr.fetchall(): - cr.execute('DROP TABLE subscription_document') + cr.execute('DROP TABLE subscription_document') cr.execute('SELECT tablename FROM pg_tables WHERE tablename = \'subscription_subscription_history\'') if cr.fetchall(): - cr.execute('DROP TABLE subscription_subscription_history') + cr.execute('DROP TABLE subscription_subscription_history') cr.commit() # -------------------- # @@ -186,9 +186,9 @@ cr.commit() cr.execute('SELECT a.attname FROM pg_class c, pg_attribute a WHERE c.relname = \'res_currency_rate\' AND a.attname = \'rate_old\' AND c.oid = a.attrelid') if not cr.fetchall(): - cr.execute('ALTER TABLE res_currency_rate ADD rate_old NUMERIC(12,6)') - cr.execute('UPDATE res_currency_rate SET rate_old = rate') - cr.execute('UPDATE res_currency_rate SET rate = (1 / rate_old)') + cr.execute('ALTER TABLE res_currency_rate ADD rate_old NUMERIC(12,6)') + cr.execute('UPDATE res_currency_rate SET rate_old = rate') + cr.execute('UPDATE res_currency_rate SET rate = (1 / rate_old)') cr.commit() cr.close diff --git a/doc/migrate/4.2.0-4.4.0/pre.py b/doc/migrate/4.2.0-4.4.0/pre.py index ac8ed0a6017..8d0ad4243a0 100644 --- a/doc/migrate/4.2.0-4.4.0/pre.py +++ b/doc/migrate/4.2.0-4.4.0/pre.py @@ -54,15 +54,15 @@ options.db_name = 'terp' # default value parser.parse_args(values=options) if hasattr(options, 'config'): - configparser = ConfigParser.ConfigParser() - configparser.read([options.config]) - for name, value in configparser.items('options'): - if not (hasattr(options, name) and getattr(options, name)): - if value in ('true', 'True'): - value = True - if value in ('false', 'False'): - value = False - setattr(options, name, value) + configparser = ConfigParser.ConfigParser() + configparser.read([options.config]) + for name, value in configparser.items('options'): + if not (hasattr(options, name) and getattr(options, name)): + if value in ('true', 'True'): + value = True + if value in ('false', 'False'): + value = False + setattr(options, name, value) # ----- @@ -80,8 +80,8 @@ cr = db.cursor() # ------------------------------ # cr.execute('ALTER TABLE ir_attachment \ - ALTER COLUMN res_model DROP NOT NULL, \ - ALTER COLUMN res_id DROP NOT NULL') + ALTER COLUMN res_model DROP NOT NULL, \ + ALTER COLUMN res_id DROP NOT NULL') cr.commit() # ---------------------------------- # @@ -96,13 +96,13 @@ a.attname='date_deadline' AND c.oid=a.attrelid AND a.atttypid=t.oid""") res = cr.dictfetchall() if res[0]['typname'] != 'timestamp': - for line in ( - "ALTER TABLE crm_case RENAME date_deadline TO date_deadline_bak", - "ALTER TABLE crm_case ADD date_deadline timestamp", - "UPDATE crm_case SET date_deadline = date_deadline_bak", - "ALTER TABLE crm_case DROP date_deadline_bak", - ): - cr.execute(line) + for line in ( + "ALTER TABLE crm_case RENAME date_deadline TO date_deadline_bak", + "ALTER TABLE crm_case ADD date_deadline timestamp", + "UPDATE crm_case SET date_deadline = date_deadline_bak", + "ALTER TABLE crm_case DROP date_deadline_bak", + ): + cr.execute(line) cr.commit() cr.execute('drop view report_task_user_pipeline_open'); diff --git a/doc/tests/check_profile_l10n_all.py b/doc/tests/check_profile_l10n_all.py index 4404913fc80..d53d911adb8 100755..100644 --- a/doc/tests/check_profile_l10n_all.py +++ b/doc/tests/check_profile_l10n_all.py @@ -40,15 +40,15 @@ import base64 url = 'http://localhost:8069/xmlrpc' profiles = [ - 'profile_accounting', - 'profile_service', - 'profile_manufacturing' + 'profile_accounting', + 'profile_service', + 'profile_manufacturing' ] l10n_charts = [ - 'l10n_chart_uk_minimal', - False, - 'l10n_be', - 'l10n_fr' + 'l10n_chart_uk_minimal', + False, + 'l10n_be', + 'l10n_fr' ] dbname = 'test' admin_passwd = 'admin' @@ -64,62 +64,62 @@ demos = [True] langs = lang or (map(lambda x: x[0], sock2.list_lang()) + ['en_US']) def wait(id): - progress=0.0 - while not progress==1.0: - time.sleep(3) - progress,users = sock2.get_progress(admin_passwd, id) - time.sleep(3) - return True + progress=0.0 + while not progress==1.0: + time.sleep(3) + progress,users = sock2.get_progress(admin_passwd, id) + time.sleep(3) + return True def wizard_run(wizname, fieldvalues={}, endstate='end'): - wiz_id = sock4.create(dbname, uid, 'admin', wizname) - state = 'init' - datas = {'form':{}} - while state!=endstate: - res = sock4.execute(dbname, uid, 'admin', wiz_id, datas, state, {}) - if 'datas' in res: - datas['form'].update( res['datas'] ) - if res['type']=='form': - for field in res['fields'].keys(): - datas['form'][field] = res['fields'][field].get('value', False) - state = res['state'][-1][0] - datas['form'].update(fieldvalues) - elif res['type']=='state': - state = res['state'] - return True + wiz_id = sock4.create(dbname, uid, 'admin', wizname) + state = 'init' + datas = {'form':{}} + while state!=endstate: + res = sock4.execute(dbname, uid, 'admin', wiz_id, datas, state, {}) + if 'datas' in res: + datas['form'].update( res['datas'] ) + if res['type']=='form': + for field in res['fields'].keys(): + datas['form'][field] = res['fields'][field].get('value', False) + state = res['state'][-1][0] + datas['form'].update(fieldvalues) + elif res['type']=='state': + state = res['state'] + return True for demo in demos: - for l10n in l10n_charts: - print 'Testing localisation', l10n, '...' - for prof in profiles: - print '\tTesting profile', prof, '...' - id = sock2.create(admin_passwd, dbname, demo, lang) - wait(id) - uid = sock3.login(dbname, 'admin','admin') + for l10n in l10n_charts: + print 'Testing localisation', l10n, '...' + for prof in profiles: + print '\tTesting profile', prof, '...' + id = sock2.create(admin_passwd, dbname, demo, lang) + wait(id) + uid = sock3.login(dbname, 'admin','admin') - idprof = sock.execute(dbname, uid, 'admin', 'ir.module.module', 'search', [('name','=',prof)]) - if l10n: - idl10n = sock.execute(dbname, uid, 'admin', 'ir.module.module', 'search', [('name','=',l10n)]) - else: - idl10n = [-1] - wizard_run('base_setup.base_setup', { - 'profile': idprof[0], - 'charts': idl10n[0], - }, 'menu') - for lang in langs: - print '\t\tTesting Language', lang, '...' - wizard_run('module.lang.install', {'lang': lang}) + idprof = sock.execute(dbname, uid, 'admin', 'ir.module.module', 'search', [('name','=',prof)]) + if l10n: + idl10n = sock.execute(dbname, uid, 'admin', 'ir.module.module', 'search', [('name','=',l10n)]) + else: + idl10n = [-1] + wizard_run('base_setup.base_setup', { + 'profile': idprof[0], + 'charts': idl10n[0], + }, 'menu') + for lang in langs: + print '\t\tTesting Language', lang, '...' + wizard_run('module.lang.install', {'lang': lang}) - ok = False - range = 4 - while (not ok) and range: - try: - time.sleep(2) - id = sock2.drop(admin_passwd, dbname) - ok = True - except: - range -= 1 - time.sleep(2) + ok = False + range = 4 + while (not ok) and range: + try: + time.sleep(2) + id = sock2.drop(admin_passwd, dbname) + ok = True + except: + range -= 1 + time.sleep(2) diff --git a/setup.py b/setup.py index 2c8923f4f3b..2c8923f4f3b 100755..100644 --- a/setup.py +++ b/setup.py diff --git a/win32/TinyERPServerService.py b/win32/TinyERPServerService.py index b39c0c6701a..ca07917ac94 100644 --- a/win32/TinyERPServerService.py +++ b/win32/TinyERPServerService.py @@ -72,15 +72,15 @@ class TinyERPServerService(win32serviceutil.ServiceFramework): def StartTERP(self):
- # The server finds now its configuration automatically on Windows
- # We start the ERP Server as an independent process, but we keep its handle
- # The server's binary must be one directory above the service's binary (when py2exe'd the python libraries shouldn' mix)
- service_dir = os.path.dirname(sys.argv[0])
- server_dir = os.path.split(service_dir)[0]
- server_path = os.path.join(server_dir, 'tinyerp-server.exe')
- self.terpprocess = subprocess.Popen([server_path], \
+ # The server finds now its configuration automatically on Windows
+ # We start the ERP Server as an independent process, but we keep its handle
+ # The server's binary must be one directory above the service's binary (when py2exe'd the python libraries shouldn' mix)
+ service_dir = os.path.dirname(sys.argv[0])
+ server_dir = os.path.split(service_dir)[0]
+ server_path = os.path.join(server_dir, 'tinyerp-server.exe')
+ self.terpprocess = subprocess.Popen([server_path], \
cwd=server_dir,
- creationflags=win32process.CREATE_NO_WINDOW)
+ creationflags=win32process.CREATE_NO_WINDOW)
def StartControl(self,ws):
@@ -89,15 +89,15 @@ class TinyERPServerService(win32serviceutil.ServiceFramework): self.stopping = True
def SvcDoRun(self):
- # Start Tiny ERP Server itself
+ # Start Tiny ERP Server itself
self.StartTERP()
# start the loop waiting for the Service Manager's stop signal
- thread.start_new_thread(self.StartControl, (self.hWaitStop,))
- # Log a info message that the server is running
- servicemanager.LogInfoMsg("Tiny ERP Server up and running")
- # verification if the server is really running, else quit with an error
+ thread.start_new_thread(self.StartControl, (self.hWaitStop,))
+ # Log a info message that the server is running
+ servicemanager.LogInfoMsg("Tiny ERP Server up and running")
+ # verification if the server is really running, else quit with an error
self.terpprocess.wait()
- if not self.stopping:
+ if not self.stopping:
sys.exit("Tiny ERP Server check: server not running, check the logfile for more info")
|
