Created
September 4, 2019 15:58
-
-
Save yucer/d29489d77670e1b935263ec1f3737fce to your computer and use it in GitHub Desktop.
Load odoo schema into neo4j (python2)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
import os | |
import sys | |
import optparse | |
import erppeek | |
from py2neo import Node, Relationship, Graph, watch | |
from py2neo.packages.httpstream import http | |
options = None | |
neo_graph = None | |
neo_modules = {} | |
neo_models = {} | |
neo_fields = {} | |
odoo_API = None | |
class DL: # DebugLevel constants | |
NONE = 0 | |
WARN = 1 | |
INFO = 2 | |
ODOO = 3 | |
NEO = 4 | |
HTTP = 5 | |
DEFAULT = INFO | |
def info(msg, _level = None): | |
_level = _level or DL.DEFAULT | |
if options.debug_level >= _level: | |
print(msg) | |
def neo_connect(host, port, user, password): | |
url = "http://%s:%s@%s:%s/db/data/" % (user, password, host, port) | |
return Graph(url) | |
def neo_create(obj): | |
info(str(obj), DL.NEO) | |
if not options.dry: | |
neo_graph.create(obj) | |
def neo_execute(cmd, params=None): | |
cmd = map_labels(cmd) | |
params = params or {} | |
info(cmd % params, DL.NEO) | |
if options.dry: | |
return None | |
else: | |
return neo_graph.run(cmd, params) | |
def neo_label(obj_name): | |
return "%s_%s" % (odoo_API, obj_name) | |
def map_labels(neo_query): | |
labels = ['Model', 'Field'] | |
nq = neo_query | |
for lab in labels: | |
nq = nq.replace(':%s' % lab, ':%s' % neo_label(lab)) | |
return nq | |
def empty_neo_graph(): | |
info('# Empty DB Graph') | |
neo_execute('MATCH (m1)-[r]->(m2) DELETE r', {}) # drop relations first | |
neo_execute('MATCH (m) DELETE m', {}) # drop nodes | |
def obj_vals(obj, exclusions=None): | |
exclusions = exclusions or [] | |
try: | |
values = obj._model.read(obj.id, obj._model.keys()) | |
fix_id = lambda v: v[0] if (type(v)==list) and len(v)==2 and type(v[0])==int and type(v[1])!=int else v | |
res = {key: fix_id(value) for (key, value) in values.items() if not key in exclusions} | |
except: | |
info("Could not read %s(%s)" % (obj._model, obj.id), DL.ODOO) | |
res = None | |
return res | |
def import_modules(): | |
global neo_modules | |
info('# Importing modules') | |
installed_modules = client.modules(installed=True).values()[0] | |
installed_modules.sort() | |
for im_name in installed_modules: | |
_m = client.model('ir.module.module').browse([('name','=',im_name)])[0] | |
f_exclusions = ['icon_image','description','description_html', 'menus_by_module', | |
'views_by_module', 'reports_by_module', 'author', 'website', 'icon', | |
'create_uid', 'create_date', 'write_uid', 'write_date', 'summary'] | |
neo_modules[_m.name] = Node(neo_label("Module"), **obj_vals(_m, f_exclusions)) | |
neo_create(neo_modules[_m.name]) | |
info('# Importing dependencies') | |
for im_name in installed_modules: | |
m1 = client.model('ir.module.module').browse([('name','=',im_name)])[0] | |
for m2 in m1.dependencies_id: | |
r_depends_on = Relationship(neo_modules[m1.name], "DEPENDS_ON", neo_modules[m2.name]) | |
neo_create(r_depends_on) | |
def import_model_relations(): | |
global neo_models | |
global neo_fields | |
info('# Importing model relations') | |
for model in neo_models: | |
_fields = neo_fields[model] | |
for field in _fields: | |
_f = _fields[field] | |
r_model = _f['relation'] | |
if r_model: | |
if r_model in neo_models: | |
r_has_field = Relationship(neo_models[model], | |
"RELATED_WITH", | |
neo_models[r_model], | |
field=_f['name'], | |
required=_f['required']) | |
neo_create(r_has_field) | |
else: | |
warn_msg = "Warning: '%s' related field from model '%s' refer to a missing model: '%s'" | |
info(warn_msg % (field, model, r_model), DL.WARN) | |
# Translate to model dependencies | |
dep_query = '''MATCH p=(m1:Model)-[r:RELATED_WITH]->(m2:Model) | |
WHERE r.required=true | |
WITH DISTINCT m1, m2 | |
RETURN m1.model as M1, m2.model as M2 | |
''' | |
for rec in neo_execute(dep_query, {}): | |
if rec["M1"] != rec["M2"]: | |
add_model_dependency(rec["M1"], rec["M2"], 'schema') | |
def get_model_deps(): | |
'''variant with networkX trasversal ''' | |
import networkx as nx | |
nx_digraph = nx.DiGraph() | |
node_set = set([]) | |
edge_set = set([]) | |
# get the nodes | |
neo_nodes_query = '''MATCH (m:Model) RETURN m.model as M''' | |
for rec in neo_execute(neo_nodes_query, {}): | |
if rec.M not in node_set: | |
node_set.add(rec.M) | |
nx_digraph.add_node(rec.M) | |
# get the dependences | |
neo_md_query = '''MATCH (m1:Model)-[:DEPENDS_ON]->(m2:Model) | |
RETURN m1.model as M1, m2.model as M2''' | |
for rec in neo_execute(neo_md_query, {}): | |
if rec["M1"] != rec["M2"]: | |
if (rec["M1"], rec["M2"]) not in edge_set: | |
edge_set.add((rec["M1"], rec["M2"])) | |
nx_digraph.add_edge(rec["M1"], rec["M2"]) | |
# calculate dependency sets | |
nx_graph = nx_digraph.to_undirected() | |
comps = sorted(nx.connected_component_subgraphs(nx_graph), key=len, reverse=options.reverse) | |
all_deps = [] | |
for c in comps: | |
cmp_digraph = nx_digraph.subgraph(c) | |
deps = nx.algorithms.dag.topological_sort(cmp_digraph, None, True) | |
all_deps.append(deps) | |
return all_deps | |
def show_model_deps(dep_sets): | |
if options.json: | |
print("# -*- coding: utf-8 -*-") | |
print("") | |
print("global more_data") | |
print("") | |
print("more_data = {") | |
print(" 'class_kjellberg_migration_V7_v8' : {") | |
print(" 'v8_model_order': [") | |
for dep in dep_sets: | |
print(" [") | |
print(" '%s'" % ("',\n '".join(dep))) | |
print(" ],") | |
print(" ],") | |
print(" },") | |
print("}") | |
else: | |
for idx in range(len(dep_sets)): | |
dep = dep_sets[idx] | |
info('\n#------- set #%d ---- %d models ------\n' % (idx, len(dep)) ) | |
for model in dep: | |
print(model) | |
def check_deps(all_deps): | |
from itertools import chain | |
dep_list = list(chain(*all_deps)) | |
order = {k:v for v,k in list(enumerate(dep_list))} | |
# verify functional dependencies | |
info('\nVerifying functional dependencies order. (A -> B) => B goes first in the order.\n') | |
for fdep in functional_model_deps: | |
if order[fdep[0]] < order[fdep[1]]: | |
info("order doesn't meet functional dependency: %s (%d) > %s (%d)" % (fdep[0], order[fdep[0]], fdep[1], order[fdep[1]]), DL.WARN) | |
else: | |
info("functional dependency verified: %s (%d) > %s (%d)" % (fdep[0], order[fdep[0]], fdep[1], order[fdep[1]])) | |
# verify delegated inheritances | |
info('\nVerifying inherits by delegation order. Childs should go first in the order.\n') | |
inherits_q = 'MATCH (a:Model)-[:INHERITS_FROM]->(b:Model) RETURN a.model as M1, b.model as M2' | |
for rec in neo_execute(inherits_q, {}): | |
child = rec["M1"] | |
parent = rec["M2"] | |
if order[child] > order[parent]: | |
info("order doesn't meet inheritance by delegation: child '%s' (%d) > parent '%s' (%d)" %\ | |
(child, order[child], parent, order[parent]), DL.WARN) | |
else: | |
info("inheritance by delegation order veryfied: child '%s' (%d) < parent '%s' (%d)" %\ | |
(child, order[child], parent, order[parent]), DL.WARN) | |
def model_depends(model1, model2): | |
search_dep_q =''' | |
MATCH (m:Model {model:'%(m1)s'})-[r:DEPENDS_ON*]->(m2:Model {model:'%(m2)s'}) | |
RETURN m.model as M1, m2.model as M2 | |
''' | |
params = {'m1': model1, 'm2': model2 } | |
recs = neo_execute(search_dep_q % params, {}) | |
return bool(len(recs)) | |
def rate_model(dlist, idx): | |
rels_q=''' | |
OPTIONAL MATCH (a:Model {model:'%(model)s'})<-[r:RELATED_WITH]-(b:Model) | |
WHERE b.model in %(mafter)s | |
WITH a.model as M1, count(b) as referrers | |
RETURN M1, referrers''' | |
prms = { | |
'model': dlist[idx], | |
'mafter': str([str(a) for a in dlist[idx+1:]]) | |
} | |
w = list(neo_execute(rels_q % prms, {}))[0] | |
return (prms['model'], w.referrers) | |
def rate_sol(dlist): | |
nlist=[] | |
for idx in range(len(dlist)): | |
model, w = rate_model(dlist, idx) | |
nlist.append((model, w)) | |
rating=sum(map(lambda x:x[1], nlist)) | |
info('solution rated: %d' % rating, DL.INFO) | |
return rating | |
def optimize_deps(dep_sets): | |
import itertools | |
dep_sets2 = sorted(dep_sets, key=lambda ds:len(ds), reverse=True) | |
dlist = list(itertools.chain(*dep_sets2)) | |
rating2 = rate_sol(dlist) | |
for passnum in range(len(dlist)-1, 0, -1): | |
moves=0 | |
for i in range(passnum): | |
m1, w1 = rate_model(dlist, i) | |
m2, w2 = rate_model(dlist, i+1) | |
if (w2 < w1) and not model_depends(m2, m1): | |
tmp = dlist[i] | |
dlist[i] = dlist[i+1] | |
dlist[i+1] = tmp | |
moves = moves + 1 | |
if moves: | |
info(' -> %d moves made' % moves, DL.INFO) | |
rating = rating2 | |
rating2 = rate_sol(dlist) | |
if (rating2 >= rating): | |
break | |
else: | |
break | |
return [dlist] | |
def query_model_deps(): | |
all_deps = get_model_deps() | |
if options.optimize: | |
all_deps = optimize_deps(all_deps) | |
show_model_deps(all_deps) | |
check_deps(all_deps) | |
def import_models(): | |
global neo_modules | |
global neo_models | |
global neo_fields | |
info('# Importing models') | |
model_obj = client.model('ir.model') | |
model_ids = model_obj.search([]) | |
_models = model_obj.browse(model_ids) | |
f_exclusions = ['create_uid', 'create_date', 'write_uid', 'write_date'] | |
for _m in _models: | |
vals = obj_vals(_m, f_exclusions) | |
if vals is None: | |
info("# Could not import '%s'" % _m.model) | |
else: | |
neo_models[_m.model] = Node(neo_label("Model"), **vals) | |
neo_create(neo_models[_m.model]) | |
for m1 in _models: | |
if m1.model not in neo_models: | |
info("# Skipping '%s' inheritance info" % m1.model) | |
continue | |
info("# Importing '%s' inheritance info" % m1.model) | |
if 'inherited_model_ids' in m1._model.keys(): | |
for m2 in m1.inherited_model_ids: | |
if m2.model not in neo_models: | |
info("# Skipping '%s' inheritance info" % m2.model) | |
continue | |
r_inherits_from = Relationship(neo_models[m1.model], "INHERITS_FROM", neo_models[m2.model]) | |
neo_create(r_inherits_from) | |
else: | |
info("# API doesn't support inherited_model_ids !", DL.WARN) | |
info("# Importing '%s' implementation info" % m1.model) | |
for m2_name in m1.modules.split(', '): | |
r_implemented_on = Relationship(neo_models[m1.model], "IMPLEMENTED_ON", neo_modules[m2_name]) | |
neo_create(r_implemented_on) | |
info("# Importing '%s' fields" % m1.model) | |
_fields = neo_fields.get(m1.model, None) or {} | |
for _f in m1.field_id: | |
if _f.name not in f_exclusions: | |
f_values = obj_vals(_f) | |
if f_values is None: | |
info("# Could not import field [%s].%s" % (m1.model, _f.name)) | |
else: | |
f_node = Node(neo_label("Field"), **f_values) | |
neo_create(f_node) | |
_fields[_f.name] = f_node | |
r_has_field = Relationship(neo_models[m1.model], "HAS_FIELD", f_node) | |
neo_create(r_has_field) | |
neo_fields[m1.model] = _fields | |
import_model_relations() | |
def delete_memory_models(): | |
'''exclude memory models''' | |
info('# Excluding memory models') | |
neo_execute("MATCH (mo:Model {osv_memory:true})-[r]-() DELETE r") | |
neo_execute("MATCH (mo:Model {osv_memory:true}) DELETE mo") | |
def add_field_dependency(model1, field1, store1, path, model2, field2, store2): | |
search_dep_q =''' | |
MATCH (m1:Model {model:'%(m1)s'})-[:HAS_FIELD]->(f1:Field {name:'%(f1)s'}), | |
(m2:Model {model:'%(m2)s'})-[:HAS_FIELD]->(f2:Field {name:'%(f2)s'}), | |
(f1:Field)-[r:FDEPENDS_ON {path:'%(path)s'}]->(f2:Field) | |
RETURN f1.name as F1, f2.name as F2 | |
''' | |
add_dep_q =''' | |
MATCH (m1:Model {model:'%(m1)s'})-[:HAS_FIELD]->(f1:Field {name:'%(f1)s'}), | |
(m2:Model {model:'%(m2)s'})-[:HAS_FIELD]->(f2:Field {name:'%(f2)s'}) | |
SET f1.store='%(s1)s', f2.store='%(s2)s' | |
CREATE (f1)-[r:FDEPENDS_ON {path:'%(path)s'}]->(f2) | |
''' | |
params = {'m1': model1, 'f1': field1, 's1': store1.lower(), | |
'path': path, | |
'm2': model2, 'f2': field2, 's2': store2.lower()} | |
cursor = neo_execute(search_dep_q % params, {}) | |
if cursor.forward(): | |
info('field dependency skipped %(f1)s -> %(f2)s (already exist!)' % params, DL.WARN) | |
else: | |
neo_execute(add_dep_q % params, {}) | |
info('field dependency added %(f1)s -> %(f2)s' % params) | |
def load_field_deps(fdeps_file): | |
import csv, codecs, cStringIO | |
# def unicode_csv_reader(utf8_data, dialect=csv.excel, **kwargs): | |
# csv_reader = csv.reader(utf8_data, dialect=dialect, **kwargs) | |
# for row in csv_reader: | |
# yield [unicode(cell, 'utf-8') for cell in row] | |
with open(fdeps_file) as fdeps: | |
csv_reader = csv.reader(fdeps, delimiter=',', quotechar='"') | |
header = csv_reader.next() | |
for row in csv_reader: | |
m1, f1, s1, path, m2, f2, s2 = tuple(row) | |
add_field_dependency(m1, f1, s1, path, m2, f2, s2) | |
def add_model_dependency(model1, model2, dtype): | |
search_dep_q =''' | |
MATCH (m:Model {model:'%(m1)s'})-[r:DEPENDS_ON {dtype:'%(dtype)s'}]->(m2:Model {model:'%(m2)s'}) | |
RETURN m.model as M1, m2.model as M2 | |
''' | |
add_dep_q =''' | |
MATCH (m:Model {model:'%(m1)s'}), (m2:Model {model:'%(m2)s'}) | |
CREATE (m)-[r:DEPENDS_ON {dtype:'%(dtype)s'}]->(m2) | |
''' | |
params = {'m1': model1, 'm2': model2, 'dtype':dtype } | |
cursor = neo_execute(search_dep_q % params, {}) | |
if cursor.forward(): | |
info('%(dtype)s dependency skipped %(m1)s -> %(m2)s (already exist!)' % params, DL.WARN) | |
else: | |
neo_execute(add_dep_q % params, {}) | |
info('%(dtype)s dependency added %(m1)s -> %(m2)s' % params) | |
def add_multicompany_deps(): | |
info('# Adding multicompany dependencies ...') | |
neo_mc_query = ''' | |
MATCH (m:Model)-[r:RELATED_WITH {field:'company_id', required:false}]->(m2:Model {model:'res.company'}) | |
OPTIONAL MATCH revs=(m2)-[i:RELATED_WITH]->(m) | |
WITH m, m2, collect(i) AS rev_coll | |
WHERE none(rev in rev_coll where rev.required) | |
RETURN m.model as M1, m2.model as M2, rev_coll ORDER BY m.model | |
''' | |
for rec in neo_execute(neo_mc_query, {}): | |
if rec["M1"] != rec["M2"]: | |
add_model_dependency(rec["M1"], rec["M2"], 'multicompany') | |
def add_rev_inherits_deps(): | |
info('# Adding reverse dependencies for inherits by delegation ...') | |
neo_mc_query = ''' | |
MATCH (a:Model)-[:INHERITS_FROM]->(b:Model) | |
RETURN a.model as M1, b.model as M2 | |
''' | |
for rec in neo_execute(neo_mc_query, {}): | |
if rec["M1"] != rec["M2"]: | |
add_model_dependency(rec["M2"], rec["M1"], 'rev-inheritance-by-delegation') | |
def add_special_relation_deps(model): | |
return # not tested still | |
info("# Treating relationships to model '%s' dependencies ..." % model) | |
neo_rel_q = ''' | |
MATCH (m:Model)-[r:RELATED_WITH {required:false}]-(m2:Model {model:'%(model)s'}) | |
WHERE (m<>m2) | |
RETURN m.model as M1, m2.model as M2 | |
''' | |
for rec in neo_execute(neo_rel_q, {}): | |
if rec["M1"] != rec["M2"]: | |
add_model_dependency(rec["M1"], rec["M2"], 'special-relation') | |
def add_user_deps(): | |
add_special_relation_deps('res.users') | |
functional_model_deps= [ | |
(u'product.template', u'product.product'), | |
(u'stock.move', u'kjerp.product.uomconvert.line'), | |
(u'res.country', u'kjerp.sales.area'), | |
(u'res.partner', u'res.country'), | |
(u'account.move', u'account.journal'), | |
# (u'stock.production.lot', u'stock.quant'), | |
] | |
def add_functional_deps(): | |
# add functional dependencies | |
if options.multicompany_deps: | |
add_multicompany_deps() | |
if options.users_deps: | |
add_user_deps() | |
add_rev_inherits_deps() | |
if not options.no_funct_deps: | |
for (m1, m2) in functional_model_deps: | |
add_model_dependency(m1, m2, 'functional') | |
info("functional dependency loaded '%s' -> '%s' ...." % (m1, m2)) | |
def remove_close_dep_loops(): | |
'''Exclude closed dependency loops excluding schema dependencies | |
invalidated by odoo functional invariants for some field cases. | |
''' | |
info('# Removing closed dependency loops ...') | |
info('# CASE #1: Exclude inherits by delegation') | |
neo_execute(''' | |
// Exclude the inherits by delegation | |
MATCH (a:Model)-[:INHERITS_FROM]->(b:Model), (a)-[r:DEPENDS_ON]->(b) | |
DELETE r | |
//CREATE (b)-[:DEPENDS_ON]->(a) | |
//RETURN a.model, b.model | |
''') | |
info('# CASE #2: Exclude the inverse required fields') | |
neo_execute(''' | |
// Exclude the inverse required fields. | |
MATCH (m:Model)-[:HAS_FIELD]->(f:Field), | |
(m)-[r:DEPENDS_ON {dtype:'schema'}]->(m2:Model) | |
WHERE (f.relation<>'') | |
AND (f.relation_field<>'') | |
AND (f.required<>false) | |
AND (f.relation_field<>false) | |
AND (m2.model=f.relation) | |
DELETE r | |
//RETURN m.model, f.name, f.relation_field, f.relation, m2.model | |
''') | |
info('# CASE #3: Company creates its partner automatically') | |
neo_execute(''' | |
MATCH (m:Model {model:'res.company'})-[r:DEPENDS_ON]->(m2:Model {model:'res.partner'}) | |
DELETE r | |
//RETURN m.model, m2.model | |
''') | |
info('# CASE #4: Exclude loops to self model') | |
neo_execute(''' | |
MATCH (m:Model)-[r:DEPENDS_ON]->(m:Model) | |
DELETE r | |
//RETURN m.model | |
''') | |
def import_openupgrade_mappings(): | |
'''import openupgrade migration mappings''' | |
info('# importing openupgrade migration mappings') | |
if not os.path.exists(options.ou_dir): | |
info('ERROR: Openupgrade code folder not found in "%s"' % options.ou_dir ) | |
else: | |
# read upgrades | |
upgrades = {} | |
for root, directories, filenames in os.walk(options.ou_dir): | |
for filename in filenames: | |
if filename.endswith('openupgrade_analysis.txt'): | |
fanalysis = os.path.join(root, filename) | |
parts = root.split('/') | |
version = parts[-1] | |
module = parts[-3] | |
upgrades[module]=(version, fanalysis) | |
# parse the anaylisis files | |
from itertools import takewhile | |
for module in upgrades: | |
# if not module in neo_modules: | |
# info('Ignoring openupgrade module upgrade (module not loaded): %s ' % module, DL.WARN) | |
# else: | |
info('Loading openupgrade mappings for module: %s ' % module) | |
fname = upgrades[module][1] | |
lines = [] | |
strip_all = lambda x: [s.strip() for s in x] | |
with open(fname) as f: | |
lines = list(takewhile(lambda l: 'XML records' not in l, f))[1:] | |
for line in lines: | |
meta = line.split(':', 1) | |
_module, model, field = strip_all(meta[0].split('/')) | |
if module!=_module: | |
info("analysis line does not correspond to the module '%s':\n %s" % (module, line), DL.WARN) | |
changes = meta[1].strip() | |
neo_execute("CREATE (d:Delta {module:'%s', model:'%s', field:'%s', change:'%s'})" % ( | |
module, model, field, changes)) | |
# insert into the graph | |
def main(): | |
global neo_graph | |
global options | |
global client | |
global odoo_API | |
p = optparse.OptionParser() | |
p.add_option('--env', '-e', help='erppeek enviroment section with cx info', default='V8') | |
p.add_option('--odoo-api', '-V', help='odoo api version', default='') | |
p.add_option('--neo-host', '-H', help='neo4j host', type="string", default="localhost") | |
p.add_option('--neo-port', '-p', help='neo4j port', type="int", default=8474) | |
p.add_option('--neo-user', '-u', help='neo4j user', type="string", default="neo4j") | |
p.add_option('--neo-pwd', '-w', help='neo4j password', type="string", default="admin") | |
p.add_option('--neo-timeout', '-O', help='neo4j client timeout', type="int", default=9999) | |
p.add_option('--debug-level', '-L', help='0 None, 1 Warnings, 2 Info (default), 3 Neo, 4 HTTP', | |
type="int", default=DL.DEFAULT) | |
p.add_option('--debug', '-D', help='call debug entrypoint', action="store_true", default=False) | |
p.add_option('--import-all', '-a', help='import all odoo info', action="store_true", default=False) | |
p.add_option('--import-ou', '-G', help='import openupgrade mapping info', action="store_true", default=False) | |
p.add_option('--meta', '-m', help='path to elego metadata-based migration code (default: $META_SRC)', type="string", default="") | |
p.add_option('--ou', '-g', help='path to openupgrade code (default: $OU_SRC)', type="string", default="") | |
p.add_option('--ou-version', '-v', help='openupgrade version', type="string", default='8.0') | |
p.add_option('--dry', '-d', help='just show the planned operations over the graph', action="store_true", default=False) | |
p.add_option('--clean', '-c', help='erase neo db befor start', action="store_true", default=False) | |
p.add_option('--module', '-M', help='restrict the query to the object of the given module', action="store_true", default=False) | |
p.add_option('--no-osv-memory', '-N', help='exclude memory models', action="store_true", default=False) | |
p.add_option('--no-loops', '-l', help='remove dependency loops', action="store_true", default=False) | |
p.add_option('--no-funct-deps', '-F', help='exclude functional dependencies', action="store_true", default=False) | |
p.add_option('--reverse', '-r', help='reverse the order of dependency sets (bigger first) ', action="store_true", default=False) | |
p.add_option('--multicompany-deps', '-C', help='treat multicompany fields as dependency', action="store_true", default=False) | |
p.add_option('--users-deps', '-U', help='force user fields as dependency', action="store_true", default=False) | |
p.add_option('--query-model-deps', '-S', help='calculate odoo model dependency sets on neo4j using networkx', | |
action="store_true", default=False) | |
p.add_option('--optimize', '-z', help='optimize orders (more refered models first)', action="store_true", default=False) | |
p.add_option('--json', '-J', help='returns a json structure', action="store_true", default=False) | |
p.add_option('--load-fdeps', '-f', help='load fields dependencies from external csv', type="string", default=False) | |
options, arguments = p.parse_args() | |
if options.json: | |
options.debug_level = 0 | |
info('\n using odoo connection=%s' % options.env) | |
odoo_API = options.odoo_api or options.env[:2] | |
info('\n using API version=%s' % odoo_API) | |
assert odoo_API in ('V7', 'V8', 'V9'), 'Not valid odoo API detected!. Please specify a valid connection prefix or use -V' | |
options.ou = options.ou or os.environ.get('OU_SRC', '') | |
if not options.ou: | |
options.ou_version = '' | |
else: | |
info('\n Using Openupgrade mapping info from: %s' % options.ou) | |
options.meta = options.meta or os.environ.get('META_SRC', '') | |
if not options.meta: | |
info('\n Using Metadata''s migration code from: %s' % options.meta) | |
http.socket_timeout = options.neo_timeout | |
if options.clean or options.import_all or options.query_model_deps or options.no_loops or options.import_ou or options.debug or options.load_fdeps: | |
neo_graph = neo_connect(options.neo_host, options.neo_port, options.neo_user, options.neo_pwd) | |
if options.debug_level >= DL.HTTP : | |
watch("httpstream") | |
if options.clean: | |
empty_neo_graph() | |
if options.debug: | |
import_model_relations() | |
if options.import_all: | |
client=erppeek.Client.from_config(options.env) | |
import_modules() | |
import_models() | |
if options.load_fdeps: | |
load_field_deps(options.load_fdeps) | |
if options.no_osv_memory: | |
delete_memory_models() | |
# remove loop dependencies after all dependency additions | |
if options.import_all and not options.no_funct_deps: | |
add_functional_deps() | |
if options.import_all or options.no_loops: | |
remove_close_dep_loops() | |
# if options.import_ou: or options.import_all: | |
# import_openupgrade_mappings() | |
if options.query_model_deps: | |
query_model_deps() | |
else: | |
p.print_help() | |
sys.exit(2) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Last time tested with:
docker run -d --name neo4j -v ~/neo4j-data:/data -p 8474:7474 -p 7687:7687 neo4j:3.1.3