77
75
keys(obj) # List field names of the model
78
76
fields(obj, names=None) # Return details for the fields
79
77
field(obj, name) # Return details for the field
79
do(obj, method, *params) # Generic 'service.execute'
80
wizard(name) # Return the 'id' of a new wizard
81
wizard(name_or_id, datas=None, action='init')
82
# Generic 'wizard.execute'
84
client # Client object, connected
85
client.modules(name) # List modules matching pattern
86
client.upgrade(module1, module2, ...)
90
STABLE_STATES = ('uninstallable', 'uninstalled', 'installed')
91
DOMAIN_OPERATORS = frozenset('!|&')
82
92
# Supported operators are
83
93
# =, !=, >, >=, <, <=, like, ilike, in,
84
94
# not like, not ilike, not in, child_of
93
103
ini_path = os.path.splitext(__file__)[0] + '.ini'
96
def read_config(section):
105
# Published object methods
107
'db': ['create', 'drop', 'dump', 'restore', 'rename', 'list', 'list_lang',
108
'change_admin_password', 'server_version', 'migrate_databases'],
109
'common': ['about', 'login', 'timezone_get', 'get_server_environment',
110
'login_message', 'check_connectivity'],
111
'object': ['execute', 'exec_workflow'],
112
'wizard': ['execute', 'create'],
113
'report': ['report', 'report_get'],
116
'db': ['create_database', 'db_exist'],
117
'common': ['get_stats', 'list_http_services', 'version',
118
'authenticate', 'get_os_time', 'get_sqlcount'],
119
'object': ['execute_kw'],
121
'report': ['render_report'],
124
# - (not in 6.1) 'common': ['logout', 'ir_get', 'ir_set', 'ir_del']
125
# - (not in 6.1) 'object': ['obj_list']
126
# - 'db': ['get_progress']
127
# - 'common': ['get_available_updates', 'get_migration_scripts', 'set_loglevel']
130
def read_config(section=None):
97
131
p = ConfigParser.SafeConfigParser()
98
132
with open(ini_path) as f:
100
136
server = 'http://%s:%s' % (p.get(section, 'host'), p.get(section, 'port'))
101
137
db = p.get(section, 'database')
102
138
user = p.get(section, 'username')
149
185
domain = params[0]
150
186
for idx, term in enumerate(domain):
151
187
if isinstance(term, basestring):
188
if term in DOMAIN_OPERATORS:
152
190
m = _term_re.match(term.strip())
192
raise ValueError("Cannot parse term %r" % term)
155
193
(field, operator, value) = m.groups()
157
195
value = literal_eval(value)
158
196
except Exception:
197
# Interpret the value as a string
160
199
domain[idx] = (field, operator, value)
161
200
if kwargs and len(params) == 1:
175
214
def __init__(self, server, db, user, password):
176
sock = xmlrpclib.ServerProxy(server + '/xmlrpc/common')
177
uid = sock.login(db, user, password)
215
def get_proxy(name, prefix=server + '/xmlrpc/'):
216
return xmlrpclib.ServerProxy(prefix + name, allow_none=True)
217
self.common = common = get_proxy('common')
218
uid = common.login(db, user, password)
179
220
raise RuntimeError('Invalid username or password')
180
sock = xmlrpclib.ServerProxy(server + '/xmlrpc/object', allow_none=True)
221
self.db = sockdb = get_proxy('db')
222
sock = get_proxy('object')
223
wizard = get_proxy('wizard')
224
report = get_proxy('report')
181
225
self._server = server
226
self.server_version = ver = sockdb.server_version()
227
self.major_version = major_version = '.'.join(ver.split('.', 2)[:2])
228
# Authenticated endpoints
182
229
self._execute = functools.partial(sock.execute, db, uid, password)
230
self.exec_workflow = functools.partial(sock.exec_workflow, db, uid, password)
231
self.wizard_execute = functools.partial(wizard.execute, db, uid, password)
232
self.wizard_create = functools.partial(wizard.create, db, uid, password)
233
self.report = functools.partial(report.report, db, uid, password)
234
self.report_get = functools.partial(report.report_get, db, uid, password)
235
m_db = _methods['db'][:]
236
m_common = _methods['common'][:]
237
# Set the special value returned by dir(...)
238
sockdb.__dir__ = lambda m=m_db: m
239
common.__dir__ = lambda m=m_common: m
240
if major_version[:2] != '5.':
241
# Only for OpenERP >= 6
242
self.execute_kw = functools.partial(sock.execute, db, uid, password)
243
self.render_report = functools.partial(report.render_report, db, uid, password)
244
m_db += _methods_6_1['db']
245
m_common += _methods_6_1['common']
185
248
def from_config(cls, environment):
216
279
print 'Ignoring: %s = %r' % item
217
280
return self._execute(obj, method, *params)
283
def wizard(self, name, datas=None, action='init', context=None):
284
if isinstance(name, (int, long)):
287
wiz_id = self.wizard_create(name)
289
if action == 'init' and name != wiz_id:
292
return self.wizard_execute(wiz_id, datas, action, context)
294
def _upgrade(self, modules, button):
295
# Click upgrade/install/uninstall button
296
ids = self.search('ir.module.module', [('name', 'in', modules)])
299
self.execute('ir.module.module', button, ids)
300
mods = self.read('ir.module.module',
301
[('state', 'not in', STABLE_STATES)], 'name state')
304
print '%s module(s) selected' % len(ids)
305
print '%s module(s) to update:' % len(mods)
307
print ' %(state)s\t%(name)s' % mod
309
if self.major_version[:2] == '5.':
310
# Wizard "Apply Scheduled Upgrades"
311
rv = self.wizard('module.upgrade', action='start')
312
if 'config' not in [state[0] for state in rv.get('state', ())]:
313
# Something bad happened
316
self.execute('base.module.upgrade', 'upgrade_module', [])
318
def upgrade(self, *modules):
319
# Button "Schedule Upgrade"
320
return self._upgrade(modules, button='button_upgrade')
322
def install(self, *modules):
323
# Button "Schedule for Installation"
324
return self._upgrade(modules, button='button_install')
326
def uninstall(self, *modules):
327
# Button "Uninstall (beta)"
328
return self._upgrade(modules, button='button_uninstall')
219
330
def search(self, obj, *params, **kwargs):
220
331
return self.execute(obj, 'search', *params, **kwargs)
231
342
return sorted([m['model'] for m in models])
344
def modules(self, name, installed=None):
345
domain = [('name', 'like', name)]
346
if installed is not None:
347
op = installed and '=' or '!='
348
domain.append(('state', op, 'installed'))
349
mods = self.read('ir.module.module', domain, 'name state')
353
if mod['state'] in res:
354
res[mod['state']].append(mod['name'])
356
res[mod['state']] = [mod['name']]
233
359
def keys(self, obj):
234
return sorted(self.execute(obj, 'fields_get_keys'))
360
obj_keys = self.execute(obj, 'fields_get_keys')
361
return obj_keys and sorted(obj_keys)
236
363
def fields(self, obj, names=None):
237
364
return self.execute(obj, 'fields_get', names)
253
380
parser = optparse.OptionParser(
254
381
usage='%prog [options] [id [id ...]]',
255
382
description='Inspect data on OpenERP objects')
256
parser.add_option('-m', '--model',
257
help='the type of object to find')
383
parser.add_option('-l', '--list', action='store_true', dest='list_env',
384
help='list sections of %r' % ini_path)
258
385
parser.add_option('--env',
259
386
help='read connection settings from the given '
260
387
'section of %r' % ini_path)
266
393
parser.add_option('-p', '--password', default=DEFAULT_PASSWORD,
267
394
help='password (yes this will be in your shell history and '
268
395
'ps from other users)')
396
parser.add_option('-m', '--model',
397
help='the type of object to find')
269
398
parser.add_option('-s', '--search', action='append', dest='search',
270
399
help='search condition (multiple allowed); alternatively, pass '
271
400
'multiple IDs as positional parameters after the options')