commit 859fe7ae3698ec30bf934ad55b6997001db5a0b4 Author: DiG Date: Fri Jan 14 14:27:23 2022 +0100 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e38ac3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,142 @@ +# Project +datas +# ---> Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + diff --git a/.modelignore b/.modelignore new file mode 100644 index 0000000..f4f7a63 --- /dev/null +++ b/.modelignore @@ -0,0 +1,7 @@ +ir.ui.view +ir.model.access +ir.module.module +ir.module.category +res.users +res.groups +res.company diff --git a/.relationrc b/.relationrc new file mode 100644 index 0000000..5d538af --- /dev/null +++ b/.relationrc @@ -0,0 +1,3 @@ +# + +product.uom diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c075f8 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# odoo-export + +Exports odoo models and datas to flat files (JSON or TSV) \ No newline at end of file diff --git a/ir.field.sh b/ir.field.sh new file mode 100644 index 0000000..efb8f34 --- /dev/null +++ b/ir.field.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +model="ir.model.fields" +folder="datas/$model" +mkdir -p "$folder" + +#./odoo-rpc.py $model browse 20 > "$folder/20.json" +ids="$(./odoo-rpc.py $model search model_id.model = $1)" diff --git a/odoo-rpc.py b/odoo-rpc.py new file mode 100644 index 0000000..c036754 --- /dev/null +++ b/odoo-rpc.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python + +# Usage: +# ./odoo-export.py [ [ []]] + + +from __future__ import print_function +import os +import sys +import json +import odoorpc # pip install odoorpc + + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + +# CONFIG +FOLDER = '/home/dig/odoo-export/datas' +PARAMS = sys.argv[1:] + +MODEL_IGNORE = [] +with open(".modelignore", "r") as file: + for line in file: + MODEL_IGNORE.append(line.strip()) + +eprint( len(sys.argv), 'argument(s):', str(sys.argv) ) +eprint( MODEL_IGNORE ) + + + +# TABLE VIEW COLUMNS +cols = { + '': [ 'id', 'name' ], # default + 'ir.model': [ 'id', 'model' ], + 'ir.model.fields': [ 'id', 'name', 'ttype', 'create_uid', 'relation', 'display_name', 'complete_name' ], +} + + + +def jlog( obj ): + print( json.dumps(obj, indent=4) ) #"\t") ) + +def ensure_dir(d): + if not os.path.exists(d): + os.makedirs(d) + +def args2domains( list ): + domains = [] + while len(list) > 0: + str = list[0] + if str == '&' or str == '|' or str == '!': + domains.append( str ) + list = list[1:] + else: + domains.append( (list[0], list[1], list[2]) ) + list = list[3:] + return domains + +eprint( args2domains(sys.argv[3:]) ) +#quit() + +odoo = None + +def autolog(): + # Then, use the odoorpc.ODOO.load() class method: + 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 + + +# Prepare the connection to the server +# odoo = odoorpc.ODOO('localhost', port=8069) + +# Check available databases +# eprint( 'Databases: ', odoo.db.list() ) +#jlog( odoo.db.list() ) + + + + + +# 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" % ( FOLDER, 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 ) + + +def tsv( *fields ): + print( *fields, sep='\t' ) + +def render_tsv( model, list ): + + if model in cols: + _cols = cols[model] + else: + _cols = cols[''] + + tsv( *_cols ) + + for obj in list: + tsv( *[ obj[col] for col in _cols] ) + +#def render_json( model, obj ): + + +# COMMANDS + +def login( server = None, port = None, db = None, user = None, passwd = None ): + + 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 ) + 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'] + ids = Model.search([( 'model_id.model', '=', MODEL )]) + render_tsv( MODEL, Model.browse(ids) ) + +current_exports = [] + +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" % (FOLDER, model) ) + rfields = rel_fields( model ) + eprint( rfields ) + for inst in Model.browse( ids ): + data = inst.read()[0] + filename = "%s/%s/%s.json" % ( FOLDER, 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" % ( FOLDER, 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" % ( FOLDER, 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) ) + + + + + +# AUTO EXEC +MODEL = PARAMS[0] +if len(PARAMS) > 1: + METHOD = PARAMS[1] + + + +if PARAMS[0] == 'login': + login( *PARAMS[1:] ) + quit() +else: + odoo = autolog() + + +if MODEL in odoo.env: + Model = odoo.env[MODEL] + + + if METHOD == 'search': + search( MODEL, args2domains(sys.argv[3:]) ) + #ids = Model.search( args2domains(sys.argv[3:]) ) + #print( 'id', 'name', sep='\t' ) + #for inst in Model.browse( ids ): + # render_tsv( MODEL, inst ) + + if METHOD == 'browse': + ids = sys.argv[3:] + ids = [ int(s) for s in ids ] + #print( ids ) + for inst in Model.browse( ids ): + jlog( inst.read()[0] ) + + if METHOD == 'export-json': + export_json( MODEL, args2domains(sys.argv[3:]) ) + + if METHOD == 'fields_get': + jlog( Model.fields_get() ) + + if METHOD == 'fields': + Model = odoo.env['ir.model.fields'] + ids = Model.search([( 'model_id.model', '=', MODEL )]) + render_tsv( MODEL, Model.browse(ids) ) + #for inst in Field.browse( ids ): + # render_tsv( 'ir.model.fields', inst ) + +elif MODEL == 'db': + if METHOD == 'list': + jlog( odoo.db.list() ) + + +elif MODEL == 'schema': + get_schema( METHOD ) + + + diff --git a/product.uom.sh b/product.uom.sh new file mode 100644 index 0000000..7bb5067 --- /dev/null +++ b/product.uom.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +folder="datas/product.uom" +mkdir -p "$folder" + +./odoo-rpc.py product.uom browse 20 > "$folder/20.json"