#!/usr/bin/env python ## Usage: ## ./remote.py [ [ []]] [+] [+] ## Where are: ## json Renders json data. ## tsv Renders tsv data. ## csv Renders csv data. ## xml Soon WIP Renders xml data. ## recurse Execute the recursively. ## recurse=L Execute the 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 )