#!/usr/bin/env python # Orlando Soto, June 2014. from DataBaseHandler import * import os, shutil, sys, time def get_options(argv): source_db, dest_db = (argv[-2], argv[-1]) force = False update = False start_node = '' link_key = '' dest_schema = False columns = None for i in range(1,len(argv)): if argv[i] in ['--help','-h']: print """ %s [options] source_file destiny_file The script will merge data from 'source_file' data base into 'destiny_file' data base creating a new file called 'destiny_file.out', preserving the information in source and destiny data bases. Must be used with SQLite3 data base files. options: [ -s or --starting-point' starting_node ] : String representing the starting node to proceed with the merging. E.g. 'BCAL:DISC' will paste BCAL:DISC and all his branches at 'BCAL' in the destiny data base. [ -l or --link-key table_name] : Name of the table to be linked through the leaves of the data base tree (detector hierarchy). [ -f or --force ] : If the merge starting point exists in the destiny data base, it will be overwritten. [ -i --source-file file_name ] : Use 'file_name' as source data base. [ -o or --dest-file file_name ] : Use 'file_name' as destiny data base. [ -u or --update col_n | --all | -a ] : Update using the column names specified. 'col_n' must be a list with comma separated, e.g. 'type,chanid'. Alternatively you can put --all or -a instead, and all column except primary and foreign keys will be updated. [ --dest-schema ] : Use destiny data base schema to read data bases. [ -h or --help ] : Show this help. Example: ./merge_db.py -s BCAL:DISC -l 'channel' tt.db tt_dest.db This command will create 'tt_dest.db.out' containing the data of tt.db (BCAL:DISC) merged into tt_dest.db. """% argv[0] sys.exit(0) if argv[i] in ['--starting-point','-s']: try: start_node = str(argv[i+1]) except ValueError: print 'Error: bad migration starting point.' sys.exit(1) except IndexError: print 'Error: You should provide starting node full name.' sys.exit(1) if argv[i] in ['--link-key','-l']: try: link_key = str(argv[i+1]) except ValueError: print 'Error: bad migration starting point.' sys.exit(1) except IndexError: print 'Error: You should provide starting node full name.' sys.exit(1) if argv[i] in ['--force','-f']: force = True if argv[i] in ['--source-file','-i']: try: source_db = str(argv[i+1]) except ValueError: print 'Error: bad migration starting point.' sys.exit(1) except IndexError: print 'Error: You should provide starting node full name.' sys.exit(1) if argv[i] in ['--dest-file','-o']: try: dest_db = str(argv[i+1]) except ValueError: print 'Error: bad migration starting point.' sys.exit(1) except IndexError: print 'Error: You should provide starting node full name.' sys.exit(1) if argv[i] in ['--update','-u']: update = True columns = None try: col = argv[i+1] except IndexError: print "Error: Missing column names to update." sys.exit(1) if col in ['--all','-a']: columns = None else: columns = col.split(',') if argv[i] in ['--dest-schema']: dest_schema = True if len(sys.argv)<3: print 'You must supply at least 2 DB files, source DB and destiny DB.' sys.exit(1) return start_node.split(':'), force, update, columns, link_key, dest_schema, source_db, dest_db def make_link(dh_s,dh_d,link_key): node_cnt = 0 session2 = dh_d.get_session() DH = dh_s.__class__ print 'Making links with %s on Detector_Hierarchy leaves'%link_key leaves = dh_s.get_leaves() count = 0 for l in leaves: dh_d = DH() dh_d = dh_d.get_node(session2,l.get_node_name().split(':')) link_leaf = getattr(l,link_key) if link_leaf: linked = True for col in filter(lambda x: x.primary_key,getattr(l,link_key).__table__.c._data.values()): linked = linked and (getattr(dh_d, col.name) <> None) else: linked = True if linked: count = count + 1 print ('\rcomplete %03d %%'%(float(count)/float(len(leaves))*100)), continue link_root = link_leaf.get_root_parent() data = link_root.get_data() root_dest = link_root.get_node(session2,data) node_cnt = node_cnt+1 if not root_dest.is_complete: print '\nCreating entry for table %s at destiny DB'% link_root.__table__.name root_dest,node_count = link_root.clone_all() node_cnt = node_count + node_cnt session2.add(root_dest) session2.commit() leaf_dest = root_dest cnt = 0 while leaf_dest.children: node_cnt = node_cnt+1 leaf_found = None cnt = cnt + 1 for child in leaf_dest.children: node_cnt = node_cnt+1 child_source = link_leaf.get_root_parent(cnt) if not child_source.has_diff(child.get_data()): leaf_found = child break if not leaf_found: print '\nCreating entry for table %s at destiny DB'% child_source.__table__.name leaf_found, node_count = child_source.clone_all() node_cnt = node_count + node_cnt leaf_dest.children.append(leaf_found) session2.commit() #print leaf_found.children else: pass #print '\nEntry in table %s found'% child_source.__table__.name leaf_dest = leaf_found for col in filter(lambda x: x.primary_key,getattr(l,link_key).__table__.c._data.values()): setattr(dh_d, col.name, getattr(leaf_dest,col.name)) count = count + 1 print ('\rcomplete %03d %%'%(float(count)/float(len(leaves))*100)), sys.stdout.flush() print 'Done!' return node_cnt def merge(dh_s,dh_d,start_node): node_cnt = 0 session2 = dh_d.get_session() DH = dh_s.__class__ # global session1,session2,dh_d,dh_s,link_key,DH dh_d = dh_d.get_node(session2,start_node[:-1]) if not dh_d.is_complete: print "node %s not found. Merge can't procced starting at the selected node "%start_node[:-1] sys.exit(1) print 'Merging Detector_Hierarchy tree' dh_clone,node_count = dh_s.clone_all() node_cnt = node_cnt + node_count dh_clone.parent_id = dh_d.id session2.add(dh_clone) session2.commit() print 'Done!' return node_count #### MAIN ##### start = time.time() node_cnt = 0 start_node, force, update, columns, link_key, dest_schema, source_db, dest_db = get_options(sys.argv) if not (os.path.exists(source_db)): raise OSError, "File %s not found"%source_db if not (os.path.exists(dest_db)): raise OSError, "File %s not found"%dest_db out_db = dest_db + ".out" shutil.copyfile(dest_db,out_db) engine1 = create_engine('sqlite:///%s'%source_db, echo=False) # Create a connector for the data base SQLite engine2 = create_engine('sqlite:///%s'%out_db, echo=False) # Create a connector for the data base SQLite Session = sessionmaker(bind=engine1) # Bind to the data base. session1 = Session() # Connect to the data base (will connect on the first rollback, commit). Session = sessionmaker(bind=engine2) # Bind to the data base. session2 = Session() # Connect to the data base (will connect on the first rollback, commit). Base1 = declarative_base() # Get the instance of the class to read the data base schemas. Base1.metadata.reflect(engine1) # Reading daba base table schemas. Base1.root_name = '' # Field created for tree structured tables Base2 = declarative_base() # Get the instance of the class to read the data base schemas. Base2.metadata.reflect(engine2) # Reading daba base table schemas. Base2.root_name = '' # Field created for tree structured tables Base = Base1 if dest_schema: Base = Base2 DH, Cr, Mo, Ch = make_classes(Base) dh_s = DH() dh_d = DH() dh_s = dh_s.get_node(session1,start_node) if not dh_s.is_complete: print "Error: source DB doesn't have selected node %s"%start_node sys.exit(1) dh_d = dh_d.get_node(session2,start_node) if not dh_d.is_complete: node_count = merge(dh_s,dh_d,start_node) node_cnt = node_cnt + node_count if link_key: node_count = make_link(dh_s,dh_d,link_key) node_cnt = node_cnt + node_count session2.commit() else: if not update and not force: print "Node '%s' already exist. See --help for options --update or --force"%start_node sys.exit(1) if force: print "Node '%s' already exist. All data starting at '%s' will be replaced with the data from source data base '%s'"%(start_node,start_node,source_db) session2.delete(dh_d) node_count = merge(dh_s,dh_d,start_node) node_cnt = node_cnt + node_count if link_key: node_count = make_link(dh_s,dh_d,link_key) node_cnt = node_cnt + node_count session2.commit() else: print node_count = dh_d.copy_data_all(dh_s,columns) node_cnt = node_cnt + node_count session2.commit() if link_key: make_link(dh_s,dh_d,link_key) session2.commit() end = time.time() print "All done!\nElapsed time: %f (s)" % (float(end-start)) print "Nodes processed: %d" % node_cnt print "Average seconds per node : %f s" % ((float(end-start))/float(node_cnt))