merge trunk #1015
bzr revid: jean-baptiste.aubort@camptocamp.com-20080822144626-kblv6krdoo4pema2
This commit is contained in:
+21
-17
@@ -270,25 +270,26 @@ def load_module_graph(cr, graph, status=None, **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,))
|
||||
|
||||
# check if all model of the module have at least a access rule.
|
||||
cr.execute(""" SELECT name
|
||||
FROM ir_model m
|
||||
WHERE EXISTS (SELECT 1
|
||||
FROM ir_model_data
|
||||
WHERE module = %s
|
||||
AND model = m.name
|
||||
)
|
||||
AND NOT EXISTS (SELECT 1
|
||||
FROM ir_model_access
|
||||
WHERE model_id = m.id
|
||||
)
|
||||
""", (m,))
|
||||
|
||||
for (model,) in cr.fetchall():
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'addon:%s:object %s has no access rules!' % (m,model,))
|
||||
# check if all model of the module have at least a access rule.
|
||||
# TODO: improve this query which is very slow !!!
|
||||
cr.execute(""" SELECT name
|
||||
FROM ir_model m
|
||||
WHERE EXISTS (SELECT 1
|
||||
FROM ir_model_data
|
||||
WHERE module = %s
|
||||
AND model = m.name
|
||||
)
|
||||
AND NOT EXISTS (SELECT 1
|
||||
FROM ir_model_access
|
||||
WHERE model_id = m.id
|
||||
)
|
||||
""", (m,))
|
||||
|
||||
cr.commit()
|
||||
for (model,) in cr.fetchall():
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'addon:%s:object %s has no access rules!' % (m,model,))
|
||||
|
||||
cr.commit()
|
||||
statusi+=1
|
||||
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
@@ -326,6 +327,9 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
if force_demo:
|
||||
force.append('demo')
|
||||
if update_module:
|
||||
for module in tools.config['init']:
|
||||
cr.execute('update ir_module_module set state=%s where state=%s and name=%s', ('to install', 'uninstalled', module))
|
||||
cr.commit()
|
||||
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')")
|
||||
|
||||
@@ -47,7 +47,6 @@ CREATE TABLE ir_model_fields (
|
||||
select_level varchar(4),
|
||||
field_description varchar(256),
|
||||
ttype varchar(64),
|
||||
group_name varchar(64),
|
||||
state varchar(64) default 'base',
|
||||
view_load boolean,
|
||||
relate boolean default False,
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
<record model="res.groups" id="group_extended">
|
||||
<field name="name">Extended View</field>
|
||||
</record>
|
||||
|
||||
<record model="res.groups" id="group_no_one">
|
||||
<field name="name">No One</field>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Users
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
import ir_sequence
|
||||
import ir_ui_menu
|
||||
import ir_ui_view
|
||||
import ir_actions
|
||||
import ir_default
|
||||
import ir_model
|
||||
import ir_actions
|
||||
import ir_report_custom
|
||||
import ir_attachment
|
||||
import ir_cron
|
||||
|
||||
Regular → Executable
+85
-9
@@ -46,6 +46,7 @@
|
||||
<field name="res_model">ir.values</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="values_view_tree"/>
|
||||
<field name="context">{'read':'default'}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="next_id_4" name="Low Level" parent="menu_custom"/>
|
||||
@@ -130,6 +131,7 @@
|
||||
<field name="name" select="1"/>
|
||||
<field name="type" select="1"/>
|
||||
<field name="usage"/>
|
||||
<field name="parent_id"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
@@ -391,6 +393,44 @@
|
||||
</record>
|
||||
<menuitem action="action_res_groups" id="menu_action_res_groups" parent="base.menu_users"/>
|
||||
|
||||
<record id="view_roles_form" model="ir.ui.view">
|
||||
<field name="name">res.roles.form</field>
|
||||
<field name="model">res.roles</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Role">
|
||||
<field colspan="4" name="name" select="1"/>
|
||||
<field colspan="4" name="parent_id"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_roles_tree" model="ir.ui.view">
|
||||
<field name="name">res.roles.tree</field>
|
||||
<field name="model">res.roles</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="field_parent">child_id</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Roles">
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="action_res_roles" model="ir.actions.act_window">
|
||||
<field name="name">Roles Structure</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.roles</field>
|
||||
<field name="view_type">tree</field>
|
||||
<field eval="[('parent_id','=',False)]" name="domain"/>
|
||||
</record>
|
||||
<menuitem action="action_res_roles" id="menu_action_res_roles" parent="base.menu_users"/>
|
||||
|
||||
<record id="action_res_roles_form" model="ir.actions.act_window">
|
||||
<field name="name">Roles</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.roles</field>
|
||||
<field name="view_type">form</field>
|
||||
</record>
|
||||
<menuitem action="action_res_roles_form" id="menu_action_res_roles_form" parent="menu_action_res_roles"/>
|
||||
|
||||
<!-- View -->
|
||||
<record id="view_view_form" model="ir.ui.view">
|
||||
@@ -799,7 +839,8 @@ Menu Edition
|
||||
<field name="sequence"/>
|
||||
<field colspan="4" name="parent_id" select="1"/>
|
||||
<field name="action"/>
|
||||
<field name="icon"/>
|
||||
<field name="icon" on_change="onchange_icon(icon)"/>
|
||||
<field name="icon_pict" widget="picture" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Groups">
|
||||
<field colspan="4" name="groups_id"/>
|
||||
@@ -1129,22 +1170,42 @@ Cron Jobs
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Server Action">
|
||||
<field name="name" select="1"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="state" select="1"/>
|
||||
<field name="model_id" select="1"/>
|
||||
<field name="sequence" select="2"/>
|
||||
<field name="code" colspan="4"/>
|
||||
<field name="trigger_name" select="2"/>
|
||||
<field name="trigger_object" select="2"/>
|
||||
<field name="trigger_object_id"/>
|
||||
<field name="message" select="2" colspan="4"/>
|
||||
<field name="address" select="2"/>
|
||||
<field name="child_ids" colspan="4"/>
|
||||
<notebook colspan="4">
|
||||
<page string="Python Code" attrs="{'invisible':[('state','!=','python')]}">
|
||||
<separator colspan="4" string="Python code"/>
|
||||
<field name="code" colspan="4" nolabel="1" />
|
||||
</page>
|
||||
|
||||
<page string="Trigger" attrs="{'invisible':[('state','!=','trigger')]}">
|
||||
<separator colspan="4" string="Trigger Configuration"/>
|
||||
<field name="trigger_obj_id" select="2" colspan="4"/>
|
||||
<field name="trigger_name" select="2"/>
|
||||
</page>
|
||||
|
||||
<page string="Email" attrs="{'invisible':[('state','=','python'),('state','=','dummy'),('state','=','trigger'), ('state','=','object_create'), ('state','=','object_write'), ('state','=','client_action'), ('state','=','other')]}">
|
||||
<separator colspan="4" string="Email Configuration"/>
|
||||
<field name="address" domain="[('model_id','=',model_id)]"/>
|
||||
<field name="message" select="2" colspan="4"/>
|
||||
<newline/>
|
||||
<label colspan="4" string="Use o to access the current object, you can use the any attributes which is belongs to current selected obect with o, i.e. o.partner_id.name for Invoice Object"/>
|
||||
</page>
|
||||
|
||||
<page string="Other Actions" attrs="{'invisible':[('state','!=','other')]}">
|
||||
<separator colspan="4" string="Other Actions Configuration"/>
|
||||
<field name="child_ids" nolabel="1" colspan="4" />
|
||||
</page>
|
||||
|
||||
</notebook>
|
||||
<field name="usage"/>
|
||||
<field name="type"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="view_server_action_tree" model="ir.ui.view">
|
||||
<field name="name">Server Actions</field>
|
||||
@@ -1164,8 +1225,23 @@ Cron Jobs
|
||||
<field name="res_model">ir.actions.server</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_server_action_tree"/>
|
||||
<field name="context">{'key':'server_action'}</field>
|
||||
</record>
|
||||
<menuitem action="action_server_action" id="menu_server_action" parent="base.next_id_6"/>
|
||||
|
||||
<record id="view_model_fields_tree" model="ir.ui.view">
|
||||
<field name="name">ir.model.fields.tree</field>
|
||||
<field name="model">ir.model.fields</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Fields">
|
||||
<field name="complete_name"/>
|
||||
<field name="model_id"/>
|
||||
<field name="name"/>
|
||||
<field name="field_description"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</terp>
|
||||
|
||||
Regular → Executable
+132
-13
@@ -55,7 +55,7 @@ class report_custom(osv.osv):
|
||||
_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),
|
||||
'model':fields.char('Object', 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.")
|
||||
@@ -99,7 +99,7 @@ class report_xml(osv.osv):
|
||||
_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),
|
||||
'model': fields.char('Object', 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),
|
||||
@@ -173,8 +173,8 @@ class act_window(osv.osv):
|
||||
'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),
|
||||
'res_model': fields.char('Object', size=64),
|
||||
'src_model': fields.char('Source Object', 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),
|
||||
@@ -255,7 +255,83 @@ class act_url(osv.osv):
|
||||
}
|
||||
act_url()
|
||||
|
||||
#
|
||||
def model_get(self, cr, uid, context={}):
|
||||
wkf_pool = self.pool.get('workflow')
|
||||
ids = wkf_pool.search(cr, uid, [])
|
||||
osvs = wkf_pool.read(cr, uid, ids, ['osv'])
|
||||
|
||||
res = []
|
||||
mpool = self.pool.get('ir.model')
|
||||
for osv in osvs:
|
||||
model = osv.get('osv')
|
||||
id = mpool.search(cr, uid, [('model','=',model)])
|
||||
name = mpool.read(cr, uid, id)[0]['name']
|
||||
res.append((model, name))
|
||||
|
||||
return res
|
||||
|
||||
class ir_model_fields(osv.osv):
|
||||
_inherit = 'ir.model.fields'
|
||||
_rec_name = 'complete_name'
|
||||
_columns = {
|
||||
'complete_name': fields.char('Complete Name', required=True, size=64, select=1),
|
||||
}
|
||||
|
||||
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80):
|
||||
def get_fields(cr, uid, field, rel):
|
||||
result = []
|
||||
mobj = self.pool.get('ir.model')
|
||||
id = mobj.search(cr, uid, [('model','=',rel)])
|
||||
|
||||
obj = self.pool.get('ir.model.fields')
|
||||
ids = obj.search(cr, uid, [('model_id','in',id)])
|
||||
records = obj.read(cr, uid, ids)
|
||||
for record in records:
|
||||
id = record['id']
|
||||
fld = field + '/' + record['name']
|
||||
|
||||
result.append((id, fld))
|
||||
return result
|
||||
|
||||
if not args:
|
||||
args=[]
|
||||
if not context:
|
||||
context={}
|
||||
return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
|
||||
|
||||
if context.get('key') != 'server_action':
|
||||
return super(ir_model_fields, self).name_search(cr, uid, name, args, operator, context, limit)
|
||||
|
||||
result = []
|
||||
obj = self.pool.get('ir.model.fields')
|
||||
ids = obj.search(cr, uid, args)
|
||||
records = obj.read(cr, uid, ids)
|
||||
for record in records:
|
||||
id = record['id']
|
||||
field = record['name']
|
||||
|
||||
if record['ttype'] == 'many2one':
|
||||
rel = record['relation']
|
||||
res = get_fields(cr, uid, field, record['relation'])
|
||||
for rs in res:
|
||||
result.append(rs)
|
||||
|
||||
result.append((id, field))
|
||||
|
||||
for rs in result:
|
||||
obj.write(cr, uid, [rs[0]], {'complete_name':rs[1]})
|
||||
|
||||
iids = []
|
||||
for rs in result:
|
||||
iids.append(rs[0])
|
||||
|
||||
result = super(ir_model_fields, self).name_search(cr, uid, name, [('complete_name','ilike',name), ('id','in',iids)], operator, context, limit)
|
||||
|
||||
return result
|
||||
|
||||
ir_model_fields()
|
||||
|
||||
##
|
||||
# Actions that are run on the server side
|
||||
#
|
||||
class actions_server(osv.osv):
|
||||
@@ -274,15 +350,17 @@ class actions_server(osv.osv):
|
||||
('object_write','Write Object'),
|
||||
('client_action','Client Action'),
|
||||
('other','Others Actions'),
|
||||
], 'Action State', required=True, size=32),
|
||||
], 'Action State', required=True, size=32, change_default=True),
|
||||
'code': fields.text('Python Code'),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'model_id': fields.many2one('ir.model', 'Model', required=True),
|
||||
'model_id': fields.many2one('ir.model', 'Object', 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),
|
||||
'trigger_obj_id': fields.reference('Trigger On', selection=model_get, 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),
|
||||
'address': fields.many2one('ir.model.fields', 'Email From / SMS'),
|
||||
#selection(_get_fields ,'Email / SMS', 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),
|
||||
@@ -301,6 +379,7 @@ class actions_server(osv.osv):
|
||||
# If you plan to return an action, assign: action = {...}
|
||||
"""
|
||||
}
|
||||
|
||||
#
|
||||
# Context should contains:
|
||||
# ids : original ids
|
||||
@@ -323,19 +402,59 @@ class actions_server(osv.osv):
|
||||
exec action.code in localdict
|
||||
if 'action' in localdict:
|
||||
return localdict['action']
|
||||
|
||||
if action.state == 'email':
|
||||
user = config['email_from']
|
||||
subject = action.name
|
||||
|
||||
obj_pool = self.pool.get(action.model_id.model)
|
||||
id = context.get('active_id')
|
||||
obj = obj_pool.browse(cr, uid, id)
|
||||
|
||||
fields = action.address.complete_name.split('/')
|
||||
for field in fields:
|
||||
obj = getattr(obj, field)
|
||||
|
||||
address = obj
|
||||
|
||||
body = action.message
|
||||
if tools.email_send_attach(user, action.address, subject, body, debug=False) == True:
|
||||
logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (action.address))
|
||||
#TODO : Apply Mail merge in to the Content of the Email
|
||||
|
||||
if tools.email_send_attach(user, address, subject, body, debug=False) == True:
|
||||
logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address))
|
||||
else:
|
||||
logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (action.address))
|
||||
logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (address))
|
||||
|
||||
if action.state == 'trigger':
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
res = str(action.trigger_obj_id).split(',')
|
||||
model = res[0]
|
||||
id = res[1]
|
||||
wf_service.trg_validate(uid, model, int(id), action.trigger_name, cr)
|
||||
|
||||
if action.state == 'sms':
|
||||
#TODO: Apply mearge with the field
|
||||
if tools.sms_send(user, password, api_id, text, to) == True:
|
||||
logger.notifyChannel('sms', netsvc.LOG_INFO, 'SMS successfully send to : %s' % (action.address))
|
||||
else:
|
||||
logger.notifyChannel('sms', netsvc.LOG_ERROR, 'Failed to send SMS to : %s' % (action.address))
|
||||
|
||||
if action.state == 'other':
|
||||
localdict = {
|
||||
'self': self.pool.get(action.model_id.model),
|
||||
'context': context,
|
||||
'time': time,
|
||||
'ids': ids,
|
||||
'cr': cr,
|
||||
'uid': uid
|
||||
}
|
||||
|
||||
for act in action.child_ids:
|
||||
code = """action = {'model':'%s','type':'%s', %s}""" % (action.model_id.model, act.type, act.usage)
|
||||
exec code in localdict
|
||||
if 'action' in localdict:
|
||||
return localdict['action']
|
||||
|
||||
return False
|
||||
actions_server()
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class ir_attachment(osv.osv):
|
||||
'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_model': fields.char('Resource Object',size=64, readonly=True),
|
||||
'res_id': fields.integer('Resource ID', readonly=True),
|
||||
'link': fields.char('Link', size=256)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ class ir_cron(osv.osv, netsvc.Agent):
|
||||
'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),
|
||||
'model': fields.char('Object', size=64),
|
||||
'function': fields.char('Function', size=64),
|
||||
'args': fields.text('Arguments'),
|
||||
'priority': fields.integer('Priority', help='0=Very Urgent\n10=Not urgent')
|
||||
|
||||
@@ -33,8 +33,8 @@ 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),
|
||||
'field_tbl': fields.char('Object',size=64),
|
||||
'field_name': fields.char('Object field',size=64),
|
||||
'value': fields.char('Default Value',size=64),
|
||||
'uid': fields.many2one('res.users', 'Users'),
|
||||
'page': fields.char('View',size=64),
|
||||
|
||||
@@ -46,7 +46,7 @@ class ir_model(osv.osv):
|
||||
_description = "Objects"
|
||||
_rec_name = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Model Name', size=64, translate=True, required=True),
|
||||
'name': fields.char('Object 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),
|
||||
@@ -82,9 +82,7 @@ class ir_model(osv.osv):
|
||||
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 unlink(self, cr, user, ids, context=None):
|
||||
for model in self.browse(cr, user, ids, context):
|
||||
if model.state <> 'manual':
|
||||
@@ -208,8 +206,8 @@ class ir_model_fields(osv.osv):
|
||||
_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'),
|
||||
'relation': fields.char('Object Relation', size=64),
|
||||
'model_id': fields.many2one('ir.model', 'Object id', required=True, select=True, ondelete='cascade'),
|
||||
'field_description': fields.char('Field Label', required=True, size=256),
|
||||
'relate': fields.boolean('Click and Relate'),
|
||||
|
||||
@@ -252,8 +250,8 @@ class ir_model_fields(osv.osv):
|
||||
|
||||
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']
|
||||
model_data=self.pool.get('ir.model').browse(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)
|
||||
@@ -270,7 +268,7 @@ 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),
|
||||
'model_id': fields.many2one('ir.model', 'Object', required=True),
|
||||
'group_id': fields.many2one('res.groups', 'Group'),
|
||||
'perm_read': fields.boolean('Read Access'),
|
||||
'perm_write': fields.boolean('Write Access'),
|
||||
@@ -353,7 +351,7 @@ 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),
|
||||
'model': fields.char('Object', required=True, size=64),
|
||||
'module': fields.char('Module', required=True, size=64),
|
||||
'res_id': fields.integer('Resource ID'),
|
||||
'noupdate': fields.boolean('Non Updatable'),
|
||||
@@ -571,3 +569,5 @@ class ir_model_config(osv.osv):
|
||||
'target':'new',
|
||||
}
|
||||
ir_model_config()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -38,7 +38,7 @@ 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),
|
||||
'model_id': fields.many2one('ir.model','Object', 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),
|
||||
|
||||
@@ -38,7 +38,7 @@ class ir_rule_group(osv.osv):
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=128, select=1),
|
||||
'model_id': fields.many2one('ir.model', 'Model',select=1, required=True),
|
||||
'model_id': fields.many2one('ir.model', 'Object',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'),
|
||||
|
||||
@@ -34,7 +34,7 @@ import tools
|
||||
|
||||
TRANSLATION_TYPE = [
|
||||
('field', 'Field'),
|
||||
('model', 'Model'),
|
||||
('model', 'Object'),
|
||||
('rml', 'RML'),
|
||||
('selection', 'Selection'),
|
||||
('view', 'View'),
|
||||
|
||||
@@ -35,15 +35,15 @@ import netsvc
|
||||
import os
|
||||
|
||||
def _check_xml(self, cr, uid, ids, context={}):
|
||||
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'))
|
||||
frng = tools.file_open(os.path.join('base','rng','view.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
|
||||
|
||||
@@ -60,7 +60,7 @@ 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),
|
||||
'model': fields.char('Object', size=64, required=True),
|
||||
'priority': fields.integer('Priority', required=True),
|
||||
'type': fields.selection((
|
||||
('tree','Tree'),
|
||||
@@ -68,7 +68,7 @@ class view(osv.osv):
|
||||
('graph', 'Graph'),
|
||||
('calendar', 'Calendar')), 'View Type', required=True),
|
||||
'arch': fields.text('View Architecture', required=True),
|
||||
'inherit_id': fields.many2one('ir.ui.view', 'Inherited View'),
|
||||
'inherit_id': fields.many2one('ir.ui.view', 'Inherited View', ondelete='cascade'),
|
||||
'field_parent': fields.char('Childs Field',size=64),
|
||||
}
|
||||
_defaults = {
|
||||
@@ -116,7 +116,7 @@ class view(osv.osv):
|
||||
|
||||
return result
|
||||
|
||||
return super(view, self).write(cr, uid, ids, vals, context)
|
||||
return super(view, self).write(cr, uid, ids, vals, context)
|
||||
view()
|
||||
|
||||
class view_sc(osv.osv):
|
||||
|
||||
@@ -59,7 +59,7 @@ class ir_values(osv.osv):
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=128),
|
||||
'model': fields.char('Model', size=128),
|
||||
'model': fields.char('Object', size=128),
|
||||
'value': fields.text('Value'),
|
||||
'value_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle,
|
||||
method=True, type='text', string='Value'),
|
||||
|
||||
@@ -33,7 +33,7 @@ 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),
|
||||
'model_id': fields.many2one('ir.model','Object', 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'),
|
||||
|
||||
@@ -38,7 +38,7 @@ class workflow(osv.osv):
|
||||
_log_access = False
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, required=True),
|
||||
'osv': fields.char('Resource Model', size=64, required=True,select=True),
|
||||
'osv': fields.char('Resource Object', size=64, required=True,select=True),
|
||||
'on_create': fields.boolean('On Create', select=True),
|
||||
'activities': fields.one2many('workflow.activity', 'wkf_id', 'Activities'),
|
||||
}
|
||||
@@ -145,7 +145,7 @@ class wkf_instance(osv.osv):
|
||||
'wkf_id': fields.many2one('workflow', 'Workflow', ondelete='restrict', select=True),
|
||||
'uid': fields.integer('User ID'),
|
||||
'res_id': fields.integer('Resource ID', select=True),
|
||||
'res_type': fields.char('Resource Model', size=64, select=True),
|
||||
'res_type': fields.char('Resource Object', size=64, select=True),
|
||||
'state': fields.char('State', size=32, select=True),
|
||||
}
|
||||
def _auto_init(self, cr, context={}):
|
||||
@@ -180,7 +180,7 @@ class wkf_triggers(osv.osv):
|
||||
_log_access = False
|
||||
_columns = {
|
||||
'res_id': fields.integer('Resource ID', size=128),
|
||||
'model': fields.char('Model', size=128),
|
||||
'model': fields.char('Object', size=128),
|
||||
'instance_id': fields.many2one('workflow.instance', 'Destination Instance', ondelete="cascade"),
|
||||
'workitem_id': fields.many2one('workflow.workitem', 'Workitem', required=True, ondelete="cascade"),
|
||||
}
|
||||
|
||||
@@ -266,42 +266,28 @@ class module(osv.osv):
|
||||
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):
|
||||
|
||||
def state_update(self, cr, uid, ids, newstate, states_to_update, context={}, level=50):
|
||||
if level<1:
|
||||
raise Exception, _('Recursion error in modules dependencies !')
|
||||
raise orm.except_orm(_('Error'), _('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 dep.state == 'unknown':
|
||||
raise orm.except_orm(_('Error'), _('Unmet dependency: %s') % (dep.name,))
|
||||
if dep.state != newstate:
|
||||
ids2 = self.search(cr, uid, [('name','=',dep.name)])
|
||||
mdemo = self.state_update(cr, uid, ids2, newstate, states_to_update, context, level-1,) and mdemo
|
||||
if not module.dependencies_id:
|
||||
mdemo = module.demo
|
||||
if module.state == 'uninstalled':
|
||||
if module.state in states_to_update:
|
||||
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)
|
||||
return self.state_update(cr, uid, ids, 'to install', ['uninstalled'], context)
|
||||
|
||||
def button_install_cancel(self, cr, uid, ids, context={}):
|
||||
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False})
|
||||
@@ -327,7 +313,7 @@ class module(osv.osv):
|
||||
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)
|
||||
return self.state_update(cr, uid, ids, 'to upgrade', ['installed'], context)
|
||||
def button_upgrade_cancel(self, cr, uid, ids, context={}):
|
||||
self.write(cr, uid, ids, {'state': 'installed'})
|
||||
return True
|
||||
|
||||
@@ -79,15 +79,12 @@
|
||||
<newline/>
|
||||
<field name="state" readonly="1" select="1"/>
|
||||
<group col="6" colspan="2">
|
||||
<button name="button_install" states="uninstalled" string="Install" type="object"/>
|
||||
<button name="button_install" states="uninstalled" string="Schedule for Installation" type="object"/>
|
||||
<button name="button_install_cancel" states="to install" string="Cancel Install" type="object"/>
|
||||
<button name="button_uninstall" states="installed" string="Uninstall (beta)" type="object"/>
|
||||
<button name="button_uninstall_cancel" states="to remove" string="Cancel Uninstall" type="object"/>
|
||||
<button name="button_upgrade" states="installed" string="Upgrade" type="object"/>
|
||||
<button name="button_upgrade" states="installed" string="Schedule Upgrade" type="object"/>
|
||||
<button name="button_upgrade_cancel" states="to upgrade" string="Cancel Upgrade" type="object"/>
|
||||
<!--
|
||||
<button string="Update translations" name="button_update_translations" type="object" states="installed"/>
|
||||
-->
|
||||
</group>
|
||||
</page>
|
||||
<page string="Dependencies">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</record>
|
||||
<menuitem action="wizard_update" icon="STOCK_CONVERT" id="menu_module_update" parent="base.menu_management" type="wizard"/>
|
||||
|
||||
<wizard id="wizard_upgrade" model="ir.module.module" name="module.upgrade" string="Apply upgrades"/>
|
||||
<wizard id="wizard_upgrade" model="ir.module.module" name="module.upgrade" string="Apply Scheduled Upgrades"/>
|
||||
<menuitem action="wizard_upgrade" id="menu_wizard_upgrade" parent="base.menu_management" type="wizard"/>
|
||||
|
||||
<record id="wizard_lang_install" model="ir.actions.wizard">
|
||||
|
||||
@@ -37,81 +37,14 @@ import pooler
|
||||
|
||||
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>
|
||||
</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>
|
||||
</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_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)
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
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),])
|
||||
ids=lang_obj.search(cr, uid, ['&', ('active', '=', True), ('translatable', '=', True),])
|
||||
langs=lang_obj.browse(cr, uid, ids)
|
||||
return [(lang.code, lang.translatable and lang.name or _('New language')) for lang in langs]
|
||||
return [('', _('New language'))] + [(lang.code, lang.name) for lang in langs]
|
||||
|
||||
|
||||
def act_cancel(self, cr, uid, ids, context=None):
|
||||
@@ -132,7 +65,11 @@ class wizard_export_lang(osv.osv_memory):
|
||||
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.")
|
||||
ext = this.lang and '.po' or '.pot'
|
||||
this.advice = _("Save this document to a %s file and edit it with a specific software or a text editor. The file encoding is UTF-8." % (ext,))
|
||||
elif this.format == 'tgz':
|
||||
ext = this.lang and '.po' or '.pot'
|
||||
this.advice = _('Save this document to a .tgz file. This archive containt UTF-8 %s files and may be uploaded to launchpad.' % (ext,))
|
||||
|
||||
out=base64.encodestring(buf.getvalue())
|
||||
buf.close()
|
||||
@@ -140,8 +77,8 @@ class wizard_export_lang(osv.osv_memory):
|
||||
|
||||
_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),
|
||||
'lang': fields.selection(_get_languages, 'Language'), # not required: unset = new language
|
||||
'format': fields.selection( ( ('csv','CSV File'), ('po','PO File'), ('tgz', 'TGZ Archive')), '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),
|
||||
|
||||
@@ -89,6 +89,15 @@ class wizard_info_get(wizard.interface):
|
||||
mod_obj = pool.get('ir.module.module')
|
||||
ids = mod_obj.search(cr, uid, [
|
||||
('state', 'in', ['to upgrade', 'to remove', 'to install'])])
|
||||
unmet_packages=[]
|
||||
mod_dep_obj = pool.get('ir.module.module.dependency')
|
||||
for mod in mod_obj.browse(cr,uid,ids):
|
||||
depends_mod_ids=mod_dep_obj.search(cr,uid,[('module_id','=',mod.id)])
|
||||
for dep_mod in mod_dep_obj.browse(cr,uid,depends_mod_ids):
|
||||
if dep_mod.state in ('unknown','uninstalled'):
|
||||
unmet_packages.append(dep_mod.name)
|
||||
if len(unmet_packages):
|
||||
raise wizard.except_wizard('Unmet dependency !','Following modules are uninstalled or unknown. \n\n'+'\n'.join(unmet_packages))
|
||||
mod_obj.download(cr, uid, ids, context=context)
|
||||
cr.commit()
|
||||
db, pool = pooler.restart_pool(cr.dbname, update_module=True)
|
||||
|
||||
@@ -142,6 +142,7 @@ class res_partner(osv.osv):
|
||||
'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"),
|
||||
'vat_subject': fields.boolean('Subjected to the VAT'),
|
||||
'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'),
|
||||
'website': fields.char('Website',size=64),
|
||||
'comment': fields.text('Notes'),
|
||||
|
||||
@@ -279,8 +279,7 @@
|
||||
<page string="Event History">
|
||||
<field colspan="4" name="events" nolabel="1" widget="one2many_list"/>
|
||||
</page>
|
||||
<page groups="base.group_extended" string="Properties">
|
||||
<properties/>
|
||||
<page string="Properties">
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
|
||||
@@ -97,9 +97,9 @@ class res_company(osv.osv):
|
||||
|
||||
def _get_euro(self, cr, uid, context={}):
|
||||
try:
|
||||
return self.pool.get('res.currency').search(cr, uid, [('rate', '=', 1.0),])[0]
|
||||
return self.pool.get('res.currency').search(cr, uid, [])[0]
|
||||
except:
|
||||
return 1
|
||||
return False
|
||||
|
||||
def _check_recursion(self, cr, uid, ids):
|
||||
level = 100
|
||||
|
||||
@@ -60,8 +60,7 @@
|
||||
<field name="name">Requests</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.request</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="res_request_tree-view"/>
|
||||
<field name="view_id" eval="False"/>
|
||||
</record>
|
||||
<menuitem id="next_id_12" name="Requests" parent="base.next_id_4"/><menuitem action="res_request-act" id="menu_res_request_act" parent="next_id_12"/>
|
||||
|
||||
@@ -90,11 +89,11 @@
|
||||
</record>
|
||||
|
||||
<record id="res_request_link-act" model="ir.actions.act_window">
|
||||
<field name="name">Request</field>
|
||||
<field name="name">Accepted Links in Requests</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">res.request.link</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="res_request_link-view"/>
|
||||
<field name="view_id" eval="False"/>
|
||||
</record>
|
||||
<menuitem action="res_request_link-act" id="menu_res_request_link_act" parent="base.next_id_12"/>
|
||||
|
||||
@@ -128,4 +127,4 @@
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</terp>
|
||||
</terp>
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rng:grammar xmlns:rng="http://relaxng.org/ns/structure/1.0">
|
||||
<rng:define name="form">
|
||||
<rng:element name="form">
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="col"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="editable"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="type"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:ref name="notebook"/>
|
||||
<rng:ref name="field"/>
|
||||
<rng:ref name="group"/>
|
||||
<rng:ref name="button"/>
|
||||
<rng:ref name="hpaned" />
|
||||
<rng:ref name="label" />
|
||||
<rng:ref name="separator"/>
|
||||
<rng:ref name="image"/>
|
||||
<rng:ref name="form"/>
|
||||
<rng:element name="newline"><rng:empty/></rng:element>
|
||||
<rng:element name="properties"><rng:empty/></rng:element>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="tree">
|
||||
<rng:element name="tree">
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colors"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="color"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="editable"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="toolbar"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:ref name="field"/>
|
||||
<rng:ref name="separator"/>
|
||||
<rng:ref name="tree"/>
|
||||
<rng:ref name="group"/>
|
||||
<rng:element name="newline"><rng:empty/></rng:element>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="image">
|
||||
<rng:element name="image">
|
||||
<rng:attribute name="name"/>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="label">
|
||||
<rng:element name="label">
|
||||
<rng:optional><rng:attribute name="align"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="nolabel"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="hpaned">
|
||||
<rng:element name="hpaned">
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
<rng:zeroOrMore>
|
||||
<rng:ref name="any" />
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="any">
|
||||
<rng:element>
|
||||
<rng:anyName/>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:attribute>
|
||||
<rng:anyName/>
|
||||
</rng:attribute>
|
||||
<rng:text/>
|
||||
<rng:ref name="any"/>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="page">
|
||||
<rng:element name="page">
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="groups"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:element name="newline"><rng:empty/></rng:element>
|
||||
<rng:element name="properties"><rng:empty/></rng:element>
|
||||
<rng:ref name="label" />
|
||||
<rng:ref name="field"/>
|
||||
<rng:ref name="group"/>
|
||||
<rng:ref name="separator"/>
|
||||
<rng:ref name="button"/>
|
||||
<rng:ref name="form" />
|
||||
<rng:ref name="page" />
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="notebook">
|
||||
<rng:element name="notebook">
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:oneOrMore>
|
||||
<rng:ref name="page"/>
|
||||
</rng:oneOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="separator">
|
||||
<rng:element name="separator">
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="col"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="select"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="xpath">
|
||||
<rng:element name="xpath">
|
||||
<rng:optional><rng:attribute name="expr"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:ref name="notebook" />
|
||||
<rng:ref name="graph" />
|
||||
<rng:ref name="calendar" />
|
||||
<rng:ref name="form"/>
|
||||
<rng:ref name="tree"/>
|
||||
<rng:ref name="field"/>
|
||||
<rng:ref name="separator"/>
|
||||
<rng:ref name="page"/>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="field">
|
||||
<rng:element name="field">
|
||||
<rng:attribute name="name" />
|
||||
<rng:optional><rng:attribute name="editable"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="completion"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="width"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="type"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="ref"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="eval"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="search"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="model"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="use"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="on_change"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="domain"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="invisible"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="visible"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="sum"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="select"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="group"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="operator"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="nolabel"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="default_get"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="required"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="readonly"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="view_mode"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="widget"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="context"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="groups"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="states"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="digits"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="icon"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="mode"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="size"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="height"/></rng:optional>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:ref name="form"/>
|
||||
<rng:ref name="tree"/>
|
||||
<rng:ref name="field"/>
|
||||
<rng:ref name="separator"/>
|
||||
<rng:ref name="xpath"/>
|
||||
<rng:ref name="button"/>
|
||||
<rng:ref name="group"/>
|
||||
<rng:element name="newline"><rng:empty/></rng:element>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="group">
|
||||
<rng:element name="group">
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="col"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="expand"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="states"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="groups"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="fill"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="height"/></rng:optional>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:ref name="separator"/>
|
||||
<rng:ref name="button"/>
|
||||
<rng:ref name="field"/>
|
||||
<rng:ref name="label" />
|
||||
<rng:ref name="group" />
|
||||
<rng:element name="properties"><rng:empty/></rng:element>
|
||||
<rng:element name="newline"><rng:empty/></rng:element>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="calendar">
|
||||
<rng:element name="calendar">
|
||||
<rng:optional><rng:attribute name="string" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="color" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="date_start" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="date_stop" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="day_length" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="date_delay" /></rng:optional>
|
||||
<rng:oneOrMore>
|
||||
<rng:ref name="field"/>
|
||||
</rng:oneOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="graph">
|
||||
<rng:element name="graph">
|
||||
<rng:optional><rng:attribute name="string" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="orientation" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="type" /></rng:optional>
|
||||
<rng:oneOrMore>
|
||||
<rng:ref name="field"/>
|
||||
</rng:oneOrMore>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:define name="button">
|
||||
<rng:element name="button">
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="name" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="icon" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="string" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="states" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="type" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="special" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="align" /></rng:optional>
|
||||
<rng:optional><rng:attribute name="colspan"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="groups"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="target"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="readonly"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="position"/></rng:optional>
|
||||
<rng:zeroOrMore>
|
||||
<rng:choice>
|
||||
<rng:ref name="form" />
|
||||
<rng:ref name="field" />
|
||||
<rng:ref name="tree" />
|
||||
<rng:ref name="notebook" />
|
||||
<rng:ref name="graph" />
|
||||
<rng:ref name="calendar" />
|
||||
<rng:ref name="xpath" />
|
||||
<rng:ref name="page" />
|
||||
<rng:ref name="separator"/>
|
||||
<rng:ref name="button"/>
|
||||
<rng:element name="properties"><rng:empty/></rng:element>
|
||||
<rng:element name="newline"><rng:empty/></rng:element>
|
||||
</rng:choice>
|
||||
</rng:zeroOrMore>
|
||||
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
<rng:start>
|
||||
<rng:choice>
|
||||
<rng:ref name="form" />
|
||||
<rng:ref name="field" />
|
||||
<rng:ref name="tree" />
|
||||
<rng:ref name="notebook" />
|
||||
<rng:ref name="graph" />
|
||||
<rng:ref name="calendar" />
|
||||
<rng:ref name="xpath" />
|
||||
<rng:ref name="page" />
|
||||
<rng:ref name="separator"/>
|
||||
<rng:ref name="button"/>
|
||||
</rng:choice>
|
||||
</rng:start>
|
||||
</rng:grammar>
|
||||
@@ -158,6 +158,7 @@
|
||||
<rng:optional> <rng:attribute name="sequence"/> </rng:optional>
|
||||
<rng:optional> <rng:attribute name="groups"/> </rng:optional>
|
||||
<rng:optional> <rng:attribute name="type"/> </rng:optional>
|
||||
<rng:optional> <rng:attribute name="menu"/> </rng:optional>
|
||||
<rng:empty />
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
+104
-88
@@ -31,56 +31,62 @@
|
||||
import time
|
||||
import threading
|
||||
|
||||
import SimpleXMLRPCServer,signal,sys,xmlrpclib
|
||||
import SimpleXMLRPCServer, signal, sys, xmlrpclib
|
||||
import SocketServer
|
||||
import socket
|
||||
import logging
|
||||
import os
|
||||
|
||||
_service={}
|
||||
_group={}
|
||||
_res_id=1
|
||||
_res={}
|
||||
_service = {}
|
||||
_group = {}
|
||||
_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)
|
||||
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]
|
||||
self._meth = {}
|
||||
s = _service[name]
|
||||
for m in s._method:
|
||||
self._meth[m]=s._method[m]
|
||||
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):
|
||||
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
|
||||
_group[name] = {}
|
||||
_group[name][self.__name] = self
|
||||
|
||||
def exportMethod(self, m):
|
||||
if callable(m):
|
||||
self._method[m.__name__]=m
|
||||
self._method[m.__name__] = m
|
||||
|
||||
def serviceEndPoint(self,s):
|
||||
def serviceEndPoint(self, s):
|
||||
if Service._serviceEndPointID >= 2**16:
|
||||
Service._serviceEndPointID = 0
|
||||
Service._serviceEndPointID += 1
|
||||
@@ -89,57 +95,62 @@ class Service(object):
|
||||
def conversationId(self):
|
||||
return 1
|
||||
|
||||
def processResponse(self,s,id):
|
||||
def processResponse(self, s, id):
|
||||
self._response_process, self._response_process_id = s, id
|
||||
|
||||
def processFailure(self,s,id):
|
||||
def processFailure(self, s, id):
|
||||
pass
|
||||
|
||||
def resumeResponse(self,s):
|
||||
def resumeResponse(self, s):
|
||||
pass
|
||||
|
||||
def cancelResponse(self,s):
|
||||
def cancelResponse(self, s):
|
||||
pass
|
||||
|
||||
def suspendResponse(self,s):
|
||||
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)
|
||||
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))
|
||||
raise Exception("%s -- %s\n\n%s"%(origin, description, details))
|
||||
else:
|
||||
raise
|
||||
|
||||
def currentFailure(self,s):
|
||||
def currentFailure(self, s):
|
||||
pass
|
||||
|
||||
|
||||
class LocalService(Service):
|
||||
def __init__(self, name):
|
||||
self.__name=name
|
||||
s=_service[name]
|
||||
self._service=s
|
||||
self.__name = name
|
||||
s = _service[name]
|
||||
self._service = s
|
||||
for m in s._method:
|
||||
setattr(self,m,s._method[m])
|
||||
setattr(self, m, s._method[m])
|
||||
|
||||
|
||||
class ServiceUnavailable(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def service_exist(name):
|
||||
return (name in _service) and bool(_service[name])
|
||||
|
||||
|
||||
def get_rpc_paths():
|
||||
return map(lambda s: '/xmlrpc/%s' % s, _service)
|
||||
|
||||
LOG_DEBUG='debug'
|
||||
LOG_INFO='info'
|
||||
LOG_WARNING='warn'
|
||||
LOG_ERROR='error'
|
||||
LOG_CRITICAL='critical'
|
||||
LOG_DEBUG = 'debug'
|
||||
LOG_INFO = 'info'
|
||||
LOG_WARNING = 'warn'
|
||||
LOG_ERROR = 'error'
|
||||
LOG_CRITICAL = 'critical'
|
||||
|
||||
|
||||
def init_logger():
|
||||
from tools import config
|
||||
@@ -149,15 +160,8 @@ def init_logger():
|
||||
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:
|
||||
handler = logging.handlers.TimedRotatingFileHandler(logf,'D',1,30)
|
||||
except:
|
||||
sys.stderr.write("ERROR: couldn't create the logfile directory\n")
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
else:
|
||||
@@ -175,9 +179,10 @@ def init_logger():
|
||||
|
||||
|
||||
class Logger(object):
|
||||
def notifyChannel(self,name,level,msg):
|
||||
def notifyChannel(self, name, level, msg):
|
||||
log = logging.getLogger(name)
|
||||
getattr(log,level)(msg)
|
||||
getattr(log, level)(msg)
|
||||
|
||||
|
||||
class Agent(object):
|
||||
_timers = []
|
||||
@@ -185,9 +190,9 @@ class Agent(object):
|
||||
|
||||
def setAlarm(self, fn, dt, args=None, kwargs=None):
|
||||
if not args:
|
||||
args=[]
|
||||
args = []
|
||||
if not kwargs:
|
||||
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))
|
||||
@@ -201,42 +206,49 @@ class Agent(object):
|
||||
def quit(cls):
|
||||
for timer in cls._timers:
|
||||
timer.cancel()
|
||||
quit=classmethod(quit)
|
||||
quit = classmethod(quit)
|
||||
|
||||
|
||||
class RpcGateway(object):
|
||||
def __init__(self, name):
|
||||
self.name=name
|
||||
self.name = name
|
||||
|
||||
|
||||
class Dispatcher(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
def monitor(self,signal):
|
||||
|
||||
def monitor(self, signal):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
pass
|
||||
|
||||
|
||||
class xmlrpc(object):
|
||||
class RpcGateway(object):
|
||||
def __init__(self, name):
|
||||
self.name=name
|
||||
self.name = name
|
||||
|
||||
|
||||
class GenericXMLRPCRequestHandler:
|
||||
def _dispatch(self, method, params):
|
||||
#print 'TERP-CALL : ',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
|
||||
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)
|
||||
s = str(e)
|
||||
import tools
|
||||
if tools.config['debug_mode']:
|
||||
import pdb
|
||||
@@ -244,10 +256,12 @@ class GenericXMLRPCRequestHandler:
|
||||
pdb.post_mortem(tb)
|
||||
raise xmlrpclib.Fault(s, tb_s)
|
||||
|
||||
|
||||
class SimpleXMLRPCRequestHandler(GenericXMLRPCRequestHandler,
|
||||
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
|
||||
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.rpc_paths = get_rpc_paths()
|
||||
|
||||
|
||||
class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn,
|
||||
SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||
|
||||
@@ -256,12 +270,13 @@ class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn,
|
||||
socket.SO_REUSEADDR, 1)
|
||||
SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self)
|
||||
|
||||
|
||||
class HttpDaemon(threading.Thread):
|
||||
|
||||
def __init__(self, interface,port, secure=False):
|
||||
def __init__(self, interface, port, secure=False):
|
||||
threading.Thread.__init__(self)
|
||||
self.__port=port
|
||||
self.__interface=interface
|
||||
self.__port = port
|
||||
self.__interface = interface
|
||||
self.secure = secure
|
||||
if secure:
|
||||
from ssl import SecureXMLRPCServer
|
||||
@@ -277,17 +292,17 @@ class HttpDaemon(threading.Thread):
|
||||
SecureXMLRPCServer.SecureXMLRPCServer.server_bind(self)
|
||||
|
||||
self.server = SecureThreadedXMLRPCServer((interface, port),
|
||||
SecureXMLRPCRequestHandler,0)
|
||||
SecureXMLRPCRequestHandler, 0)
|
||||
else:
|
||||
self.server = SimpleThreadedXMLRPCServer((interface, port),
|
||||
SimpleXMLRPCRequestHandler,0)
|
||||
SimpleXMLRPCRequestHandler, 0)
|
||||
|
||||
def attach(self,path,gw):
|
||||
def attach(self, path, gw):
|
||||
pass
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
if os.name <> 'nt':
|
||||
if os.name != 'nt':
|
||||
if hasattr(socket, 'SHUT_RDWR'):
|
||||
if self.secure:
|
||||
self.server.socket.sock_shutdown(socket.SHUT_RDWR)
|
||||
@@ -342,17 +357,17 @@ class TinySocketClientThread(threading.Thread):
|
||||
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
|
||||
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)
|
||||
s = str(e)
|
||||
import tools
|
||||
if tools.config['debug_mode']:
|
||||
import pdb
|
||||
@@ -368,11 +383,12 @@ class TinySocketClientThread(threading.Thread):
|
||||
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.__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))
|
||||
@@ -394,7 +410,7 @@ class TinySocketServerThread(threading.Thread):
|
||||
return False
|
||||
|
||||
def stop(self):
|
||||
self.running=False
|
||||
self.running = False
|
||||
for t in self.threads:
|
||||
t.stop()
|
||||
try:
|
||||
|
||||
+206
-249
@@ -1,64 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from tools import flatten
|
||||
from tools import flatten, reverse_enumerate
|
||||
|
||||
|
||||
class expression(object):
|
||||
"""
|
||||
parse a domain expression
|
||||
examples:
|
||||
|
||||
>>> e = [('foo', '=', 'bar')]
|
||||
>>> expression(e).parse().to_sql()
|
||||
'foo = bar'
|
||||
>>> e = [('id', 'in', [1,2,3])]
|
||||
>>> expression(e).parse().to_sql()
|
||||
'id in (1, 2, 3)'
|
||||
>>> e = [('field', '=', 'value'), ('field', '<>', 'value')]
|
||||
>>> expression(e).parse().to_sql()
|
||||
'( field = value AND field <> value )'
|
||||
>>> e = [('&', ('field', '<', 'value'), ('field', '>', 'value'))]
|
||||
>>> expression(e).parse().to_sql()
|
||||
'( field < value AND field > value )'
|
||||
>>> e = [('|', ('field', '=', 'value'), ('field', '=', 'value'))]
|
||||
>>> expression(e).parse().to_sql()
|
||||
'( field = value OR field = value )'
|
||||
>>> e = [('&', ('field1', '=', 'value'), ('field2', '=', 'value'), ('|', ('field3', '<>', 'value'), ('field4', '=', 'value')))]
|
||||
>>> expression(e).parse().to_sql()
|
||||
'( field1 = value AND field2 = value AND ( field3 <> value OR field4 = value ) )'
|
||||
>>> e = [('&', ('|', ('a', '=', '1'), ('b', '=', '2')), ('|', ('c', '=', '3'), ('d', '=', '4')))]
|
||||
>>> expression(e).parse().to_sql()
|
||||
'( ( a = 1 OR b = 2 ) AND ( c = 3 OR d = 4 ) )'
|
||||
>>> e = [('|', (('a', '=', '1'), ('b', '=', '2')), (('c', '=', '3'), ('d', '=', '4')))]
|
||||
>>> expression(e).parse().to_sql()
|
||||
'( ( a = 1 AND b = 2 ) OR ( c = 3 AND d = 4 ) )'
|
||||
>>> expression(e).parse().get_tables()
|
||||
[]
|
||||
>>> expression('fail').parse().to_sql()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Bad expression: 'fail'
|
||||
>>> e = [('fail', 'is', 'True')]
|
||||
>>> expression(e).parse().to_sql()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Bad expression: ('&', ('fail', 'is', 'True'))
|
||||
use a real polish notation
|
||||
leafs are still in a ('foo', '=', 'bar') format
|
||||
For more info: http://christophe-simonis-at-tiny.blogspot.com/2008/08/new-new-domain-notation.html
|
||||
"""
|
||||
|
||||
def _is_operator(self, element):
|
||||
return isinstance(element, str) \
|
||||
and element in ['&','|']
|
||||
and element in ['&', '|', '!']
|
||||
|
||||
def _is_leaf(self, element):
|
||||
return isinstance(element, tuple) \
|
||||
def _is_leaf(self, element, internal=False):
|
||||
OPS = ('=', '!=', '<>', '<=', '<', '>', '>=', '=like', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in', 'child_of')
|
||||
INTERNAL_OPS = OPS + ('inselect',)
|
||||
return (isinstance(element, tuple) or isinstance(element, list)) \
|
||||
and len(element) == 3 \
|
||||
and element[1] in ('=', '!=', '<>', '<=', '<', '>', '>=', '=like', 'like', 'not like', 'ilike', 'not ilike', 'in', 'not in', 'child_of')
|
||||
|
||||
def _is_expression(self, element):
|
||||
return isinstance(element, tuple) \
|
||||
and len(element) > 2 \
|
||||
and self._is_operator(element[0])
|
||||
|
||||
and (((not internal) and element[1] in OPS) \
|
||||
or (internal and element[1] in INTERNAL_OPS))
|
||||
|
||||
def __execute_recursive_in(self, cr, s, f, w, ids):
|
||||
res = []
|
||||
@@ -73,271 +37,264 @@ class expression(object):
|
||||
|
||||
|
||||
def __init__(self, exp):
|
||||
if exp and isinstance(exp, tuple):
|
||||
if not self._is_leaf(exp) and not self._is_operator(exp[0]):
|
||||
exp = list(exp)
|
||||
if exp and isinstance(exp, list):
|
||||
if len(exp) == 1 and self._is_leaf(exp[0]):
|
||||
exp = exp[0]
|
||||
else:
|
||||
if not self._is_operator(exp[0][0]):
|
||||
if isinstance(exp[0],list):
|
||||
exp=tuple(exp[0])
|
||||
else:
|
||||
exp.insert(0, '&')
|
||||
exp = tuple(exp)
|
||||
else:
|
||||
exp = exp[0]
|
||||
|
||||
# check if the expression is valid
|
||||
if not reduce(lambda acc, val: acc and (self._is_operator(val) or self._is_leaf(val)), exp, True):
|
||||
raise ValueError('Bad domain expression: %r' % (exp,))
|
||||
self.__exp = exp
|
||||
self.__operator = '&'
|
||||
self.__children = []
|
||||
|
||||
self.__tables = []
|
||||
self.__tables = {} # used to store the table to use for the sql generation. key = index of the leaf
|
||||
self.__joins = []
|
||||
self.__table = None
|
||||
self.__main_table = None # 'root' table. set by parse()
|
||||
self.__DUMMY_LEAF = (1, '=', 1) # a dummy leaf that must not be parsed or sql generated
|
||||
|
||||
self.__left, self.__right = None, None
|
||||
if self._is_leaf(self.__exp):
|
||||
self.__left, self.__operator, self.__right = self.__exp
|
||||
if isinstance(self.__right, list):
|
||||
self.__right = tuple(self.__right)
|
||||
elif exp and not self._is_expression(self.__exp):
|
||||
raise ValueError, 'Bad expression: %r' % (self.__exp,)
|
||||
|
||||
def parse(self, cr, uid, table, context):
|
||||
|
||||
def _rec_get(ids, table, parent):
|
||||
if not ids:
|
||||
return []
|
||||
ids2 = table.search(cr, uid, [(parent, 'in', ids)], context=context)
|
||||
return ids + _rec_get(ids2, table, parent)
|
||||
|
||||
""" transform the leafs of the expression """
|
||||
if not self.__exp:
|
||||
return self
|
||||
|
||||
if self._is_leaf(self.__exp):
|
||||
self.__table = table
|
||||
self.__tables.append(self.__table._table)
|
||||
if self.__left in table._inherit_fields:
|
||||
self.__table = table.pool.get(table._inherit_fields[self.__left][0])
|
||||
if self.__table._table not in self.__tables:
|
||||
self.__tables.append(self.__table._table)
|
||||
self.__joins.append('%s.%s' % (table._table, table._inherits[self.__table._name]))
|
||||
fargs = self.__left.split('.', 1)
|
||||
field = self.__table._columns.get(fargs[0], False)
|
||||
def _rec_get(ids, table, parent, left='id', prefix=''):
|
||||
if table._parent_store and (not table.pool._init):
|
||||
# TODO: Improve where joins are implemented for many with '.', replace by:
|
||||
# doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)]
|
||||
doms = []
|
||||
for o in table.browse(cr, uid, ids, context=context):
|
||||
if doms:
|
||||
doms.insert(0,'|')
|
||||
doms += ['&',('parent_left','<',o.parent_right),('parent_left','>=',o.parent_left)]
|
||||
if prefix:
|
||||
return [(left, 'in', table.search(cr, uid, doms, context=context))]
|
||||
return doms
|
||||
else:
|
||||
def rg(ids, table, parent):
|
||||
if not ids:
|
||||
return []
|
||||
ids2 = table.search(cr, uid, [(parent, 'in', ids)], context=context)
|
||||
return ids+rg(ids2, table, parent)
|
||||
return [(prefix+left, 'in', rg(ids2, table, parent))]
|
||||
|
||||
self.__main_table = table
|
||||
|
||||
i = -1
|
||||
while i+1<len(self.__exp):
|
||||
i+=1
|
||||
e = self.__exp[i]
|
||||
if self._is_operator(e) or e == self.__DUMMY_LEAF:
|
||||
continue
|
||||
left, operator, right = e
|
||||
|
||||
working_table = table
|
||||
if left in table._inherit_fields:
|
||||
working_table = table.pool.get(table._inherit_fields[left][0])
|
||||
if working_table not in self.__tables.values():
|
||||
self.__joins.append(('%s.%s=%s.%s' % (working_table._table, 'id', table._table, table._inherits[working_table._name]), working_table._table))
|
||||
|
||||
self.__tables[i] = working_table
|
||||
|
||||
fargs = left.split('.', 1)
|
||||
field = working_table._columns.get(fargs[0], False)
|
||||
if not field:
|
||||
if self.__left == 'id' and self.__operator == 'child_of':
|
||||
self.__right += _rec_get(self.__right, self.__table, self.__table._parent_name)
|
||||
self.__operator = 'in'
|
||||
return self
|
||||
if len(fargs) > 1:
|
||||
if field._type == 'many2one':
|
||||
self.__left = fargs[0]
|
||||
self.__right = table.pool.get(field._obj).search(cr, uid, [(fargs[1], self.__operator, self.__right)], context=context)
|
||||
self.__operator = 'in'
|
||||
return self
|
||||
if left == 'id' and operator == 'child_of':
|
||||
dom = _rec_get(right, working_table, working_table._parent_name)
|
||||
self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
|
||||
continue
|
||||
|
||||
field_obj = table.pool.get(field._obj)
|
||||
if len(fargs) > 1:
|
||||
if field._type == 'many2one':
|
||||
right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context)
|
||||
self.__exp[i] = (fargs[0], 'in', right)
|
||||
continue
|
||||
|
||||
if field._properties:
|
||||
# this is a function field
|
||||
if not field._fnct_search and not field.store:
|
||||
# the function field doesn't provide a search function and doesn't store values in the database, so we must ignore it : we generate a dummy leaf
|
||||
self.__left, self.__operator, self.__right = 1, '=', 1
|
||||
self.__exp = '' # force to generate an empty sql expression
|
||||
else:
|
||||
# we need to replace this leaf to a '&' expression
|
||||
# we clone ourself...
|
||||
import copy
|
||||
newexp = copy.copy(self)
|
||||
self.__table = None
|
||||
self.__tables, self.__joins = [], []
|
||||
self.__children = []
|
||||
if not field.store:
|
||||
if not field._fnct_search:
|
||||
# the function field doesn't provide a search function and doesn't store
|
||||
# values in the database, so we must ignore it : we generate a dummy leaf
|
||||
self.__exp[i] = self.__DUMMY_LEAF
|
||||
else:
|
||||
subexp = field.search(cr, uid, table, left, [self.__exp[i]])
|
||||
# we assume that the expression is valid
|
||||
# we create a dummy leaf for forcing the parsing of the resulting expression
|
||||
self.__exp[i] = '&'
|
||||
self.__exp.insert(i + 1, self.__DUMMY_LEAF)
|
||||
for j, se in enumerate(subexp):
|
||||
self.__exp.insert(i + 2 + j, se)
|
||||
|
||||
# else, the value of the field is store in the database, so we search on it
|
||||
|
||||
if field._fnct_search:
|
||||
subexp = field.search(cr, uid, table, self.__left, [self.__exp])
|
||||
self.__children.append(expression(subexp).parse(cr, uid, table, context))
|
||||
if field.store:
|
||||
self.__children.append(newexp)
|
||||
|
||||
self.__left, self.__right = None, None
|
||||
self.__operator = '&'
|
||||
self.__exp = ('&',) + tuple( [tuple(e.__exp) for e in self.__children] )
|
||||
|
||||
elif field._type == 'one2many':
|
||||
if isinstance(self.__right, basestring):
|
||||
ids2 = [x[0] for x in field_obj.name_search(cr, uid, self.__right, [], self.__operator)]
|
||||
if isinstance(right, basestring):
|
||||
ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator)]
|
||||
else:
|
||||
ids2 = self.__right
|
||||
ids2 = list(right)
|
||||
if not ids2:
|
||||
self.__left, self.__operator, self.__right = 'id', '=', '0'
|
||||
self.__exp[i] = ('id', '=', '0')
|
||||
else:
|
||||
self.__left, self.__operator, self.__right = 'id', 'in', self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2)
|
||||
self.__exp[i] = ('id', 'in', self.__execute_recursive_in(cr, field._fields_id, field_obj._table, 'id', ids2))
|
||||
|
||||
elif field._type == 'many2many':
|
||||
#FIXME
|
||||
if self.__operator == 'child_of':
|
||||
if isinstance(self.__right, basestring):
|
||||
ids2 = [x[0] for x in field_obj.name_search(cr, uid, self.__right, [], 'like')]
|
||||
if operator == 'child_of':
|
||||
if isinstance(right, basestring):
|
||||
ids2 = [x[0] for x in field_obj.name_search(cr, uid, right, [], 'like')]
|
||||
else:
|
||||
ids2 = self.__right
|
||||
ids2 = list(right)
|
||||
|
||||
def _rec_convert(ids):
|
||||
if field_obj == table:
|
||||
return ids
|
||||
return self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, ids)
|
||||
|
||||
self.__left, self.__operator, self.__right = 'id', 'in', _rec_convert(ids2 + _rec_get(ids2, field_obj, self.__table._parent_name))
|
||||
|
||||
dom = _rec_get(ids2, field_obj, working_table._parent_name)
|
||||
ids2 = field_obj.search(cr, uid, doms, context=context)
|
||||
self.__exp[i] = ('id', 'in', _rec_convert(ids2))
|
||||
else:
|
||||
if isinstance(self.__right, basestring):
|
||||
res_ids = [x[0] for x in field_obj.name_search(cr, uid, self.__right, [], self.__operator)]
|
||||
if isinstance(right, basestring):
|
||||
res_ids = [x[0] for x in field_obj.name_search(cr, uid, right, [], operator)]
|
||||
else:
|
||||
res_ids = self.__right
|
||||
self.__left, self.__operator, self.__right = 'id', 'in', self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, res_ids) or [0]
|
||||
res_ids = list(right)
|
||||
self.__exp[i] = ('id', 'in', self.__execute_recursive_in(cr, field._id1, field._rel, field._id2, res_ids) or [0])
|
||||
elif field._type == 'many2one':
|
||||
if self.__operator == 'child_of':
|
||||
if isinstance(self.__right, basestring):
|
||||
ids2 = [x[0] for x in field_obj.search_name(cr, uid, self.__right, [], 'like')]
|
||||
if operator == 'child_of':
|
||||
if isinstance(right, basestring):
|
||||
ids2 = [x[0] for x in field_obj.search_name(cr, uid, right, [], 'like')]
|
||||
else:
|
||||
ids2 = list(self.__right)
|
||||
ids2 = list(right)
|
||||
|
||||
self.__operator = 'in'
|
||||
if field._obj <> self.__table._name:
|
||||
self.__right = ids2 + _rec_get(ids2, field_obj, self.__table._parent_name)
|
||||
if field._obj != working_table._name:
|
||||
dom = _rec_get(ids2, field_obj, working_table._parent_name, left=left)
|
||||
else:
|
||||
self.__right = ids2 + _rec_get(ids2, self.__table, self.__left)
|
||||
self.__left = 'id'
|
||||
dom = _rec_get(ids2, working_table, left)
|
||||
self.__exp = self.__exp[:i] + dom + self.__exp[i+1:]
|
||||
else:
|
||||
if isinstance(self.__right, basestring):
|
||||
res_ids = field_obj.name_search(cr, uid, self.__right, [], self.__operator)
|
||||
self.__operator = 'in'
|
||||
self.__right = map(lambda x: x[0], res_ids)
|
||||
if isinstance(right, basestring):
|
||||
res_ids = field_obj.name_search(cr, uid, right, [], operator)
|
||||
right = map(lambda x: x[0], res_ids)
|
||||
self.__exp[i] = (left, 'in', right)
|
||||
else:
|
||||
# other field type
|
||||
if field.translate:
|
||||
if self.__operator in ('like', 'ilike', 'not like', 'not ilike'):
|
||||
self.__right = '%%%s%%' % self.__right
|
||||
if operator in ('like', 'ilike', 'not like', 'not ilike'):
|
||||
right = '%%%s%%' % right
|
||||
|
||||
query1 = '( SELECT res_id' \
|
||||
' FROM ir_translation' \
|
||||
' WHERE name = %s' \
|
||||
' AND lang = %s' \
|
||||
' AND type = %s' \
|
||||
' AND value ' + self.__operator + ' %s' \
|
||||
' AND value ' + operator + ' %s' \
|
||||
') UNION (' \
|
||||
' SELECT id' \
|
||||
' FROM "' + self.__table._table + '"' \
|
||||
' WHERE "' + self.__left + '" ' + self.__operator + ' %s' \
|
||||
' FROM "' + working_table._table + '"' \
|
||||
' WHERE "' + left + '" ' + operator + ' %s' \
|
||||
')'
|
||||
query2 = [self.__table._name + ',' + self.__left,
|
||||
query2 = [working_table._name + ',' + left,
|
||||
context.get('lang', False) or 'en_US',
|
||||
'model',
|
||||
self.__right,
|
||||
self.__right,
|
||||
right,
|
||||
right,
|
||||
]
|
||||
|
||||
self.__left = 'id'
|
||||
self.__operator = 'inselect'
|
||||
self.__right = (query1, query2,)
|
||||
self.__exp[i] = ('id', 'inselect', (query1, query2))
|
||||
|
||||
|
||||
elif self._is_expression(self.__exp):
|
||||
self.__operator = self.__exp[0]
|
||||
|
||||
for element in self.__exp[1:]:
|
||||
if not self._is_operator(element):
|
||||
self.__children.append(expression(element).parse(cr, uid, table, context))
|
||||
return self
|
||||
|
||||
def to_sql(self):
|
||||
if not self.__exp:
|
||||
return ('', [])
|
||||
elif self._is_leaf(self.__exp):
|
||||
if self.__operator == 'inselect':
|
||||
query = '(%s.%s in (%s))' % (self.__table._table, self.__left, self.__right[0])
|
||||
params = self.__right[1]
|
||||
elif self.__operator in ['in', 'not in']:
|
||||
params = self.__right[:]
|
||||
len_before = len(params)
|
||||
for i in range(len_before)[::-1]:
|
||||
if params[i] == False:
|
||||
del params[i]
|
||||
def __leaf_to_sql(self, leaf, table):
|
||||
left, operator, right = leaf
|
||||
|
||||
len_after = len(params)
|
||||
check_nulls = len_after <> len_before
|
||||
query = '(1=0)'
|
||||
if operator == 'inselect':
|
||||
query = '(%s.%s in (%s))' % (table._table, left, right[0])
|
||||
params = right[1]
|
||||
elif operator in ['in', 'not in']:
|
||||
params = right[:]
|
||||
len_before = len(params)
|
||||
for i in range(len_before)[::-1]:
|
||||
if params[i] == False:
|
||||
del params[i]
|
||||
|
||||
if len_after:
|
||||
if self.__left == 'id':
|
||||
instr = ','.join(['%d'] * len_after)
|
||||
else:
|
||||
instr = ','.join([self.__table._columns[self.__left]._symbol_set[0]] * len_after)
|
||||
len_after = len(params)
|
||||
check_nulls = len_after != len_before
|
||||
query = '(1=0)'
|
||||
|
||||
query = '(%s.%s %s (%s))' % (self.__table._table, self.__left, self.__operator, instr)
|
||||
|
||||
if check_nulls:
|
||||
query = '(%s OR %s IS NULL)' % (query, self.__left)
|
||||
else:
|
||||
params = []
|
||||
if self.__right is False and self.__operator == '=':
|
||||
query = '%s IS NULL' % self.__left
|
||||
elif self.__right is False and self.__operator == '<>':
|
||||
query = '%s IS NOT NULL' % self.__left
|
||||
if len_after:
|
||||
if left == 'id':
|
||||
instr = ','.join(['%d'] * len_after)
|
||||
else:
|
||||
if self.__left == 'id':
|
||||
query = '%s.id %s %%s' % (self.__table._table, self.__operator)
|
||||
params = self.__right
|
||||
else:
|
||||
like = self.__operator in ('like', 'ilike', 'not like', 'not ilike')
|
||||
|
||||
op = self.__operator == '=like' and 'like' or self.__operator
|
||||
if self.__left in self.__table._columns:
|
||||
format = like and '%s' or self.__table._columns[self.__left]._symbol_set[0]
|
||||
query = '(%s.%s %s %s)' % (self.__table._table, self.__left, op, format)
|
||||
else:
|
||||
query = "(%s.%s %s '%s')" % (self.__table._table, self.__left, op, self.__right)
|
||||
|
||||
add_null = False
|
||||
if like:
|
||||
if isinstance(self.__right, str):
|
||||
str_utf8 = self.__right
|
||||
elif isinstance(self.__right, unicode):
|
||||
str_utf8 = self.__right.encode('utf-8')
|
||||
else:
|
||||
str_utf8 = str(self.__right)
|
||||
params = '%%%s%%' % str_utf8
|
||||
add_null = not str_utf8
|
||||
elif self.__left in self.__table._columns:
|
||||
params = self.__table._columns[self.__left]._symbol_set[1](self.__right)
|
||||
|
||||
if add_null:
|
||||
query = '(%s OR %s IS NULL)' % (query, self.__left)
|
||||
|
||||
joins = ' AND '.join(map(lambda j: '%s.id = %s' % (self.__table._table, j), self.__joins))
|
||||
if joins:
|
||||
query = '(%s AND (%s))' % (joins, query)
|
||||
if isinstance(params, basestring):
|
||||
params = [params]
|
||||
return (query, params)
|
||||
instr = ','.join([table._columns[left]._symbol_set[0]] * len_after)
|
||||
query = '(%s.%s %s (%s))' % (table._table, left, operator, instr)
|
||||
|
||||
if check_nulls:
|
||||
query = '(%s OR %s IS NULL)' % (query, left)
|
||||
else:
|
||||
children = [child.to_sql() for child in self.__children]
|
||||
params = flatten([child[1] for child in children])
|
||||
query = "( %s )" % (" %s " % {'&' : 'AND', '|' : 'OR' }[self.__operator]).join([child[0] for child in children if child[0]])
|
||||
return (query, params)
|
||||
params = []
|
||||
if right is False and operator == '=':
|
||||
query = '%s IS NULL' % left
|
||||
elif right is False and operator in ['<>', '!=']:
|
||||
query = '%s IS NOT NULL' % left
|
||||
else:
|
||||
if left == 'id':
|
||||
query = '%s.id %s %%s' % (table._table, operator)
|
||||
params = right
|
||||
else:
|
||||
like = operator in ('like', 'ilike', 'not like', 'not ilike')
|
||||
|
||||
def __get_tables(self):
|
||||
return self.__tables + [child.__get_tables() for child in self.__children]
|
||||
op = operator == '=like' and 'like' or operator
|
||||
if left in table._columns:
|
||||
format = like and '%s' or table._columns[left]._symbol_set[0]
|
||||
query = '(%s.%s %s %s)' % (table._table, left, op, format)
|
||||
else:
|
||||
query = "(%s.%s %s '%s')" % (table._table, left, op, right)
|
||||
|
||||
add_null = False
|
||||
if like:
|
||||
if isinstance(right, str):
|
||||
str_utf8 = right
|
||||
elif isinstance(right, unicode):
|
||||
str_utf8 = right.encode('utf-8')
|
||||
else:
|
||||
str_utf8 = str(right)
|
||||
params = '%%%s%%' % str_utf8
|
||||
add_null = not str_utf8
|
||||
elif left in table._columns:
|
||||
params = table._columns[left]._symbol_set[1](right)
|
||||
|
||||
if add_null:
|
||||
query = '(%s OR %s IS NULL)' % (query, left)
|
||||
|
||||
if isinstance(params, basestring):
|
||||
params = [params]
|
||||
return (query, params)
|
||||
|
||||
|
||||
def to_sql(self):
|
||||
stack = []
|
||||
params = []
|
||||
for i, e in reverse_enumerate(self.__exp):
|
||||
if self._is_leaf(e, internal=True):
|
||||
table = self.__tables.get(i, self.__main_table)
|
||||
q, p = self.__leaf_to_sql(e, table)
|
||||
params.insert(0, p)
|
||||
stack.append(q)
|
||||
else:
|
||||
if e == '!':
|
||||
stack.append('(NOT (%s))' % (stack.pop(),))
|
||||
else:
|
||||
ops = {'&': ' AND ', '|': ' OR '}
|
||||
q1 = stack.pop()
|
||||
q2 = stack.pop()
|
||||
stack.append('(%s %s %s)' % (q1, ops[e], q2,))
|
||||
|
||||
query = ' AND '.join(reversed(stack))
|
||||
joins = ' AND '.join(map(lambda j: j[0], self.__joins))
|
||||
if joins:
|
||||
query = '(%s) AND (%s)' % (joins, query)
|
||||
return (query, flatten(params))
|
||||
|
||||
def get_tables(self):
|
||||
return [ '"%s"' % t for t in set(flatten(self.__get_tables()))]
|
||||
|
||||
#def
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
#import doctest
|
||||
#doctest.testmod()
|
||||
return ['"%s"' % t._table for t in set(self.__tables.values()+[self.__main_table])]
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
+128
-106
@@ -1,5 +1,4 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# -*- encoding: iso-8859-1 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
|
||||
@@ -49,19 +48,22 @@ import warnings
|
||||
|
||||
import tools
|
||||
|
||||
|
||||
def _symbol_set(symb):
|
||||
if symb==None or symb==False:
|
||||
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
|
||||
_multi = False
|
||||
_symbol_c = '%s'
|
||||
_symbol_f = _symbol_set
|
||||
_symbol_set = (_symbol_c, _symbol_f)
|
||||
@@ -79,13 +81,12 @@ class _column(object):
|
||||
self.ondelete = ondelete
|
||||
self.translate = translate
|
||||
self._domain = domain or []
|
||||
self.relate =False
|
||||
self.relate = False
|
||||
self._context = context
|
||||
self.group_name = False
|
||||
self.write = False
|
||||
self.read = False
|
||||
self.view_load = 0
|
||||
self.select=select
|
||||
self.select = select
|
||||
for a in args:
|
||||
if args[a]:
|
||||
setattr(self, a, args[a])
|
||||
@@ -96,21 +97,25 @@ class _column(object):
|
||||
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) )
|
||||
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 !')
|
||||
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 !')
|
||||
raise Exception(_('Not implemented get_memory method !'))
|
||||
|
||||
def get(self, cr, obj, ids, name, context=None, values=None):
|
||||
raise Exception, _('undefined get method !')
|
||||
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)
|
||||
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 !')
|
||||
raise Exception(_('Not implemented search_memory method !'))
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Simple fields
|
||||
@@ -121,34 +126,38 @@ class boolean(_column):
|
||||
_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)
|
||||
|
||||
|
||||
class reference(_column):
|
||||
_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
|
||||
#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:
|
||||
if symb == None or symb == False:
|
||||
return None
|
||||
|
||||
# we need to convert the string to a unicode object to be able
|
||||
# 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')
|
||||
@@ -158,38 +167,46 @@ class char(_column):
|
||||
u_symb = unicode(symb)
|
||||
return u_symb.encode('utf8')[:self.size]
|
||||
|
||||
|
||||
class text(_column):
|
||||
_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
|
||||
|
||||
|
||||
class date(_column):
|
||||
_type = 'date'
|
||||
|
||||
|
||||
class datetime(_column):
|
||||
_type = 'datetime'
|
||||
|
||||
|
||||
class time(_column):
|
||||
_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)
|
||||
|
||||
|
||||
class selection(_column):
|
||||
_type = 'selection'
|
||||
|
||||
|
||||
def __init__(self, selection, string='unknown', **args):
|
||||
_column.__init__(self, string=string, **args)
|
||||
self.selection = selection
|
||||
@@ -211,7 +228,7 @@ 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)
|
||||
@@ -219,25 +236,26 @@ class one2one(_column):
|
||||
|
||||
def set(self, cr, obj_src, id, field, act, user=None, context=None):
|
||||
if not context:
|
||||
context={}
|
||||
context = {}
|
||||
obj = obj_src.pool.get(self._obj)
|
||||
self._table = obj_src.pool.get(self._obj)._table
|
||||
if act[0]==0:
|
||||
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))
|
||||
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)
|
||||
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)
|
||||
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
|
||||
@@ -254,9 +272,9 @@ class many2one(_column):
|
||||
|
||||
def get(self, cr, obj, ids, name, user=None, context=None, values=None):
|
||||
if not context:
|
||||
context={}
|
||||
context = {}
|
||||
if not values:
|
||||
values={}
|
||||
values = {}
|
||||
res = {}
|
||||
for r in values:
|
||||
res[r['id']] = r[name]
|
||||
@@ -268,10 +286,10 @@ class many2one(_column):
|
||||
try:
|
||||
names = dict(obj.name_get(cr, user, filter(None, res.values()), context))
|
||||
except except_orm:
|
||||
names={}
|
||||
names = {}
|
||||
|
||||
iids = filter(None, res.values())
|
||||
cr.execute('select id,'+obj._rec_name+' from '+obj._table+' where id in ('+','.join(map(str,iids))+')')
|
||||
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]
|
||||
|
||||
@@ -284,36 +302,37 @@ class many2one(_column):
|
||||
|
||||
def set(self, cr, obj_src, id, field, values, user=None, context=None):
|
||||
if not context:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
elif act[0] == 2:
|
||||
cr.execute('delete from '+self._table+' where id=%d', (act[1],))
|
||||
elif act[0]==3 or act[0]==5:
|
||||
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))
|
||||
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))
|
||||
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)
|
||||
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
|
||||
@@ -330,42 +349,41 @@ class one2many(_column):
|
||||
res = {}
|
||||
for id in ids:
|
||||
res[id] = []
|
||||
ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit)
|
||||
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'] )
|
||||
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={}
|
||||
context = {}
|
||||
if not values:
|
||||
return
|
||||
obj = obj.pool.get(self._obj)
|
||||
for act in values:
|
||||
if act[0]==0:
|
||||
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:
|
||||
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:
|
||||
elif act[0] == 3:
|
||||
obj.datas[act[1]][self._fields_id] = False
|
||||
elif act[0]==4:
|
||||
elif act[0] == 4:
|
||||
obj.datas[act[1]] = id
|
||||
elif act[0]==5:
|
||||
elif act[0] == 5:
|
||||
for o in obj.datas.values():
|
||||
if o[self._fields_id]==id:
|
||||
if o[self._fields_id] == id:
|
||||
o[self._fields_id] = False
|
||||
elif act[0]==6:
|
||||
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 = {}
|
||||
@@ -374,33 +392,33 @@ class one2many(_column):
|
||||
res = {}
|
||||
for id in ids:
|
||||
res[id] = []
|
||||
ids2 = obj.pool.get(self._obj).search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit)
|
||||
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'] )
|
||||
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={}
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
elif act[0] == 6:
|
||||
if not act[2]:
|
||||
ids2 = [0]
|
||||
else:
|
||||
@@ -408,9 +426,11 @@ class one2many(_column):
|
||||
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
|
||||
# (1, ID, { fields }) modification
|
||||
@@ -424,7 +444,7 @@ 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
|
||||
@@ -435,15 +455,15 @@ class many2many(_column):
|
||||
|
||||
def get(self, cr, obj, ids, name, user=None, offset=0, context=None, values=None):
|
||||
if not context:
|
||||
context={}
|
||||
context = {}
|
||||
if not values:
|
||||
values={}
|
||||
values = {}
|
||||
res = {}
|
||||
if not ids:
|
||||
return res
|
||||
for id in ids:
|
||||
res[id] = []
|
||||
ids_s = ','.join(map(str,ids))
|
||||
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)
|
||||
|
||||
@@ -463,39 +483,39 @@ class many2many(_column):
|
||||
|
||||
def set(self, cr, obj, id, name, values, user=None, context=None):
|
||||
if not context:
|
||||
context={}
|
||||
context = {}
|
||||
if not values:
|
||||
return
|
||||
obj = obj.pool.get(self._obj)
|
||||
for act in values:
|
||||
if act[0]==0:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
elif act[0] == 6:
|
||||
|
||||
d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name)
|
||||
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 )
|
||||
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]:
|
||||
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)
|
||||
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 = {}
|
||||
@@ -508,39 +528,42 @@ class many2many(_column):
|
||||
return
|
||||
for act in values:
|
||||
# TODO: use constants instead of these magic numbers
|
||||
if act[0]==0:
|
||||
if act[0] == 0:
|
||||
raise _('Not Implemented')
|
||||
elif act[0]==1:
|
||||
elif act[0] == 1:
|
||||
raise _('Not Implemented')
|
||||
elif act[0]==2:
|
||||
elif act[0] == 2:
|
||||
raise _('Not Implemented')
|
||||
elif act[0]==3:
|
||||
elif act[0] == 3:
|
||||
raise _('Not Implemented')
|
||||
elif act[0]==4:
|
||||
elif act[0] == 4:
|
||||
raise _('Not Implemented')
|
||||
elif act[0]==5:
|
||||
elif act[0] == 5:
|
||||
raise _('Not Implemented')
|
||||
elif act[0]==6:
|
||||
elif act[0] == 6:
|
||||
obj.datas[id][name] = act[2]
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Function fields
|
||||
# ---------------------------------------------------------
|
||||
|
||||
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):
|
||||
|
||||
#
|
||||
# multi: compute several fields in one call
|
||||
#
|
||||
def __init__(self, fnct, arg=None, fnct_inv=None, fnct_inv_arg=None, type='float', fnct_search=None, obj=None, method=False, store=False, multi=False, **args):
|
||||
_column.__init__(self, **args)
|
||||
self._obj = obj
|
||||
self._method = method
|
||||
self._fnct = fnct
|
||||
self._fnct_inv = fnct_inv
|
||||
self._arg = arg
|
||||
self._multi = multi
|
||||
if 'relation' in args:
|
||||
self._obj = args['relation']
|
||||
self._fnct_inv_arg = fnct_inv_arg
|
||||
@@ -553,7 +576,7 @@ class function(_column):
|
||||
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
|
||||
@@ -562,23 +585,22 @@ class function(_column):
|
||||
|
||||
def get(self, cr, obj, ids, name, user=None, context=None, values=None):
|
||||
if not context:
|
||||
context={}
|
||||
context = {}
|
||||
if not values:
|
||||
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)
|
||||
return self._fnct(cr, obj._table, ids, name, self._arg, context)
|
||||
|
||||
def set(self, cr, obj, id, name, value, user=None, context=None):
|
||||
if not context:
|
||||
context={}
|
||||
context = {}
|
||||
if self._fnct_inv:
|
||||
self._fnct_inv(obj, cr, user, id, name, value, self._fnct_inv_arg, context)
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Serialized fields
|
||||
# ---------------------------------------------------------
|
||||
@@ -596,7 +618,7 @@ class property(function):
|
||||
|
||||
def _fnct_write(self, obj, cr, uid, id, prop, id_val, val, context=None):
|
||||
if not context:
|
||||
context={}
|
||||
context = {}
|
||||
(obj_dest,) = val
|
||||
definition_id = self._field_get(cr, uid, obj._name, prop)
|
||||
|
||||
@@ -629,7 +651,7 @@ class property(function):
|
||||
|
||||
def _fnct_read(self, obj, cr, uid, ids, prop, val, context=None):
|
||||
if not context:
|
||||
context={}
|
||||
context = {}
|
||||
property = obj.pool.get('ir.property')
|
||||
definition_id = self._field_get(cr, uid, obj._name, prop)
|
||||
|
||||
@@ -646,7 +668,7 @@ class property(function):
|
||||
|
||||
res = {}
|
||||
for id in ids:
|
||||
res[id]= default_val
|
||||
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
|
||||
|
||||
+411
-291
File diff suppressed because it is too large
Load Diff
+14
-9
@@ -60,6 +60,8 @@ class osv_pool(netsvc.Service):
|
||||
self.created = []
|
||||
self._sql_error = {}
|
||||
self._store_function = {}
|
||||
self._init = True
|
||||
self._init_parent = {}
|
||||
netsvc.Service.__init__(self, 'object_proxy', audience='')
|
||||
self.joinGroup('web-services')
|
||||
self.exportMethod(self.exportedMethods)
|
||||
@@ -68,6 +70,17 @@ class osv_pool(netsvc.Service):
|
||||
self.exportMethod(self.execute)
|
||||
self.exportMethod(self.execute_cr)
|
||||
|
||||
def init_set(self, cr, mode):
|
||||
if mode<>self._init:
|
||||
if mode:
|
||||
self._init_parent={}
|
||||
if not mode:
|
||||
for o in self._init_parent:
|
||||
self.get(o)._parent_store_compute(cr)
|
||||
self._init = mode
|
||||
return True
|
||||
return False
|
||||
|
||||
def execute_cr(self, cr, uid, obj, method, *args, **kw):
|
||||
try:
|
||||
object = pooler.get_pool(cr.dbname).get(obj)
|
||||
@@ -152,19 +165,11 @@ class osv_pool(netsvc.Service):
|
||||
|
||||
#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
|
||||
@@ -223,7 +228,7 @@ class osv(orm.orm):
|
||||
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
|
||||
assert pool.get(parent_name), "parent class %s does not exist in module %s !" % (parent_name, module)
|
||||
nattr = {}
|
||||
for s in ('_columns', '_defaults', '_inherits', '_constraints', '_sql_constraints'):
|
||||
new = copy.copy(getattr(pool.get(parent_name), s))
|
||||
|
||||
+12
-1
@@ -36,6 +36,7 @@ import netsvc
|
||||
db_dic = {}
|
||||
pool_dic = {}
|
||||
|
||||
|
||||
def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False):
|
||||
if not status:
|
||||
status={}
|
||||
@@ -46,13 +47,17 @@ def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False)
|
||||
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)
|
||||
cr = db.cursor()
|
||||
pool.init_set(cr, False)
|
||||
cr.commit()
|
||||
cr.close()
|
||||
|
||||
if not update_module:
|
||||
import report
|
||||
@@ -60,11 +65,13 @@ def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False)
|
||||
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)
|
||||
|
||||
|
||||
def close_db(db_name):
|
||||
if db_name in db_dic:
|
||||
db_dic[db_name].truedb.close()
|
||||
@@ -72,6 +79,7 @@ def close_db(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]
|
||||
@@ -80,10 +88,12 @@ def get_db_only(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]
|
||||
|
||||
|
||||
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]
|
||||
@@ -94,6 +104,7 @@ def get_pool(db_name, force_demo=False, status=None, update_module=False):
|
||||
return pool
|
||||
# return get_db_and_pool(db_name)[1]
|
||||
|
||||
|
||||
def init():
|
||||
global db
|
||||
# db = get_db_only(tools.config['db_name'])
|
||||
|
||||
@@ -56,6 +56,7 @@ class db(netsvc.Service):
|
||||
self.exportMethod(self.list)
|
||||
self.exportMethod(self.list_lang)
|
||||
self.exportMethod(self.change_admin_password)
|
||||
self.exportMethod(self.server_version)
|
||||
self.actions = {}
|
||||
self.id = 0
|
||||
self.id_protect = threading.Semaphore()
|
||||
@@ -281,6 +282,12 @@ class db(netsvc.Service):
|
||||
lang = os.path.basename(fname).split(".")[0]
|
||||
return (lang, lang_dict.get(lang, lang))
|
||||
return [lang_tuple(fname) for fname in file_list]
|
||||
|
||||
def server_version(self):
|
||||
""" Return the version of the server
|
||||
Used by the client to verify the compatibility with its own version
|
||||
"""
|
||||
return tinyerp_version
|
||||
db()
|
||||
|
||||
class common(netsvc.Service):
|
||||
|
||||
+6
-1
@@ -28,6 +28,7 @@
|
||||
###############################################################################
|
||||
import socket
|
||||
import cPickle
|
||||
import cStringIO
|
||||
import marshal
|
||||
|
||||
class Myexception(Exception):
|
||||
@@ -82,7 +83,11 @@ class mysocket:
|
||||
if chunk == '':
|
||||
raise RuntimeError, "socket connection broken"
|
||||
msg = msg + chunk
|
||||
res = cPickle.loads(msg)
|
||||
msgio = cStringIO.StringIO(msg)
|
||||
unpickler = cPickle.Unpickler(msgio)
|
||||
unpickler.find_global = None
|
||||
res = unpickler.load()
|
||||
|
||||
if isinstance(res[0],Exception):
|
||||
if exception:
|
||||
raise Myexception(str(res[0]), str(res[1]))
|
||||
|
||||
+20
-15
@@ -50,7 +50,7 @@ __builtin__.__dict__['tinyerp_version_string'] = "Tiny ERP Server " + __version_
|
||||
#----------------------------------------------------------
|
||||
# python imports
|
||||
#----------------------------------------------------------
|
||||
import sys,os,signal
|
||||
import sys, os, signal
|
||||
#----------------------------------------------------------
|
||||
# ubuntu 8.04 has obsoleted `pyxml` package and installs here.
|
||||
# the path needs to be updated before any `import xml`
|
||||
@@ -75,9 +75,9 @@ logger = netsvc.Logger()
|
||||
import tools
|
||||
import time
|
||||
|
||||
if sys.platform=='win32':
|
||||
if sys.platform == 'win32':
|
||||
import mx.DateTime
|
||||
mx.DateTime.strptime = lambda x,y: mx.DateTime.mktime(time.strptime(x, y))
|
||||
mx.DateTime.strptime = lambda x, y: mx.DateTime.mktime(time.strptime(x, y))
|
||||
|
||||
#os.chdir(tools.file_path_root)
|
||||
|
||||
@@ -104,9 +104,9 @@ try:
|
||||
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()
|
||||
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:
|
||||
@@ -165,7 +165,7 @@ if tools.config['upgrade']:
|
||||
tools.config['update'][m] = 1
|
||||
|
||||
#----------------------------------------------------------
|
||||
# import basic modules
|
||||
# import basic modules
|
||||
#----------------------------------------------------------
|
||||
import osv, workflow, report, service
|
||||
|
||||
@@ -184,13 +184,17 @@ if tools.config['init'] or tools.config['update']:
|
||||
if tools.config["translate_out"]:
|
||||
import csv
|
||||
|
||||
logger.notifyChannel("init", netsvc.LOG_INFO, 'writing translation file for language %s to %s' % (tools.config["language"], tools.config["translate_out"]))
|
||||
if tools.config["language"]:
|
||||
msg = "language %s" % (tools.config["language"],)
|
||||
else:
|
||||
msg = "new language"
|
||||
logger.notifyChannel("init", netsvc.LOG_INFO, 'writing translation file for %s to %s' % (msg, 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)
|
||||
|
||||
@@ -217,12 +221,12 @@ if tools.config['xmlrpc']:
|
||||
sys.exit(1)
|
||||
interface = tools.config["interface"]
|
||||
secure = tools.config["secure"]
|
||||
|
||||
httpd = netsvc.HttpDaemon(interface,port, secure)
|
||||
|
||||
|
||||
httpd = netsvc.HttpDaemon(interface, port, secure)
|
||||
|
||||
if tools.config["xmlrpc"]:
|
||||
xml_gw = netsvc.xmlrpc.RpcGateway('web-services')
|
||||
httpd.attach("/xmlrpc", xml_gw )
|
||||
httpd.attach("/xmlrpc", xml_gw)
|
||||
logger.notifyChannel("web-services", netsvc.LOG_INFO,
|
||||
"starting XML-RPC" + \
|
||||
(tools.config['secure'] and ' Secure' or '') + \
|
||||
@@ -242,10 +246,11 @@ if tools.config['netrpc']:
|
||||
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']:
|
||||
@@ -259,8 +264,8 @@ def handler(signum, frame):
|
||||
|
||||
from tools import config
|
||||
if config['pidfile']:
|
||||
fd=open(config['pidfile'], 'w')
|
||||
pidtext="%d" % (os.getpid())
|
||||
fd = open(config['pidfile'], 'w')
|
||||
pidtext = "%d" % (os.getpid())
|
||||
fd.write(pidtext)
|
||||
fd.close()
|
||||
|
||||
|
||||
+14
-8
@@ -58,12 +58,13 @@ class configmanager(object):
|
||||
'addons_path': None,
|
||||
'root_path': None,
|
||||
'debug_mode': False,
|
||||
'commit_mode': False,
|
||||
'import_partial': "",
|
||||
'pidfile': None,
|
||||
'logfile': None,
|
||||
'secure': False,
|
||||
'smtp_server': 'localhost',
|
||||
'smtp_user': False,
|
||||
'smtp_port':25,
|
||||
'smtp_password': False,
|
||||
'stop_after_init': False, # this will stop the server after initialization
|
||||
'price_accuracy': 2,
|
||||
@@ -96,8 +97,10 @@ class configmanager(object):
|
||||
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('--email-from', dest='email_from', default='', help='specify the SMTP email address for sending email')
|
||||
parser.add_option('--smtp', dest='smtp_server', default='', help='specify the SMTP server for sending email')
|
||||
parser.add_option('--smtp-port', dest='smtp_port', default='25', help='specify the SMTP port')
|
||||
parser.add_option('--smtp-ssl', dest='smtp_ssl', default='', help='specify the SMTP server support SSL or not')
|
||||
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')
|
||||
@@ -114,23 +117,26 @@ class configmanager(object):
|
||||
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)
|
||||
group.add_option("-P", "--import-partial", dest="import_partial", help="Use this for big data importation, if it crashes you will be able to continue at the current state. Provide a filename to store intermediate importation states.", 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.")
|
||||
"See i18n section of the user manual. Option '-d' is mandatory."
|
||||
"Option '-l' is mandatory in case of importation"
|
||||
)
|
||||
|
||||
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('-l', "--language", dest="language", help="specify the language of the translation file. Use it with --i18n-export or --i18n-import")
|
||||
group.add_option("--i18n-export", dest="translate_out", help="export all sentences to be translated to a CSV file, a PO file or a TGZ archive and exit")
|
||||
group.add_option("--i18n-import", dest="translate_in", help="import a CSV or a PO file with translations and exit. The '-l' option is required.")
|
||||
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"
|
||||
assert not (opt.translate_in and (not opt.language or not opt.db_name)), "the i18n-import option cannot be used without the language (-l) and the database (-d) options"
|
||||
assert not (opt.translate_out and (not opt.db_name)), "the i18n-export option cannot be used without the database (-d) option"
|
||||
|
||||
# place/search the config file on Win32 near the server installation
|
||||
# (../etc from the server)
|
||||
@@ -153,7 +159,7 @@ class configmanager(object):
|
||||
self.options['pidfile'] = False
|
||||
|
||||
for arg in ('interface', 'port', 'db_name', 'db_user', 'db_password', 'db_host',
|
||||
'db_port', 'logfile', 'pidfile', 'secure', 'smtp_ssl', 'email_from', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy', 'netinterface', 'netport', 'db_maxconn', 'commit_mode', 'addons_path'):
|
||||
'db_port', 'logfile', 'pidfile', 'secure', 'smtp_ssl', 'smtp_port', 'email_from', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy', 'netinterface', 'netport', 'db_maxconn', 'import_partial', 'addons_path'):
|
||||
if getattr(opt, arg):
|
||||
self.options[arg] = getattr(opt, arg)
|
||||
|
||||
|
||||
+22
-2
@@ -42,6 +42,7 @@ from config import config
|
||||
import logging
|
||||
|
||||
from lxml import etree
|
||||
import pickle
|
||||
|
||||
|
||||
class ConvertError(Exception):
|
||||
@@ -657,7 +658,7 @@ form: module.record_id""" % (xml_id,)
|
||||
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):
|
||||
if config.get('import_partial', False):
|
||||
cr.commit()
|
||||
return rec_model, id
|
||||
|
||||
@@ -728,6 +729,20 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
|
||||
reader = csv.reader(input, quotechar='"', delimiter=',')
|
||||
fields = reader.next()
|
||||
|
||||
fname_partial = ""
|
||||
if config.get('import_partial'):
|
||||
fname_partial = module + '/'+ fname
|
||||
if not os.path.isfile(config.get('import_partial')):
|
||||
pickle.dump({}, file(config.get('import_partial'),'w+'))
|
||||
else:
|
||||
data = pickle.load(file(config.get('import_partial')))
|
||||
if fname_partial in data:
|
||||
if not data[fname_partial]:
|
||||
return
|
||||
else:
|
||||
for i in range(data[fname_partial]):
|
||||
reader.next()
|
||||
|
||||
if not (mode == 'init' or 'id' in fields):
|
||||
return
|
||||
|
||||
@@ -737,7 +752,12 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
|
||||
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)
|
||||
pool.get(model).import_data(cr, uid, fields, datas,mode, module,noupdate,filename=fname_partial)
|
||||
|
||||
if config.get('import_partial'):
|
||||
data = pickle.load(file(config.get('import_partial')))
|
||||
data[fname_partial] = 0
|
||||
pickle.dump(data, file(config.get('import_partial'),'wb'))
|
||||
|
||||
#
|
||||
# xml import/export
|
||||
|
||||
+28
-3
@@ -48,6 +48,8 @@ if sys.version_info[:2] < (2, 4):
|
||||
else:
|
||||
from threading import local
|
||||
|
||||
from itertools import izip
|
||||
|
||||
# initialize a database with base/base.sql
|
||||
def init_db(cr):
|
||||
import addons
|
||||
@@ -288,7 +290,22 @@ def flatten(list):
|
||||
r.append(e)
|
||||
return r
|
||||
|
||||
|
||||
def reverse_enumerate(l):
|
||||
"""Like enumerate but in the other sens
|
||||
>>> a = ['a', 'b', 'c']
|
||||
>>> it = reverse_enumerate(a)
|
||||
>>> it.next()
|
||||
(2, 'c')
|
||||
>>> it.next()
|
||||
(1, 'b')
|
||||
>>> it.next()
|
||||
(0, 'a')
|
||||
>>> it.next()
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
StopIteration
|
||||
"""
|
||||
return izip(xrange(len(l)-1, -1, -1), reversed(l))
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Emails
|
||||
@@ -321,7 +338,15 @@ def email_send(email_from, email_to, subject, body, email_cc=None, email_bcc=Non
|
||||
msg['Message-Id'] = '<'+str(time.time())+'-tinycrm-'+str(tinycrm)+'@'+socket.gethostname()+'>'
|
||||
try:
|
||||
s = smtplib.SMTP()
|
||||
s.connect(config['smtp_server'])
|
||||
|
||||
if debug:
|
||||
s.debuglevel = 5
|
||||
if ssl:
|
||||
s.ehlo()
|
||||
s.starttls()
|
||||
s.ehlo()
|
||||
|
||||
s.connect(config['smtp_server'], config['smtp_port'])
|
||||
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())
|
||||
@@ -386,7 +411,7 @@ def email_send_attach(email_from, email_to, subject, body, email_cc=None, email_
|
||||
s.starttls()
|
||||
s.ehlo()
|
||||
|
||||
s.connect(config['smtp_server'])
|
||||
s.connect(config['smtp_server'], config['smtp_port'])
|
||||
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())
|
||||
|
||||
+102
-47
@@ -37,6 +37,8 @@ import netsvc
|
||||
from tools.misc import UpdateableStr
|
||||
import inspect
|
||||
import mx.DateTime as mxdt
|
||||
import tempfile
|
||||
import tarfile
|
||||
|
||||
class UNIX_LINE_TERMINATOR(csv.excel):
|
||||
lineterminator = '\n'
|
||||
@@ -78,6 +80,7 @@ class TinyPoFile(object):
|
||||
self.buffer.seek(0)
|
||||
self.lines = self.buffer.readlines()
|
||||
self.first = True
|
||||
self.tnrs= []
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
@@ -86,41 +89,51 @@ class TinyPoFile(object):
|
||||
.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:
|
||||
if self.tnrs:
|
||||
type, name, res_id, source, trad = self.tnrs.pop(0)
|
||||
else:
|
||||
tmp_tnrs = []
|
||||
line = None
|
||||
while not line:
|
||||
if 0 == len(self.lines):
|
||||
raise StopIteration()
|
||||
line = self.lines.pop(0).strip()
|
||||
return next()
|
||||
|
||||
while not line.startswith('msgstr'):
|
||||
if not line:
|
||||
raise Exception('malformed file')
|
||||
source += unquote(line)
|
||||
while line.startswith('#'):
|
||||
if line.startswith('#:'):
|
||||
tmp_tnrs.append( 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
|
||||
self.tnrs = []
|
||||
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)
|
||||
trad = line[8:-1]
|
||||
line = self.lines.pop(0).strip()
|
||||
|
||||
while line:
|
||||
trad += unquote(line)
|
||||
line = self.lines.pop(0).strip()
|
||||
|
||||
if tmp_tnrs:
|
||||
type, name, res_id = tmp_tnrs.pop(0)
|
||||
for t, n, r in tmp_tnrs:
|
||||
self.tnrs.append((t, n, r, source, trad))
|
||||
|
||||
self.first = False
|
||||
return type, name, res_id, source, trad
|
||||
|
||||
@@ -152,37 +165,79 @@ class TinyPoFile(object):
|
||||
}
|
||||
)
|
||||
|
||||
def write(self, module, type, name, res_id, source, trad):
|
||||
def write(self, modules, tnrs, source, trad):
|
||||
def quote(str):
|
||||
return '"%s"' % str.replace('"','\\"') \
|
||||
.replace('\n', '\\n"\n"')
|
||||
|
||||
self.buffer.write("#. module: %s\n" \
|
||||
plurial = len(modules) > 1 and 's' or ''
|
||||
self.buffer.write("#. module%s: %s\n" \
|
||||
"#, python-format\n" \
|
||||
"#: %s:%s:%s\n" \
|
||||
"msgid %s\n" \
|
||||
% (plurial, ', '.join(modules)))
|
||||
|
||||
for type, name, res_id in tnrs:
|
||||
self.buffer.write("#: %s:%s:%s\n" % (type, name, str(res_id)))
|
||||
|
||||
self.buffer.write("msgid %s\n" \
|
||||
"msgstr %s\n\n" \
|
||||
% (module, type, name, str(res_id), quote(source), quote(trad))
|
||||
)
|
||||
% (quote(source), quote(trad))
|
||||
)
|
||||
|
||||
|
||||
|
||||
# Methods to export the translation file
|
||||
|
||||
def trans_export(lang, modules, buffer, format, dbname=None):
|
||||
|
||||
def _process(format, modules, rows, buffer, lang, newlang):
|
||||
if format == 'csv':
|
||||
writer=csv.writer(buffer, 'UNIX')
|
||||
for row in rows:
|
||||
writer.writerow(row)
|
||||
elif format == 'po':
|
||||
rows.pop(0)
|
||||
writer = tools.TinyPoFile(buffer)
|
||||
writer.write_infos(modules)
|
||||
|
||||
# we now group the translations by source. That means one translation per source.
|
||||
grouped_rows = {}
|
||||
for module, type, name, res_id, src, trad in rows:
|
||||
row = grouped_rows.setdefault(src, {})
|
||||
row.setdefault('modules', set()).add(module)
|
||||
row.setdefault('translation', trad)
|
||||
row.setdefault('tnrs', []).append((type, name, res_id))
|
||||
|
||||
for src, row in grouped_rows.items():
|
||||
writer.write(row['modules'], row['tnrs'], src, row['translation'])
|
||||
|
||||
elif format == 'tgz':
|
||||
rows.pop(0)
|
||||
rows_by_module = {}
|
||||
for row in rows:
|
||||
module = row[0]
|
||||
rows_by_module.setdefault(module, []).append(row)
|
||||
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
for mod, modrows in rows_by_module.items():
|
||||
tmpmoddir = join(tmpdir, mod, 'i18n')
|
||||
os.makedirs(tmpmoddir)
|
||||
pofilename = (newlang and mod or lang) + ".po" + (newlang and 't' or '')
|
||||
buf = open(join(tmpmoddir, pofilename), 'w')
|
||||
_process('po', [mod], modrows, buf, lang, newlang)
|
||||
|
||||
tar = tarfile.open(fileobj=buffer, mode='w|gz')
|
||||
tar.add(tmpdir, '/')
|
||||
tar.close()
|
||||
|
||||
else:
|
||||
raise Exception(_('Bad file format'))
|
||||
|
||||
newlang = not bool(lang)
|
||||
if newlang:
|
||||
lang = 'en_US'
|
||||
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'))
|
||||
modules = set([t[0] for t in trans[1:]])
|
||||
_process(format, modules, trans, buffer, lang, newlang)
|
||||
del trans
|
||||
|
||||
|
||||
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
* What is this package?
|
||||
|
||||
PyChart is a Python library for creating professional quality PS, PDF,
|
||||
PNG, or SVG charts. It supports line plots, bar plots, range-fill
|
||||
plots, and pie charts. Because it is based on Python, you can make
|
||||
the full use of Python's scripting power.
|
||||
|
||||
The PyChart home page is at
|
||||
http://www.hpl.hp.com/personal/Yasushi_Saito/pychart
|
||||
|
||||
* What you need
|
||||
|
||||
You need Python (http://www.python.org) version 2.2 or later (on
|
||||
Debian GNU/Linux, you need, python-xml package as well). You also need
|
||||
Ghostscript (http://www.cs.wisc.edu/~ghost/) to produce PNG
|
||||
files.
|
||||
|
||||
Pychart used to require Ghostscript during PS and PDF
|
||||
generation to obtain font metric information, but that's no
|
||||
longer the case.
|
||||
|
||||
* Platforms supported
|
||||
|
||||
In theory, PyChart works on any platform with Python. In practice,
|
||||
I've run PyChart on Linux and Win2000.
|
||||
|
||||
* Installation
|
||||
|
||||
Become a root and do:
|
||||
|
||||
# python setup.py install
|
||||
|
||||
Or, you can set PYTHONPATH environment variable before you start
|
||||
Python. For example (in bash):
|
||||
|
||||
% PYTHONPATH=~/PyChart-1.33/pychart python mumbo.py
|
||||
|
||||
* Documentation
|
||||
|
||||
A detailed documentation is found in doc/pychart.
|
||||
|
||||
* Examples
|
||||
|
||||
All the *.py files in the demos/ directory can be run directly. For
|
||||
example,
|
||||
|
||||
setenv PYTHONPATH ..
|
||||
python linetest.py >foo.eps
|
||||
gs foo.eps
|
||||
|
||||
or
|
||||
|
||||
setenv PYTHONPATH ..
|
||||
setenv PYCHART_OPTIONS="format=pdf"
|
||||
python linetest.py >foo.pdf
|
||||
acroread foo.pdf
|
||||
|
||||
* About the author
|
||||
|
||||
Yasushi Saito (ysaito@hpl.hp.com), a full-time researcher and
|
||||
part-time hacker. This program is created mainly to serve my personal
|
||||
needs to write pretty charts for research papers. As such, it is
|
||||
updated only when I'm writing a paper, which happens about once every
|
||||
half year or so.
|
||||
|
||||
Anyway, if you have comments, requests, or (even better)
|
||||
fixes/enhancements, feel free to email me.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user