540 lines
14 KiB
Python
Executable File
540 lines
14 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
## Usage:
|
|
## ./remote.py <MODEL> <METHOD> [<param> [<param> [<param>]]] [+<opt>] [+<opt=value>]
|
|
## Where <opt> are:
|
|
## json Renders json data.
|
|
## tsv Renders tsv data.
|
|
## csv Renders csv data.
|
|
## xml Soon WIP Renders xml data.
|
|
## recurse Execute the <METHOD> recursively.
|
|
## recurse=L Execute the <METHOD> recursively, limiting
|
|
## to model names in L being a list with comma and no spaces.
|
|
|
|
|
|
from __future__ import print_function
|
|
import os
|
|
import json
|
|
import odoorpc # pip install odoorpc
|
|
#from typing import Iterable # > py38
|
|
# from collections import Iterable # < py38
|
|
|
|
from config import *
|
|
from utils import *
|
|
from renderers import render, tsv
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ODOO
|
|
|
|
# Prepare the connection to the server
|
|
# odoo = odoorpc.ODOO('localhost', port=8069)
|
|
|
|
# Check available databases
|
|
# eprint( 'Databases: ', odoo.db.list() )
|
|
#jlog( odoo.db.list() )
|
|
|
|
odoo = None
|
|
|
|
def autolog():
|
|
# Then, use the odoorpc.ODOO.load() class method:
|
|
global odoo
|
|
odoo = odoorpc.ODOO.load('session')
|
|
# Or, if you have saved your configuration in another file:
|
|
# odoo = odoorpc.ODOO.load('tutorial', '~/my_own_odoorpcrc')
|
|
|
|
# You can check available sessions with odoorpc.ODOO.list(), and remove them with odoorpc.ODOO.remove():
|
|
# odoorpc.ODOO.list()
|
|
# ['session']
|
|
# odoorpc.ODOO.remove('tutorial')
|
|
# 'tutorial' not in odoorpc.ODOO.list()
|
|
# True
|
|
|
|
# Current user
|
|
user = odoo.env.user
|
|
eprint( 'User: ', user.name ) # name of the user connected
|
|
eprint( 'Company: ', user.company_id.name ) # the name of its company
|
|
eprint( '_________________________' )
|
|
|
|
# return odoo
|
|
|
|
|
|
|
|
# Simple 'raw' query
|
|
#user_data = odoo.execute('res.users', 'read', [user.id])
|
|
#print(user_data)
|
|
|
|
# Use all methods of a model
|
|
#if 'sale.order' in odoo.env:
|
|
# Order = odoo.env['sale.order']
|
|
# order_ids = Order.search([])
|
|
# for order in Order.browse(order_ids):
|
|
# print(order.name)
|
|
# products = [line.product_id.name for line in order.order_line]
|
|
# print(products)
|
|
|
|
# Update data through a record
|
|
#user.name = "Brian Jones"
|
|
|
|
def get_schema( name ):
|
|
eprint( 'get_schema' )
|
|
Model = odoo.env['ir.model']
|
|
ids = Model.search([( 'model', '=', name )])
|
|
#print( ids )
|
|
for model in Model.browse( ids ):
|
|
obj = model.read()[0]
|
|
fields = [ field.read()[0] for field in model.field_id ]
|
|
#jlog( fields )
|
|
obj['field_id'] = fields
|
|
jlog( transform_model(obj) )
|
|
eprint( "%s/schemas/%s.json" % ( DATADIR, model.model ) )
|
|
|
|
def transform_model( obj ):
|
|
return { "name": obj['model'], "modules": obj['modules'], 'fields': [ {} for field in obj['field_id'] ] }
|
|
|
|
def rel_fields( model ):
|
|
Model = odoo.env['ir.model.fields']
|
|
ids = Model.search([( 'model_id.model', '=', model ), ('relation','!=','false')])
|
|
return Model.browse( ids )
|
|
|
|
|
|
# COMMANDS
|
|
|
|
def login( server = None, port = None, db = None, user = None, passwd = None ):
|
|
|
|
global odoo
|
|
|
|
if server:
|
|
tsv( 'Server:', server )
|
|
else:
|
|
server = raw_input('Server address: ')
|
|
|
|
if port:
|
|
tsv( 'Port:', port )
|
|
else:
|
|
port = raw_input('Port: ')
|
|
|
|
odoo = odoorpc.ODOO( server, port=port )
|
|
print( 'Connected to ' + server + ':' + port )
|
|
|
|
print( 'Available databases:' )
|
|
for _db in odoo.db.list():
|
|
print( _db )
|
|
|
|
if db:
|
|
tsv( 'Database:', db )
|
|
else:
|
|
db = raw_input('Choose database: ')
|
|
|
|
if user:
|
|
tsv( 'User:', user )
|
|
else:
|
|
user = raw_input('User: ')
|
|
|
|
if passwd:
|
|
tsv( 'Password:', '****************' )
|
|
else:
|
|
passwd = raw_input('Password: ')
|
|
|
|
|
|
eprint( server, port, user, passwd )
|
|
|
|
user = odoo.login( db, user, passwd )
|
|
odoo.save('session')
|
|
|
|
# # By default, these informations are stored in the ~/.odoorpcrc file. You can however use another file:
|
|
# # odoo.save('tutorial', '~/my_own_odoorpcrc')
|
|
|
|
def search( model, domains ):
|
|
Model = odoo.env[model]
|
|
ids = Model.search( domains )
|
|
return ids
|
|
# render_tsv( model, Model.browse(ids) )
|
|
# print( 'id', 'name', sep='\t' )
|
|
# for inst in Model.browse( ids ):
|
|
# render_tsv( model, inst )
|
|
|
|
def fields( model, domains ):
|
|
Model = odoo.env['ir.model.fields']
|
|
domains = [( 'model_id.model', '=', model )] + domains
|
|
ids = Model.search( domains )
|
|
return Model.browse(ids)
|
|
|
|
|
|
stack = []
|
|
current_exports = []
|
|
|
|
def lookup_file( muid, format ):
|
|
filename = file_path( muid, format )
|
|
# print( field.ttype, field.relation, id, _filename )
|
|
if not os.path.exists( filename ):
|
|
return { 'file': filename, 'exists': False }
|
|
else:
|
|
return { 'file': filename, 'exists': True }
|
|
|
|
_visited = []
|
|
|
|
def lookup_model( muid, recurse = False ):
|
|
eprint( "> Lookup model: %s" % (muid) )
|
|
# recursive = False
|
|
# if ( 'recursive' in OPTS and OPTS['recursive'] ): # or ( 'format' in OPTS and 'json' in OPTS['format'] ):
|
|
# recursive = OPTS['recursive']
|
|
if type(recurse) == str:
|
|
recurse = recurse.split(',')
|
|
eprint( "> Recursive: %s %s" % (recurse, type(recurse)) )
|
|
|
|
_tree = {}
|
|
# _ctree = _tree
|
|
model, id = muid.split( '/' )
|
|
id = int(id)
|
|
eprint( model, id )
|
|
_tree[muid] = {}
|
|
if muid in _visited:
|
|
_tree[muid]['error'] = 'Cyclic %s already visited' % (muid)
|
|
return _tree
|
|
# _ctree = _tree[muid]
|
|
|
|
if model in odoo.env:
|
|
rfields = rel_fields( model )
|
|
eprint( rfields )
|
|
try:
|
|
inst = odoo.env[model].browse( id )
|
|
except:
|
|
_tree[muid]['error'] = 'Instance %s not found' % (muid)
|
|
return _tree
|
|
|
|
_visited.append( muid )
|
|
# data = inst.read()[0]
|
|
# size = len( json.dumps(data, indent=4) )
|
|
# filename = json_path( model, inst.id )
|
|
# _tree[muid]['size'] = size
|
|
|
|
for field in rfields:
|
|
# if data[field.name]:
|
|
# _tree[muid][field.name] = {}
|
|
_value = getattr( inst, field.name )
|
|
print( field.name )
|
|
print( _value )
|
|
if _value:
|
|
print( field.name, field.ttype )
|
|
if field.ttype == 'many2one':
|
|
id = _value.id
|
|
# _stack.append( Muid(field.relation, id) )
|
|
_tree[muid][field.name] = [ Muid(field.relation, id) ]
|
|
|
|
if field.ttype == 'one2many':
|
|
_tree[muid][field.name] = [ Muid(field.relation, id) for id in _value.ids ]
|
|
# for id in _value.ids:
|
|
# _stack.append( Muid(field.relation, id) )
|
|
|
|
# if field.ttype == 'many2many':
|
|
# if field.ttype == 'reference':
|
|
|
|
if recurse == True or type(recurse) == list and field.relation in recurse:
|
|
_tree[muid][field.name] = [ lookup_model( id ) for id in _tree[muid][field.name] ]
|
|
|
|
|
|
else:
|
|
_tree[muid]['error'] = 'Model do not exists'
|
|
|
|
return _tree
|
|
|
|
def lookup( model, domains, recurse = False ):
|
|
eprint( "> Lookup many: %s %s" % (model,domains) )
|
|
# if model in MODEL_IGNORE:
|
|
# eprint( 'IGNORED' )
|
|
# return
|
|
Model = odoo.env[model]
|
|
ids = Model.search( domains )
|
|
# rfields = rel_fields( model )
|
|
# eprint( rfields )
|
|
|
|
for inst in Model.browse( ids ):
|
|
|
|
tree = lookup_model( Muid(model, inst.id), recurse=recurse )
|
|
filename = json_path( model, inst.id )
|
|
if filename in [item['file'] for item in stack]:
|
|
continue
|
|
tree['file'] = filename
|
|
# stack.append({ 'model': model, 'id': inst.id, 'file': filename, 'data': size })
|
|
stack.append( tree )
|
|
|
|
jlog( stack )
|
|
return stack
|
|
# return flatten_stack( stack )
|
|
|
|
def flatten_stack( data ):
|
|
flat = []
|
|
|
|
def flatten( obj ):
|
|
if type(obj) == dict:
|
|
# eprint( 'is a dict')
|
|
key = obj.keys()[0] if obj.keys()[0] != 'file' else obj.keys()[1]
|
|
flat.append( key )
|
|
for field in obj[key].keys():
|
|
# eprint( 'go flat key', field )
|
|
flatten( obj[key][field] )
|
|
|
|
if type(obj) == list:
|
|
# eprint( 'is a list')
|
|
for item in obj:
|
|
# eprint( 'item: ', item, type(item))
|
|
# if type(item) in (str,unicode):
|
|
# eprint( 'sub item is a str')
|
|
# flat.append( item )
|
|
if type(item) == dict:
|
|
# eprint( 'sub item is a dict')
|
|
flatten( item )
|
|
|
|
if type(obj) == str:
|
|
# eprint( 'is a str')
|
|
flat.append( item )
|
|
|
|
for obj in data:
|
|
flatten( obj )
|
|
|
|
return flat
|
|
|
|
|
|
|
|
def lookup_old( model, domains ):
|
|
eprint( "> Lookup: %s %s" % (model,domains) )
|
|
if model in MODEL_IGNORE:
|
|
print( 'IGNORED' )
|
|
return
|
|
Model = odoo.env[model]
|
|
ids = Model.search( domains )
|
|
rfields = rel_fields( model )
|
|
eprint( rfields )
|
|
|
|
for inst in Model.browse( ids ):
|
|
data = inst.read()[0]
|
|
size = len( json.dumps(data, indent=4) )
|
|
filename = json_path( model, inst.id )
|
|
if filename in [item['file'] for item in stack]:
|
|
continue
|
|
|
|
# print( 'Preparing data for %s' % (filename) )
|
|
stack.append({ 'model': model, 'id': inst.id, 'file': filename, 'data': size })
|
|
|
|
for field in rfields:
|
|
#data[field.name] = inst[field.name].read()[0]
|
|
#eprint( data[field.name] )
|
|
if field.relation in MODEL_IGNORE:
|
|
continue
|
|
|
|
if data[field.name]:
|
|
tsv( 'Field: ', field.ttype, field.name, field.relation, data[field.name] )
|
|
|
|
if field.ttype == 'many2one':
|
|
id = data[field.name][0]
|
|
_filename = json_path( field.relation, id )
|
|
print( field.ttype, field.relation, id, _filename )
|
|
if not os.path.exists( _filename ):
|
|
lookup( field.relation, [('id','=',id)] )
|
|
else:
|
|
print('Already exists %s' % (_filename) )
|
|
|
|
if field.ttype == 'one2many':
|
|
for id in data[field.name]:
|
|
_filename = json_path( field.relation, id )
|
|
print( field.ttype, field.relation, id, _filename )
|
|
if not os.path.exists( _filename ):
|
|
lookup( field.relation, [('id','=',id)] )
|
|
else:
|
|
print('Already exists %s' % (_filename) )
|
|
|
|
print( stack )
|
|
|
|
|
|
|
|
def export_json( model, domains ):
|
|
print( "> Export to json: %s %s" % (model,domains) )
|
|
if model in MODEL_IGNORE:
|
|
print( 'IGNORED' )
|
|
return
|
|
Model = odoo.env[model]
|
|
ids = Model.search( domains )
|
|
ensure_dir( "%s/%s" % (DATADIR, model) )
|
|
rfields = rel_fields( model )
|
|
eprint( rfields )
|
|
for inst in Model.browse( ids ):
|
|
data = inst.read()[0]
|
|
filename = "%s/%s/%s.json" % ( DATADIR, model, inst.id )
|
|
if filename in current_exports:
|
|
continue
|
|
|
|
print( 'Preparing data for %s' % (filename) )
|
|
current_exports.append( filename )
|
|
|
|
# Write pretty print JSON data to file
|
|
with open( filename, "w") as write_file:
|
|
json.dump(data, write_file, indent=4)
|
|
print( "%s written" % (filename) )
|
|
|
|
|
|
for field in rfields:
|
|
#data[field.name] = inst[field.name].read()[0]
|
|
#eprint( data[field.name] )
|
|
if field.relation in MODEL_IGNORE:
|
|
continue
|
|
if data[field.name]:
|
|
tsv( 'Field: ', field.ttype, field.name, field.relation, data[field.name] )
|
|
|
|
if field.ttype == 'many2one':
|
|
id = data[field.name][0]
|
|
_filename = "%s/%s/%s.json" % ( DATADIR, field.relation, id )
|
|
print( field.ttype, field.relation, id, _filename )
|
|
if not os.path.exists( _filename ):
|
|
export_json( field.relation, [('id','=',id)] )
|
|
else:
|
|
print('Already exists %s' % (_filename) )
|
|
|
|
if field.ttype == 'one2many':
|
|
for id in data[field.name]:
|
|
_filename = "%s/%s/%s.json" % ( DATADIR, field.relation, id )
|
|
print( field.ttype, field.relation, id, _filename )
|
|
if not os.path.exists( _filename ):
|
|
export_json( field.relation, [('id','=',id)] )
|
|
else:
|
|
print('Already exists %s' % (_filename) )
|
|
|
|
def save( model, domains, recurse = False, force = False ):
|
|
# force = OPTS['force'] if 'force' in OPTS else False
|
|
models = flatten_stack( lookup( model, domains, recurse=recurse ) )
|
|
unique_models = uniq( models )
|
|
jlog( unique_models )
|
|
to_browse = groupby_model( unique_models )
|
|
|
|
for mod in to_browse.keys():
|
|
print('loading %s %s' % (len(to_browse[mod]), mod) )
|
|
if mod in odoo.env:
|
|
try:
|
|
list = odoo.env[mod].browse( to_browse[mod] )
|
|
except:
|
|
eprint('Model %s not found' % (mod) )
|
|
continue
|
|
|
|
if list:
|
|
for inst in list:
|
|
filename = json_path( mod, inst.id )
|
|
print( inst.id, inst.name, ' > ', filename )
|
|
# Write pretty print JSON data to file
|
|
with open( filename, "w") as write_file:
|
|
json.dump(inst.read()[0], write_file, indent=4)
|
|
print( "%s written" % (filename) )
|
|
|
|
return to_browse
|
|
|
|
|
|
# AUTO EXEC
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
import sys
|
|
|
|
# PARAMS = sys.argv[1:]
|
|
PARAMS = []
|
|
OPTS = {}
|
|
for param in sys.argv[1:]:
|
|
# if param[0] == '-':
|
|
# kv = param[1:].split( '=' )
|
|
# if len(kv) == 1:
|
|
# OPTS[kv[0]] = False
|
|
# else:
|
|
# OPTS[kv[0]] = kv[1]
|
|
# elif
|
|
if param[0] == '+':
|
|
kv = param[1:].split( '=' )
|
|
if len(kv) == 1:
|
|
OPTS[kv[0]] = True
|
|
else:
|
|
OPTS[kv[0]] = kv[1]
|
|
else:
|
|
PARAMS.append( param )
|
|
|
|
|
|
eprint( len(sys.argv), 'argument(s):', PARAMS, OPTS )
|
|
eprint( MODEL_IGNORE )
|
|
|
|
|
|
|
|
MODEL = PARAMS[0]
|
|
if len(PARAMS) > 1:
|
|
METHOD = PARAMS[1]
|
|
|
|
|
|
|
|
if PARAMS[0] == 'login':
|
|
login( *PARAMS[1:] )
|
|
quit()
|
|
else:
|
|
autolog()
|
|
|
|
|
|
if MODEL in odoo.env:
|
|
Model = odoo.env[MODEL]
|
|
|
|
|
|
if METHOD == 'search':
|
|
# render_tsv( odoo.env[MODEL].browse( odoo.env[MODEL].search( args2domains(PARAMS[2:]) ) ) )
|
|
ids = search( MODEL, args2domains(PARAMS[2:]) )
|
|
render( OPTS, MODEL, Model.browse(ids) )
|
|
#ids = Model.search( args2domains(PARAMS[2:]) )
|
|
#print( 'id', 'name', sep='\t' )
|
|
#for inst in Model.browse( ids ):
|
|
# render_tsv( MODEL, inst )
|
|
|
|
if METHOD == 'browse':
|
|
ids = PARAMS[2:]
|
|
ids = [ int(s) for s in ids ]
|
|
#print( ids )
|
|
for inst in Model.browse( ids ):
|
|
jlog( inst.read()[0] )
|
|
|
|
if METHOD == 'fields_get':
|
|
jlog( Model.fields_get() )
|
|
|
|
if METHOD == 'fields':
|
|
res = fields( MODEL, args2domains(PARAMS[2:]) )
|
|
render( OPTS, 'ir.model.fields', res )
|
|
# Model = odoo.env['ir.model.fields']
|
|
# ids = Model.search([( 'model_id.model', '=', MODEL )])
|
|
# render( OPTS, 'ir.model.fields', Model.browse(ids) )
|
|
#for inst in Field.browse( ids ):
|
|
# render_tsv( 'ir.model.fields', inst )
|
|
|
|
if 'lookup' in METHOD or 'save' in METHOD:
|
|
recurse = False
|
|
if ( 'recurse' in OPTS and OPTS['recurse'] ):
|
|
recurse = OPTS['recurse']
|
|
|
|
if METHOD == 'lookup':
|
|
jlog( lookup( MODEL, args2domains(PARAMS[2:]), recurse=recurse ) )
|
|
|
|
if METHOD == 'lookup-model':
|
|
jlog( lookup_model( Muid(MODEL,PARAMS[2]), recurse=recurse ) )
|
|
|
|
# if METHOD == 'lookup-recs':
|
|
# jlog( lookup_recs( MODEL, args2domains(PARAMS[2:]) ) )
|
|
|
|
if METHOD == 'export-json':
|
|
export_json( MODEL, args2domains(PARAMS[2:]) )
|
|
|
|
if METHOD == 'save':
|
|
jlog( save( MODEL, args2domains(PARAMS[2:]), recurse=recurse ) )
|
|
|
|
elif MODEL == 'db':
|
|
if METHOD == 'list':
|
|
jlog( odoo.db.list() )
|
|
|
|
|
|
elif MODEL == 'schema':
|
|
get_schema( METHOD )
|
|
|
|
|