[MERGE] merge with trunk-server

bzr revid: ysa@tinyerp.co.in-20100408093736-996mqw3c8klknerc
This commit is contained in:
Ysa (Open ERP)
2010-04-08 15:07:36 +05:30
27 changed files with 835 additions and 329 deletions
+78 -32
View File
@@ -306,7 +306,10 @@ def load_information_from_description_file(module):
if os.path.isfile(description_file):
return eval(tools.file_open(description_file).read())
logging.warning('The module %s does not contain a description file: __openerp__.py or __terp__.py (deprecated)' % module)
#TODO: refactor the logger in this file to follow the logging guidelines
# for 6.0
logging.getLogger('addons').debug('The module %s does not contain a description file:'\
'__openerp__.py or __terp__.py (deprecated)', module)
return {}
def get_modules_with_version():
@@ -589,8 +592,75 @@ class MigrationManager(object):
if mod:
del mod
log = logging.getLogger('init')
def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
def process_sql_file(cr, file):
queries = fp.read().split(';')
for query in queries:
new_query = ' '.join(query.split())
if new_query:
cr.execute(new_query)
def load_init_update_xml(cr, m, idref, mode, kind):
for filename in package.data.get('%s_xml' % kind, []):
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
_, ext = os.path.splitext(filename)
fp = tools.file_open(opj(m, filename))
if ext == '.csv':
noupdate = (kind == 'init')
tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate)
elif ext == '.sql':
process_sql_file(cr, fp)
elif ext == '.yml':
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
else:
tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
fp.close()
def load_demo_xml(cr, m, idref, mode):
for xml in package.data.get('demo_xml', []):
name, ext = os.path.splitext(xml)
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
fp = tools.file_open(opj(m, xml))
if ext == '.csv':
tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True)
elif ext == '.yml':
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
else:
tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
fp.close()
def load_data(cr, module_name, id_map, mode):
_load_data(cr, module_name, id_map, mode, 'data')
def load_demo(cr, module_name, id_map, mode):
_load_data(cr, module_name, id_map, mode, 'demo')
def load_test(cr, module_name, id_map, mode):
cr.commit()
try:
_load_data(cr, module_name, id_map, mode, 'test')
finally:
cr.rollback()
def _load_data(cr, module_name, id_map, mode, kind):
noupdate = (kind == 'demo')
for filename in package.data.get(kind, []):
_, ext = os.path.splitext(filename)
log.info("module %s: loading %s", module_name, filename)
pathname = os.path.join(module_name, filename)
file = tools.file_open(pathname)
# TODO manage .csv file with noupdate == (kind == 'init')
if ext == '.sql':
process_sql_file(cr, fp)
elif ext == '.yml':
tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate)
else:
tools.convert_xml_import(cr, module_name, file, id_map, mode, noupdate)
file.close()
# **kwargs is passed directly to convert_xml_import
if not status:
status = {}
@@ -647,40 +717,16 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
'license': package.data.get('license', 'GPL-2'),
'certificate': package.data.get('certificate') or None,
})
for filename in package.data.get('%s_xml' % kind, []):
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
name, ext = os.path.splitext(filename)
fp = tools.file_open(opj(m, filename))
if ext == '.csv':
noupdate=False
if kind == 'init':
noupdate=True
tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate)
elif ext == '.sql':
queries = fp.read().split(';')
for query in queries:
new_query = ' '.join(query.split())
if new_query:
cr.execute(new_query)
elif ext == '.yml':
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
else:
tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
fp.close()
load_init_update_xml(cr, m, idref, mode, kind)
load_data(cr, m, idref, mode)
if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'):
status['progress'] = (float(statusi)+0.75) / len(graph)
for xml in package.data.get('demo_xml', []):
name, ext = os.path.splitext(xml)
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
fp = tools.file_open(opj(m, xml))
if ext == '.csv':
tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True)
elif ext == '.yml':
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
else:
tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
fp.close()
load_demo_xml(cr, m, idref, mode)
load_demo(cr, m, idref, mode)
cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid))
load_test(cr, m, idref, mode)
package_todo.append(package.name)
migrations.migrate_module(package, 'post')
+6 -4
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
#
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
@@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
@@ -34,7 +34,8 @@
'security/base_security.xml',
'res/res_security.xml',
'res/res_config.xml',
'maintenance/maintenance_security.xml'
'maintenance/maintenance_security.xml',
'data/res.country.state.csv'
],
'update_xml': [
'base_update.xml',
@@ -61,8 +62,9 @@
'security/base_security.xml',
'maintenance/maintenance_view.xml',
'security/ir.model.access.csv'
],
'demo_xml': ['base_demo.xml', 'res/partner/partner_demo.xml', 'res/partner/crm_demo.xml'],
'demo_xml': ['base_demo.xml', 'res/partner/partner_demo.xml', 'res/partner/crm_demo.xml','test/base_test.xml'],
'installable': True,
'active': True,
'certificate': '0076807797149',
@@ -0,0 +1,52 @@
"country_id:id","name","code"
us,"Alabama","AL"
us,"Alaska","AK"
us,"Arizona","AZ"
us,"Arkansas","AR"
us,"California","CA"
us,"Colorado","CO"
us,"Connecticut","CT"
us,"Delaware","DE"
us,"District of Columbia","DC"
us,"Florida","FL"
us,"Georgia","GA"
us,"Hawaii","HI"
us,"Idaho","ID"
us,"Illinois","IL"
us,"Indiana","IN"
us,"Iowa","IA"
us,"Kansas","KS"
us,"Kentucky","KY"
us,"Louisiana","LA"
us,"Maine","ME"
us,"Montana","MT"
us,"Nebraska","NE"
us,"Nevada","NV"
us,"New Hampshire","NH"
us,"New Jersey","NJ"
us,"New Mexico","NM"
us,"New York","NY"
us,"North Carolina","NC"
us,"North Dakota","ND"
us,"Ohio","OH"
us,"Oklahoma","OK"
us,"Oregon","OR"
us,"Maryland","MD"
us,"Massachusetts","MA"
us,"Michigan","MI"
us,"Minnesota","MN"
us,"Mississippi","MS"
us,"Missouri","MO"
us,"Pennsylvania","PA"
us,"Rhode Island","RI"
us,"South Carolina","SC"
us,"South Dakota","SD"
us,"Tennessee","TN"
us,"Texas","TX"
us,"Utah","UT"
us,"Vermont","VT"
us,"Virginia","VA"
us,"Washington","WA"
us,"West Virginia","WV"
us,"Wisconsin","WI"
us,"Wyoming","WY"
1 country_id:id name code
2 us Alabama AL
3 us Alaska AK
4 us Arizona AZ
5 us Arkansas AR
6 us California CA
7 us Colorado CO
8 us Connecticut CT
9 us Delaware DE
10 us District of Columbia DC
11 us Florida FL
12 us Georgia GA
13 us Hawaii HI
14 us Idaho ID
15 us Illinois IL
16 us Indiana IN
17 us Iowa IA
18 us Kansas KS
19 us Kentucky KY
20 us Louisiana LA
21 us Maine ME
22 us Montana MT
23 us Nebraska NE
24 us Nevada NV
25 us New Hampshire NH
26 us New Jersey NJ
27 us New Mexico NM
28 us New York NY
29 us North Carolina NC
30 us North Dakota ND
31 us Ohio OH
32 us Oklahoma OK
33 us Oregon OR
34 us Maryland MD
35 us Massachusetts MA
36 us Michigan MI
37 us Minnesota MN
38 us Mississippi MS
39 us Missouri MO
40 us Pennsylvania PA
41 us Rhode Island RI
42 us South Carolina SC
43 us South Dakota SD
44 us Tennessee TN
45 us Texas TX
46 us Utah UT
47 us Vermont VT
48 us Virginia VA
49 us Washington WA
50 us West Virginia WV
51 us Wisconsin WI
52 us Wyoming WY
+1 -1
View File
@@ -14,7 +14,7 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n"
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: base
+2 -2
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
"PO-Revision-Date: 2010-04-05 04:33+0000\n"
"PO-Revision-Date: 2010-04-07 04:18+0000\n"
"Last-Translator: goranc <goranc@gmail.com>\n"
"Language-Team: openerp-translators\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n"
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
"Language: hr\n"
+2 -2
View File
@@ -8,13 +8,13 @@ msgstr ""
"Project-Id-Version: openobject-server\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
"PO-Revision-Date: 2010-04-05 04:34+0000\n"
"PO-Revision-Date: 2010-04-07 04:14+0000\n"
"Last-Translator: Harry (Open ERP) <hmo@tinyerp.com>\n"
"Language-Team: Japanese <ja@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n"
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: base
+2 -2
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
"PO-Revision-Date: 2010-04-05 04:37+0000\n"
"PO-Revision-Date: 2010-04-07 04:19+0000\n"
"Last-Translator: Anders Wallenquist <anders.wallenquist@vertel.se>\n"
"Language-Team: <>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n"
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: base
+2 -2
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.0\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
"PO-Revision-Date: 2010-04-05 04:31+0000\n"
"PO-Revision-Date: 2010-04-07 04:12+0000\n"
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n"
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: base
+2 -2
View File
@@ -7,13 +7,13 @@ msgstr ""
"Project-Id-Version: OpenERP Server 5.0.4\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
"PO-Revision-Date: 2010-04-05 04:30+0000\n"
"PO-Revision-Date: 2010-04-07 04:21+0000\n"
"Last-Translator: OpenERP Administrators <Unknown>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n"
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
"X-Generator: Launchpad (build Unknown)\n"
#. module: base
+4 -4
View File
@@ -548,10 +548,10 @@ class ir_model_data(osv.osv):
table.replace('.', '_'))] = (table, inherit_id)
return res_id
def _unlink(self, cr, uid, model, ids, direct=False):
for id in ids:
self.unlink_mark[(model, id)]=False
cr.execute('delete from ir_model_data where res_id=%s and model=%s', (id, model))
def _unlink(self, cr, uid, model, res_ids):
for res_id in res_ids:
self.unlink_mark[(model, res_id)] = False
cr.execute('delete from ir_model_data where res_id=%s and model=%s', (res_id, model))
return True
def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False):
+9 -12
View File
@@ -73,18 +73,15 @@ class ir_sequence(osv.osv):
}
def get_id(self, cr, uid, sequence_id, test='id', context=None):
try:
assert test in ('code','id')
cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+'=%s AND active=%s FOR UPDATE', (sequence_id, True))
res = cr.dictfetchone()
if res:
cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True))
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'])
finally:
cr.commit()
assert test in ('code','id')
cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+'=%s AND active=%s FOR UPDATE NOWAIT', (sequence_id, True))
res = cr.dictfetchone()
if res:
cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True))
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):
+5 -3
View File
@@ -50,10 +50,12 @@
<filter icon="terp-sale" string="Uninstalled" domain="[('state', 'in', ['uninstalled', 'uninstallable'])]"/>
<filter icon="terp-sale" string="To be upgraded" domain="[('state','in', ['to upgrade', 'to remove', 'to install'])]"/>
<separator orientation="vertical"/>
<field name="name" select="1"/>
<field name="state" readonly="1" select="1"/>
<filter icon="terp-sale" string="Certified" domain="[('certificate','&lt;&gt;', False)]"/>
<separator orientation="vertical"/>
<field name="name" select="1"/>
<field name="state" readonly="1" select="1"/>
</group>
</group>
</search>
</field>
+1 -1
View File
@@ -50,9 +50,9 @@
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="State">
<field name="country_id"/>
<field name="name"/>
<field name="code"/>
<field name="country_id"/>
</tree>
</field>
</record>
+1 -1
View File
@@ -136,7 +136,7 @@ class res_partner(osv.osv):
'child_ids': fields.one2many('res.partner', 'parent_id', 'Partner Ref.'),
'ref': fields.char('Code', size=64),
'lang': fields.selection(_lang_get, 'Language', size=5, help="If the selected language is loaded in the system, all documents related to this partner will be printed in this language. If not, it will be english."),
'user_id': fields.many2one('res.users', 'Dedicated Salesman', help='The internal user that is in charge of communicating with this partner if any.'),
'user_id': fields.many2one('res.users', 'Salesman', help='The internal user that is in charge of communicating with this partner if any.'),
'vat': fields.char('VAT',size=32 ,help="Value Added Tax number. Check the box if the partner is subjected to the VAT. Used by the VAT legal statement."),
'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'),
'website': fields.char('Website',size=64),
@@ -60,6 +60,15 @@
<field name="name">country_id</field>
<field eval="True" name="readonly"/>
<field name="bank_type_id" ref="bank_normal"/>
</record>
<!--
Resource: res.request.link
-->
<record id="req_link_partner" model="res.request.link">
<field name="name">Partner</field>
<field name="object">res.partner</field>
</record>
</data>
</openerp>
+18 -9
View File
@@ -4,7 +4,7 @@
<!--
Resource: res.partner.category
-->
<record id="res_partner_category_0" model="res.partner.category">
<field name="name">Customer</field>
</record>
@@ -43,7 +43,7 @@
<field name="name">Bad customers</field>
<field name="parent_id" ref="res_partner_category_12"/>
</record>
<record id="res_partner_category_8" model="res.partner.category">
<field name="name">Supplier</field>
</record>
@@ -59,12 +59,12 @@
<field name="name">Textile Suppliers</field>
<field name="parent_id" ref="res_partner_category_8"/>
</record>
<!--
Resource: res.partner
-->
<record id="res_partner_asus" model="res.partner">
<field name="name">ASUStek</field>
<field name="user_id" ref="user_demo"/>
@@ -177,12 +177,12 @@
<record id="res_partner_accent" model="res.partner">
<field name="name">Université de Liège</field>
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
</record>
</record>
<!--
Resource: res.partner.address
-->
<record id="res_partner_address_1" model="res.partner.address">
<field name="city">Bruxelles</field>
<field name="name">Michel Schumacher</field>
@@ -349,6 +349,15 @@
<field name="street">Université de Liège</field>
<field name="partner_id" ref="res_partner_accent"/>
</record>
<!--
Resource: res.request
-->
<record id="res_partner_asustek_request" model="res.request">
<field name="name">ASUSTEK-Subject</field>
<field name="act_to" ref="user_demo"/>
<field name="ref_doc1">res.partner,2</field>
</record>
</data>
</openerp>
+1 -2
View File
@@ -329,7 +329,6 @@
<field colspan="4" name="events" nolabel="1" widget="one2many_list"/>
</page>
<page string="Notes">
<separator string="Description" colspan="4"/>
<field colspan="4" name="comment" nolabel="1"/>
</page>
</notebook>
@@ -344,7 +343,7 @@
<field name="arch" type="xml">
<search string="Search Partner">
<group col='10' colspan='4'>
<filter string="My Partners" icon="terp-partner" domain="[('parent_id','!=',False)]"/>
<filter string="My Partners" icon="terp-partner" domain="[('user_id','=',uid)]"/>
<separator orientation="vertical"/>
<filter string="Customers" icon="terp-partner" domain="[('customer','=',1)]" help="Customer Partners"/>
<filter string="Suppliers" icon="terp-partner" domain="[('supplier','=',1)]" help="Supplier Partners"
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!--
Assert:
-->
<assert model="res.request" id="res_partner_asustek_request" string="Reference field request returns a browsable object">
<test expr="ref_doc1.name">ASUStek</test>
</assert>
</data>
</openerp>
+4 -2
View File
@@ -148,8 +148,10 @@
<rng:define name="delete">
<rng:element name="delete">
<rng:attribute name="model" />
<rng:optional><rng:attribute name="id" /> </rng:optional>
<rng:optional><rng:attribute name="search" /></rng:optional>
<rng:choice>
<rng:attribute name="id" />
<rng:attribute name="search" />
</rng:choice>
</rng:element>
</rng:define>
+4 -1
View File
@@ -205,7 +205,10 @@ def init_logger():
for level, (fg, bg) in mapping.items():
msg = "\x1b[%dm\x1b[%dm%s\x1b[0m" % (foreground(fg), background(bg), level)
logging.addLevelName(getattr(logging, level), msg)
# jth: we should not override default level name
# jth: use this instead http://stackoverflow.com/questions/384076/how-can-i-make-the-python-logging-output-to-be-colored
# jth: also, do not output ANSI if terminal doesn't support it (test it in Eclipse console and on windows)
#logging.addLevelName(getattr(logging, level), msg)
class Logger(object):
+283 -15
View File
@@ -501,6 +501,16 @@ class orm_template(object):
self._table = self._name.replace('.', '_')
def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}):
"""
Fetch records as objects allowing to use dot notation to browse fields and relations
:param cr: database cursor
:param user: current user id
:param select: id or a list of ids
:param context: context arguments, like lang, time zone
:rtype: object or a list of objects requested
"""
if not context:
context = {}
self._list_class = list_class or browse_record_list
@@ -616,6 +626,19 @@ class orm_template(object):
return [data] + lines
def export_data(self, cr, uid, ids, fields_to_export, context=None):
"""
Export fields for selected objects
:param cr: database cursor
:param uid: current user id
:param ids: a list of ids
:param fields_to_export: a list of fields
:param context: context arguments, like lang, time zone, may contain import_comp(default: False) to make exported data compatible with import_data()
:rtype: a dictionary with a *datas* matrix
This method is used when exporting data via client menu
"""
if not context:
context = {}
imp_comp = context.get('import_comp',False)
@@ -645,6 +668,24 @@ class orm_template(object):
return {'datas':datas}
def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None):
"""
Import given data in given module
:param cr: database cursor
:param uid: current user id
:param ids: a list of ids
:param fields: a list of fields
:param data: data to import
:param mode: 'init' or 'update' for record creation
:param current_module: module name
:param noupdate: flag for record creation
:param context: context arguments, like lang, time zone,
:param filename: optional file to store partial import state for recovery
:rtype: a tuple
This method is used when importing data via client menu
"""
if not context:
context = {}
fields = map(lambda x: x.split('/'), fields)
@@ -984,16 +1025,13 @@ class orm_template(object):
self._invalids.clear()
def default_get(self, cr, uid, fields_list, context=None):
""" Set default values for the object's fields.
"""
Set default values for the object's fields.
Returns a dict of {field_name:default_value}
:param fields_list: fields for which the object doesn't have any value yet, and default values need to be provided.
If fields outside this list are returned, the user-provided values will be overwritten.
:rtype: a dict of {field_name:default_value}
Arguments:
`fields_list`: the fields for which the object doesn't have
any value yet, and default values need to be
provided. If fields outside this list are
returned, the user-provided values will be
overwritten.
"""
return {}
@@ -1316,6 +1354,23 @@ class orm_template(object):
# 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, submenu=False):
"""
Get the detailed composition of the requested view like fields, model, view architecture
:param cr: database cursor
:param user: current user id
:param view_id: id of the view or None
:param view_type: type of the view to return if view_id is None ('form', tree', ...)
:param context: context arguments, like lang, time zone
:param toolbar: true to include concextual actions
:param submenu: example (portal_project module)
:return: a dictionary describing the composition of the requested view (including inherited views and extensions)
:raise AttributeError:
* if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace'
* if some tag other than 'position' is found in parent view
:raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
"""
if not context:
context = {}
@@ -1878,6 +1933,28 @@ class orm(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', 'exists']
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None):
"""
Get the list of records in list view sorted by the given fields
:param cr: database cursor
:param uid: current user id
:param domain: a list specifying search criteria [['field_name', 'operator', 'value'], ...]
:param fields: a list of fields present in the list view specified on the object
:param groupby: a list of fields on which to sort the records
:type fields_list: a list (example ['field_name_1', ...])
:param offset: optional number of records to skip
:param limit: optional max number of records to return
:param context: context arguments, like lang, time zone
:return: a list of dictionaries(one dictionary for each record) containing:
* the values of fields sorted out by the fields in groupby arguement
* __domain: a list of tuples specifying the search criteria
* __context: a dictionary with argument like 'groupby'
:rtype: [{'field_name_1': value, ...]
:raise AccessError: * if user has no read rights on the requested object
* if user tries to bypass access rules for read on the requested object
"""
groupby_list = groupby
if isinstance(groupby, list):
groupby = groupby[0]
@@ -1955,10 +2032,13 @@ class orm(orm_template):
return data
def _inherits_join_calc(self, field, tables, where_clause):
""" Adds missing table select and join clause(s) for reaching
"""
Adds missing table select and join clause(s) for reaching
the field coming from an '_inherits' parent table.
@param tables: list of table._table names enclosed in double quotes as returned
:param tables: a list of table._table names enclosed in double quotes as returned
by _where_calc()
"""
current_table = self
while field in current_table._inherit_fields and not field in current_table._columns:
@@ -2413,6 +2493,17 @@ class orm(orm_template):
self._columns[f].restart()
def default_get(self, cr, uid, fields_list, context=None):
"""
Get default field values of the given field list of the model
:param cr: database cursor
:param uid: current user id
:param fields_list: a list of fields to get the default value
:type fields_list: a list (example ['field_name_1', ...])
:param context: context arguments, like lang, time zone
:return: a dictionary of the default values for fields (set on the object class, by the user preferences, or via the context)
"""
if not context:
context = {}
value = {}
@@ -2504,12 +2595,38 @@ class orm(orm_template):
self._inherits_reload_src()
def fields_get(self, cr, user, fields=None, context=None):
"""
Get the description of list of fields
:param cr: database cursor
:param user: current user id
:param fields: a list of fields
:param context: context arguments, like lang, time zone
:return: a dictionary of field dictionaries, each one describing a field of the business object
:raise AccessError: * if user has no create/write rights on the requested object
"""
ira = self.pool.get('ir.model.access')
read_access = ira.check(cr, user, self._name, 'write', raise_exception=False, context=context) or \
ira.check(cr, user, self._name, 'create', raise_exception=False, context=context)
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'):
"""
Read records with given ids with the given fields
:param cr: database cursor
:param user: current user id
:param ids: id or a list of the ids of the records to read
:param fields: optional list of field names to return (default: all fields would be returned)
:type fields: a list (example ['field_name_1', ...])
:param context(optional, highly recommended): context arguments, like lang, time zone
:return: a list of dictionaries((a dictionary per record asked)) with requested field values
:rtype: [{‘name_of_the_field’: value, ...}, ...]
:raise AccessError: * if user has no read rights on the requested object
* if user tries to bypass access rules for read on the requested object
"""
if not context:
context = {}
self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context)
@@ -2695,6 +2812,24 @@ class orm(orm_template):
return res
def perm_read(self, cr, user, ids, context=None, details=True):
"""
Read the permission for record of the given ids
:param cr: database cursor
:param user: current user id
:param ids: id or list of ids
:param context: context arguments, like lang, time zone
:param details: if True, *_uid fields are replaced with the name of the user
:return: a list of ownership dictionaries for each requested record
:rtype: a list of dictionaries with the following keys:
* id: object id
* create_uid: user who created the record
* create_date: date when the record was created
* write_uid: last user who changed the record
* write_date: date of the last change to the record
"""
if not context:
context = {}
if not ids:
@@ -2755,6 +2890,19 @@ class orm(orm_template):
% (operation, self._name))
def unlink(self, cr, uid, ids, context=None):
"""
Delete records with given ids
:param cr: database cursor
:param uid: current user id
:param ids: id or a list of ids
:param context(optional, highly recommended): context arguments, like lang, time zone
:return: True
:raise AccessError: * if user has no unlink rights on the requested object
* if user tries to bypass access rules for unlink on the requested object
:raise UserError: if the record is default property for other records
"""
if not ids:
return True
if isinstance(ids, (int, long)):
@@ -2787,7 +2935,6 @@ class orm(orm_template):
for sub_ids in cr.split_for_in_conditions(ids):
cr.execute('delete from ' + self._table + ' ' \
'where id in %s', (sub_ids,))
for order, object, store_ids, fields in result_store:
if object != self._name:
obj = self.pool.get(object)
@@ -2801,6 +2948,30 @@ class orm(orm_template):
# TODO: Validate
#
def write(self, cr, user, ids, vals, context=None):
"""
Update records with given ids with the given field values
:param cr: database cursor
:param user: current user id
:type user: integer (example 1)
:param ids: id or a list of ids
:param vals: a dictionary of field values to update
:type vals: a dictionary (example {'field_name':'value', ...})
:param context(optional, highly recommended): context arguments, like lang, time zone
:return: True
:raise AccessError: * if user has no write rights on the requested object
* if user tries to bypass access rules for write on the requested object
:raise ValidateError: if user tries to enter invalid value for a field that is not in selection
:raise UserError: if recurssion is found
vals format for relational field type.
+ many2many field : [(6, 0, list of ids)] (example: [(6, 0, [8, 5, 6, 4])])
+ one2many field : [(0, 0, dictionary of values)] (example: [(0, 0, {'field_name':field_value, ...})])
+ many2one field : ID of related record
+ reference field : model name, id (example: 'product.product, 5')
"""
readonly = None
for field in vals.copy():
fobj = None
@@ -2987,10 +3158,28 @@ class orm(orm_template):
# 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, ...}
"""
Create new record with specified value
:param cr: database cursor
:param user: current user id
:type user: integer (example 1)
:param vals: a dictionary for new record {'field_name':field_value, ...}
:type vals: a dictionary (example {'field_name':field_value, ...})
:param context(optional, highly recommended): context arguements, like lang, time zone
:type context: a dictionary (example {'lang':'en_us', ...})
:return: id of new record created
:raise AccessError: * if user has no create rights on the requested object
* if user tries to bypass access rules for create on the requested object
:raise ValidateError: if user tries to enter invalid value for a field that is not in selection
vals format for relational field type.
+ many2many field : [(6, 0, list of ids)] (example: [(6, 0, [8, 5, 6, 4])])
+ one2many field : [(0, 0, dictionary of values)] (example: [(0, 0, {'field_name':field_value, ...})])
+ many2one field : ID of related record
+ reference field : model name, id (example: 'product.product, 5')
"""
if not context:
context = {}
@@ -3337,6 +3526,27 @@ class orm(orm_template):
def search(self, cr, user, args, offset=0, limit=None, order=None,
context=None, count=False):
"""
Search for record/s with or without domain
:param cr: database cursor
:param user: current user id
:param args: a list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...]
:param offset: optional number of records to skip
:param limit: optional max number of records to return
:param order: optional columns to sort by (default: self._order )
:param context(optional, highly recommended): context arguments, like lang, time zone
:param count: if True, returns only the number of records matching the criteria, not their ids
:return: id or a list of ids of records matching the criteria
:rtype: integer or a list of integers
:raise AccessError: * if user tries to bypass access rules for read on the requested object.
Operators:
* =, !=, >, >=, <, <=, like, ilike, in, not in, child_of, parent_left, parent_right
Prefix operators:
* '&' (default), '|', '!'
"""
if not context:
context = {}
# compute the where, order by, limit and offset clauses
@@ -3386,6 +3596,16 @@ class orm(orm_template):
return self._columns[field].search(cr, self, args, field, value, offset, limit, uid)
def name_get(self, cr, user, ids, context=None):
"""
:param cr: database cursor
:param user: current user id
:type user: integer (example 1)
:param ids: a list of ids
:param context: context arguments, like lang, time zone
:return: tuples with the text representation of requested objects for to-many relationships
"""
if not context:
context = {}
if not ids:
@@ -3396,6 +3616,20 @@ class orm(orm_template):
[self._rec_name], context, load='_classic_write')]
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
"""
:param cr: database cursor
:param user: current user id
:param name: object name to search
:param args: a list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...]
:param operator: operator for search criterion
:param context: context arguments, like lang, time zone
:param limit: optional max number of records to return
:return: a list of object names matching the search criteria, used to provide completition for to-many relationships
This method is equivalent of search() on name + name_get()
"""
if not args:
args = []
if not context:
@@ -3408,6 +3642,17 @@ class orm(orm_template):
return res
def copy_data(self, cr, uid, id, default=None, context=None):
"""
Copy given record's data with all its fields values
:param cr: database cursor
:param user: current user id
:param ids: id of the record to copy
:param default: a dictionary of field values to update before saving the duplicate object
:param context: context arguments, like lang, time zone
:return: a dictionary containing all the field values
"""
if not context:
context = {}
if not default:
@@ -3479,6 +3724,18 @@ class orm(orm_template):
return data, trans_data
def copy(self, cr, uid, id, default=None, context=None):
"""
Duplicate record with given id updating it with default values
:param cr: database cursor
:param uid: current user id
:param id: id of the record to copy
:param default: a dictionary of field values to update before saving the duplicate object
:type default: a dictionary (example {'field_name':field_value, ...})
:param context: context arguments, like lang, time zone
:return: True
"""
trans_obj = self.pool.get('ir.translation')
data, trans_data = self.copy_data(cr, uid, id, default, context)
new_id = self.create(cr, uid, data, context)
@@ -3493,6 +3750,16 @@ class orm(orm_template):
return bool(cr.fetchone()[0])
def check_recursion(self, cr, uid, ids, parent=None):
"""
Check recursion in records
:param cr: database cursor
:param uid: current user id
:param ids: a list of ids of records
:param parent: parent field name
:return: True or False based on recursion detection
"""
if not parent:
parent = self._parent_name
ids_parent = ids[:]
@@ -3511,3 +3778,4 @@ class orm(orm_template):
return True
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+6 -9
View File
@@ -29,7 +29,7 @@ import pooler
import copy
import sys
import traceback
import logging
from psycopg2 import IntegrityError
from tools.func import wraps
@@ -57,8 +57,7 @@ class osv_pool(netsvc.Service):
return f(self, dbname, *args, **kwargs)
except orm.except_orm, inst:
if inst.name == 'AccessError':
tb_s = "AccessError\n" + "".join(traceback.format_exception(*sys.exc_info()))
self.logger.notifyChannel('web-services', netsvc.LOG_DEBUG, tb_s)
self.logger.debug("AccessError", exc_info=True)
self.abortResponse(1, inst.name, 'warning', inst.value)
except except_osv, inst:
self.abortResponse(1, inst.name, inst.exc_type, inst.value)
@@ -68,8 +67,7 @@ class osv_pool(netsvc.Service):
self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key])
self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
except Exception, e:
tb_s = "".join(traceback.format_exception(*sys.exc_info()))
self.logger.notifyChannel('web-services', netsvc.LOG_ERROR, tb_s)
self.logger.exception("Uncaught exception")
raise
return wrapper
@@ -84,7 +82,7 @@ class osv_pool(netsvc.Service):
self._store_function = {}
self._init = True
self._init_parent = {}
self.logger = netsvc.Logger()
self.logger = logging.getLogger("web-services")
netsvc.Service.__init__(self, 'object_proxy', audience='')
self.exportMethod(self.obj_list)
self.exportMethod(self.exec_workflow)
@@ -116,11 +114,10 @@ class osv_pool(netsvc.Service):
try:
try:
if method.startswith('_'):
raise except_osv('Method Error', 'Private method %s can not be calleble.' % (method,))
raise except_osv('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
res = pool.execute_cr(cr, uid, obj, method, *args, **kw)
if res is None:
self.logger.notifyChannel("web-services", netsvc.LOG_WARNING,
'Method can not return a None value (crash in XML-RPC)')
self.logger.warning('RPC methods cannot return `None` at the moment!')
cr.commit()
except Exception:
cr.rollback()
+7 -7
View File
@@ -251,12 +251,11 @@ class OerpAuthProxy(AuthProxy):
self.auth_tries = 0
self.last_auth = None
def checkRequest(self,handler,path = '/'):
if self.auth_creds:
return True
def checkRequest(self,handler,path, db=False):
auth_str = handler.headers.get('Authorization',False)
try:
db = handler.get_db_from_path(path)
if not db:
db = handler.get_db_from_path(path)
print "Got db:",db
except:
if path.startswith('/'):
@@ -267,8 +266,9 @@ class OerpAuthProxy(AuthProxy):
else:
#FIXME!
self.provider.log("Wrong path: %s, failing auth" %path)
raise AuthRejectedExc("Authorization failed. Wrong sub-path.")
raise AuthRejectedExc("Authorization failed. Wrong sub-path.")
if self.auth_creds.get(db):
return True
if auth_str and auth_str.startswith('Basic '):
auth_str=auth_str[len('Basic '):]
(user,passwd) = base64.decodestring(auth_str).split(':')
@@ -276,7 +276,7 @@ class OerpAuthProxy(AuthProxy):
acd = self.provider.authenticate(db,user,passwd,handler.client_address)
if acd != False:
self.auth_creds[db] = acd
self.last_auth=db
self.last_auth = db
return True
if self.auth_tries > 5:
self.provider.log("Failing authorization after 5 requests w/o password")
+3
View File
@@ -234,6 +234,9 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler):
if fore.close_connection:
# print "Closing connection because of handler"
self.close_connection = fore.close_connection
if hasattr(fore, '_flush'):
fore._flush()
def parse_rawline(self):
"""Parse a request (internal).
+1 -1
View File
@@ -251,7 +251,7 @@ form: module.record_id""" % (xml_id,)
pass
if 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)
self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids)
def _tag_report(self, cr, rec, data_node=None):
res = {}
+146 -215
View File
@@ -7,6 +7,7 @@ import pooler
import netsvc
import misc
from config import config
import yaml_tag
import yaml
@@ -18,161 +19,6 @@ class YamlImportException(Exception):
class YamlImportAbortion(Exception):
pass
class YamlTag(object):
"""
Superclass for constructors of custom tags defined in yaml file.
"""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __getitem__(self, key):
return getattr(self, key)
def __getattr__(self, attr):
return None
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, sorted(self.__dict__.items()))
class Assert(YamlTag):
def __init__(self, model, id, severity=logging.ERROR, string="NONAME", **kwargs):
self.model = model
self.id = id
self.severity = severity
self.string = string
super(Assert, self).__init__(**kwargs)
class Record(YamlTag):
def __init__(self, model, id, **kwargs):
self.model = model
self.id = id
super(Record, self).__init__(**kwargs)
class Python(YamlTag):
def __init__(self, model, severity=logging.ERROR, name="", **kwargs):
self.model= model
self.severity = severity
self.name = name
super(Python, self).__init__(**kwargs)
class Menuitem(YamlTag):
def __init__(self, id, name, **kwargs):
self.id = id
self.name = name
super(Menuitem, self).__init__(**kwargs)
class Workflow(YamlTag):
def __init__(self, model, action, **kwargs):
self.model = model
self.action = action
super(Workflow, self).__init__(**kwargs)
class ActWindow(YamlTag):
def __init__(self, model, action, **kwargs):
self.model = model
self.action = action
super(ActWindow, self).__init__(**kwargs)
class Function(YamlTag):
def __init__(self, model, name, **kwargs):
self.model = model
self.name = name
super(Function, self).__init__(**kwargs)
class Report(YamlTag):
def __init__(self, model, name, string, **kwargs):
self.model = model
self.name = name
self.string = string
super(Report, self).__init__(**kwargs)
class Delete(YamlTag):
def __init__(self, model, id, search, **kwargs):
self.model = model
self.id = id
self.search = search
super(Delete, self).__init__(**kwargs)
class Context(YamlTag):
def __init__(self, **kwargs):
super(Context, self).__init__(**kwargs)
class Url(YamlTag):
def __init__(self, **kwargs):
super(Url, self).__init__(**kwargs)
class Eval(YamlTag):
def __init__(self, expression):
self.expression = expression
super(Eval, self).__init__()
class IrSet(YamlTag):
def __init__(self):
super(IrSet, self).__init__()
def assert_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Assert(**kwargs)
def record_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Record(**kwargs)
def python_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Python(**kwargs)
def menuitem_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Menuitem(**kwargs)
def workflow_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Workflow(**kwargs)
def act_window_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return ActWindow(**kwargs)
def function_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Function(**kwargs)
def report_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Report(**kwargs)
def delete_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Delete(**kwargs)
def context_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Context(**kwargs)
def url_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Url(**kwargs)
def eval_constructor(loader, node):
expression = loader.construct_scalar(node)
return Eval(expression)
def ir_set_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return IrSet(**kwargs)
yaml.add_constructor(u"!assert", assert_constructor)
yaml.add_constructor(u"!record", record_constructor)
yaml.add_constructor(u"!python", python_constructor)
yaml.add_constructor(u"!menuitem", menuitem_constructor)
yaml.add_constructor(u"!workflow", workflow_constructor)
yaml.add_constructor(u"!act_window", act_window_constructor)
yaml.add_constructor(u"!function", function_constructor)
yaml.add_constructor(u"!report", report_constructor)
yaml.add_constructor(u"!context", context_constructor)
yaml.add_constructor(u"!delete", delete_constructor)
yaml.add_constructor(u"!url", url_constructor)
yaml.add_constructor(u"!eval", eval_constructor)
yaml.add_constructor(u"!ir_set", ir_set_constructor)
def _is_yaml_mapping(node, tag_constructor):
value = isinstance(node, types.DictionaryType) \
and len(node.keys()) == 1 \
@@ -183,45 +29,49 @@ def is_comment(node):
return isinstance(node, types.StringTypes)
def is_assert(node):
return _is_yaml_mapping(node, Assert)
return _is_yaml_mapping(node, yaml_tag.Assert)
def is_record(node):
return _is_yaml_mapping(node, Record)
return _is_yaml_mapping(node, yaml_tag.Record)
def is_python(node):
return _is_yaml_mapping(node, Python)
return _is_yaml_mapping(node, yaml_tag.Python)
def is_menuitem(node):
return isinstance(node, Menuitem) \
or _is_yaml_mapping(node, Menuitem)
return isinstance(node, yaml_tag.Menuitem) \
or _is_yaml_mapping(node, yaml_tag.Menuitem)
def is_function(node):
return isinstance(node, Function) \
or _is_yaml_mapping(node, Function)
return isinstance(node, yaml_tag.Function) \
or _is_yaml_mapping(node, yaml_tag.Function)
def is_report(node):
return isinstance(node, Report)
return isinstance(node, yaml_tag.Report)
def is_workflow(node):
return isinstance(node, Workflow)
return isinstance(node, yaml_tag.Workflow)
def is_act_window(node):
return isinstance(node, ActWindow)
return isinstance(node, yaml_tag.ActWindow)
def is_delete(node):
return isinstance(node, Delete)
return isinstance(node, yaml_tag.Delete)
def is_context(node):
return isinstance(node, Context)
return isinstance(node, yaml_tag.Context)
def is_url(node):
return isinstance(node, Url)
return isinstance(node, yaml_tag.Url)
def is_eval(node):
return isinstance(node, Eval)
return isinstance(node, yaml_tag.Eval)
def is_ref(node):
return isinstance(node, yaml_tag.Ref) \
or _is_yaml_mapping(node, yaml_tag.Ref)
def is_ir_set(node):
return _is_yaml_mapping(node, IrSet)
return _is_yaml_mapping(node, yaml_tag.IrSet)
class TestReport(object):
@@ -276,7 +126,7 @@ class YamlInterpreter(object):
self.pool = pooler.get_pool(cr.dbname)
self.uid = 1
self.context = {} # opererp context
self.eval_context = {'ref': self._ref, '_ref': self._ref} # added '_ref' so that record['ref'] is possible
self.eval_context = {'ref': self._ref(), '_ref': self._ref(), 'time': time} # added '_ref' so that record['ref'] is possible
def _ref(self):
return lambda xml_id: self.get_id(xml_id)
@@ -309,10 +159,11 @@ class YamlInterpreter(object):
id = self.id_map[xml_id]
else:
if '.' in xml_id:
module, xml_id = xml_id.split('.', 1)
module, checked_xml_id = xml_id.split('.', 1)
else:
module = self.module
ir_id = self.pool.get('ir.model.data')._get_id(self.cr, self.uid, module, xml_id)
checked_xml_id = xml_id
ir_id = self.pool.get('ir.model.data')._get_id(self.cr, self.uid, module, checked_xml_id)
obj = self.pool.get('ir.model.data').read(self.cr, self.uid, ir_id, ['res_id'])
id = int(obj['res_id'])
self.id_map[xml_id] = id
@@ -327,14 +178,29 @@ class YamlInterpreter(object):
def isnoupdate(self, node):
return self.noupdate or node.noupdate or False
def _get_first_result(self, results, default=False):
if len(results):
value = results[0]
if isinstance(value, types.TupleType):
value = value[0]
else:
value = default
return value
def process_comment(self, node):
return node
def _log_assert_failure(self, severity, msg, *args):
self.assert_report.record(False, severity)
self.logger.log(severity, msg, *args)
if severity >= config['assert_exit_level']:
raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % logging.getLevelName(severity))
if isinstance(severity, types.StringTypes):
levelname = severity.strip().upper()
level = logging.getLevelName(levelname)
else:
level = severity
levelname = logging.getLevelName(level)
self.assert_report.record(False, levelname)
self.logger.log(level, msg, *args)
if level >= config['assert_exit_level']:
raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % levelname)
return
def _get_assertion_id(self, assertion):
@@ -351,6 +217,7 @@ class YamlInterpreter(object):
assertion, expressions = node.items()[0]
if self.isnoupdate(assertion) and self.mode != 'init':
self.logger.warn('This assertion was not evaluated ("%s").' % assertion.string)
return
model = self.get_model(assertion.model)
ids = self._get_assertion_id(assertion)
@@ -365,7 +232,7 @@ class YamlInterpreter(object):
context = self.get_context(assertion, self.eval_context)
for id in ids:
record = model.browse(self.cr, self.uid, id, context)
for test in expressions.get('test', ''):
for test in expressions:
try:
success = eval(test, self.eval_context, RecordDictWrapper(record))
except Exception, e:
@@ -378,19 +245,39 @@ class YamlInterpreter(object):
else: # all tests were successful for this assertion tag (no break)
self.assert_report.record(True, assertion.severity)
def _coerce_bool(self, value, default=False):
if isinstance(value, types.BooleanType):
b = value
if isinstance(value, types.StringTypes):
b = value.strip().lower() not in ('0', 'false', 'off', 'no')
elif isinstance(value, types.IntType):
b = bool(value)
else:
b = default
return b
def process_record(self, node):
record, fields = node.items()[0]
self.validate_xml_id(record.id)
if self.isnoupdate(record) and self.mode != 'init':
model = self.get_model(record.model)
record_dict = self._create_record(model, fields)
self.logger.debug("RECORD_DICT %s" % record_dict)
id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \
self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode)
self.id_map[record.id] = int(id)
if config.get('import_partial', False):
self.cr.commit()
id = self.pool.get('ir.model.data')._update_dummy(self.cr, self.uid, record.model, self.module, record.id)
# check if the resource already existed at the last update
if id:
self.id_map[record] = int(id)
return None
else:
if not self._coerce_bool(record.forcecreate):
return None
model = self.get_model(record.model)
record_dict = self._create_record(model, fields)
self.logger.debug("RECORD_DICT %s" % record_dict)
id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \
self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode)
self.id_map[record.id] = int(id)
if config.get('import_partial', False):
self.cr.commit()
def _create_record(self, model, fields):
record_dict = {}
@@ -399,19 +286,50 @@ class YamlInterpreter(object):
record_dict[field_name] = field_value
return record_dict
def process_ref(self, node, column=None):
if node.search:
if node.model:
model_name = node.model
elif column:
model_name = column._obj
else:
raise YamlImportException('You need to give a model for the search, or a column to infer it.')
model = self.get_model(model_name)
q = eval(node.search, self.eval_context)
ids = model.search(self.cr, self.uid, q)
if node.use:
instances = model.browse(self.cr, self.uid, ids)
value = [inst[node.use] for inst in instances]
else:
value = ids
elif node.id:
value = self.get_id(node.id)
else:
value = None
return value
def process_eval(self, node):
return eval(node.expression, self.eval_context)
def _eval_field(self, model, field_name, expression):
column = model._columns[field_name]
if column._type == "many2one":
if is_ref(expression):
elements = self.process_ref(expression)
if column._type in ("many2many", "one2many"):
value = [(6, 0, elements)]
else: # many2one
value = self._get_first_result(elements)
elif column._type == "many2one":
value = self.get_id(expression)
elif column._type == "one2many":
other_model = self.get_model(column._obj)
value = [(0, 0, self._create_record(other_model, fields)) for fields in expression]
elif column._type == "many2many":
ids = [self.get_id(xml_id) for xml_id in expression]
value= [(6, 0, ids)]
value = [(6, 0, ids)]
else: # scalar field
if isinstance(expression, Eval):
value = eval(expression.expression)
if is_eval(expression):
value = self.process_eval(expression)
else:
value = expression
# raise YamlImportException('Unsupported column "%s" or value %s:%s' % (field_name, type(expression), expression))
@@ -446,7 +364,6 @@ class YamlInterpreter(object):
workflow, values = node.items()[0]
if self.isnoupdate(workflow) and self.mode != 'init':
return
model = self.get_model(workflow.model)
if workflow.ref:
id = self.get_id(workflow.ref)
else:
@@ -467,26 +384,44 @@ class YamlInterpreter(object):
else:
uid = self.uid
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, model, id, workflow.action, self.cr)
wf_service.trg_validate(uid, workflow.model, id, workflow.action, self.cr)
def _eval_params(self, model, params):
args = []
for i, param in enumerate(params):
if isinstance(param, types.ListType):
value = self._eval_params(model, param)
elif is_ref(param):
value = self.process_ref(param)
elif is_eval(param):
value = self.process_eval(param)
elif isinstance(param, types.DictionaryType): # supports XML syntax
param_model = self.get_model(param.get('model', model))
if 'search' in param:
q = eval(param['search'], self.eval_context)
ids = param_model.search(cr, uid, q)
value = self._get_first_result(ids)
elif 'eval' in param:
local_context = {'obj': lambda x: param_model.browse(self.cr, self.uid, x, context)}
local_context.update(self.id_map)
value = eval(param['eval'], self.eval_context, local_context)
else:
raise YamlImportException('You must provide either a !ref or at least a "eval" or a "search" to function parameter #%d.' % i)
else:
value = param # scalar value
args.append(value)
return args
def process_function(self, node):
function, values = node.items()[0]
function, params = node.items()[0]
if self.isnoupdate(function) and self.mode != 'init':
return
context = self.get_context(function, self.eval_context)
args = []
if function.eval:
args = eval(function.eval, self.eval_context)
for value in values:
if not 'model' in value and (not 'eval' in value or not 'search' in value):
raise YamlImportException('You must provide a "model" and an "eval" or "search" to evaluate.')
value_model = self.get_model(value['model'])
local_context = {'obj': lambda x: value_model.browse(self.cr, self.uid, x, context=context)}
local_context.update(self.id_map)
id = eval(value['eval'], self.eval_context, local_context)
if id != None:
args.append(id)
model = self.get_model(function.model)
context = self.get_context(function, self.eval_context)
if function.eval:
args = self.process_eval(function.eval)
else:
args = self._eval_params(function.model, params)
method = function.name
getattr(model, method)(self.cr, self.uid, *args)
@@ -624,17 +559,13 @@ class YamlInterpreter(object):
# TODO add remove ir.model.data
def process_delete(self, node):
ids = []
if len(node.search):
ids = self.pool.get(node.model).search(self.cr, self.uid, eval(node.search, self.eval_context))
if len(node.id):
try:
ids.append(self.get_id(node.id))
except:
pass
else:
ids = [self.get_id(node.id)]
if len(ids):
self.pool.get(node.model).unlink(self.cr, self.uid, ids)
self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids, direct=True)
self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids)
def process_url(self, node):
self.validate_xml_id(node.id)
@@ -717,9 +648,9 @@ class YamlInterpreter(object):
try:
self._process_node(node)
except YamlImportException, e:
self.logger.log(logging.ERROR, e)
self.logger.exception(e)
except Exception, e:
self.logger.log(logging.ERROR, e)
self.logger.exception(e)
raise e
def _process_node(self, node):
+172
View File
@@ -0,0 +1,172 @@
import yaml
import logging
class YamlTag(object):
"""
Superclass for constructors of custom tags defined in yaml file.
"""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __getitem__(self, key):
return getattr(self, key)
def __getattr__(self, attr):
return None
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, sorted(self.__dict__.items()))
class Assert(YamlTag):
def __init__(self, model, id, severity=logging.ERROR, string="NONAME", **kwargs):
self.model = model
self.id = id
self.severity = severity
self.string = string
super(Assert, self).__init__(**kwargs)
class Record(YamlTag):
def __init__(self, model, id, use='id', **kwargs):
self.model = model
self.id = id
super(Record, self).__init__(**kwargs)
class Python(YamlTag):
def __init__(self, model, severity=logging.ERROR, name="", **kwargs):
self.model= model
self.severity = severity
self.name = name
super(Python, self).__init__(**kwargs)
class Menuitem(YamlTag):
def __init__(self, id, name, **kwargs):
self.id = id
self.name = name
super(Menuitem, self).__init__(**kwargs)
class Workflow(YamlTag):
def __init__(self, model, action, **kwargs):
self.model = model
self.action = action
super(Workflow, self).__init__(**kwargs)
class ActWindow(YamlTag):
def __init__(self, model, action, **kwargs):
self.model = model
self.action = action
super(ActWindow, self).__init__(**kwargs)
class Function(YamlTag):
def __init__(self, model, name, **kwargs):
self.model = model
self.name = name
super(Function, self).__init__(**kwargs)
class Report(YamlTag):
def __init__(self, model, name, string, **kwargs):
self.model = model
self.name = name
self.string = string
super(Report, self).__init__(**kwargs)
class Delete(YamlTag):
def __init__(self, model, id, search, **kwargs):
self.model = model
self.id = id
self.search = search
super(Delete, self).__init__(**kwargs)
class Context(YamlTag):
def __init__(self, **kwargs):
super(Context, self).__init__(**kwargs)
class Url(YamlTag):
def __init__(self, **kwargs):
super(Url, self).__init__(**kwargs)
class Eval(YamlTag):
def __init__(self, expression):
self.expression = expression
super(Eval, self).__init__()
class Ref(YamlTag):
def __init__(self, *args, **kwargs):
super(Ref, self).__init__(*args, **kwargs)
class IrSet(YamlTag):
def __init__(self):
super(IrSet, self).__init__()
def assert_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Assert(**kwargs)
def record_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Record(**kwargs)
def python_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Python(**kwargs)
def menuitem_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Menuitem(**kwargs)
def workflow_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Workflow(**kwargs)
def act_window_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return ActWindow(**kwargs)
def function_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Function(**kwargs)
def report_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Report(**kwargs)
def delete_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Delete(**kwargs)
def context_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Context(**kwargs)
def url_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return Url(**kwargs)
def eval_constructor(loader, node):
expression = loader.construct_scalar(node)
return Eval(expression)
def ref_constructor(loader, tag_suffix, node):
if tag_suffix == "id":
kwargs = {"id": loader.construct_scalar(node)}
else:
kwargs = loader.construct_mapping(node)
return Ref(**kwargs)
def ir_set_constructor(loader, node):
kwargs = loader.construct_mapping(node)
return IrSet(**kwargs)
# Registers constructors for custom tags.
# Constructors are actually defined globally: do not redefined them in another
# class/file/package. This means that module recorder need import this file.
yaml.add_constructor(u"!assert", assert_constructor)
yaml.add_constructor(u"!record", record_constructor)
yaml.add_constructor(u"!python", python_constructor)
yaml.add_constructor(u"!menuitem", menuitem_constructor)
yaml.add_constructor(u"!workflow", workflow_constructor)
yaml.add_constructor(u"!act_window", act_window_constructor)
yaml.add_constructor(u"!function", function_constructor)
yaml.add_constructor(u"!report", report_constructor)
yaml.add_constructor(u"!context", context_constructor)
yaml.add_constructor(u"!delete", delete_constructor)
yaml.add_constructor(u"!url", url_constructor)
yaml.add_constructor(u"!eval", eval_constructor)
yaml.add_multi_constructor(u"!ref", ref_constructor)
yaml.add_constructor(u"!ir_set", ir_set_constructor)