diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5c248dad5f3f1e08156415db59e6b9dc2d420525..70aed2d9a5bee3a7d8495b72b7bd308895dc7572 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -81,13 +81,9 @@ test_reception_upload: - curl ${CONFIGDB_API_URI}/health # upload a blank template runkey - creates a runkey in the database - #- python3 scripts/main_configDB.py --host ${ENV_API_HOSTNAME} --port ${ENV_API_PORT} --upload-template - # TODO: we currently use a hack: build a blank template and add a new stave with it, - # because we cannot just re-upload a runkey in ConfigDB now. - # So, there is no need to upload a preliminary template. - # Uncomment this, when re-uploading a runkey works in ConfigDB. + - python3 scripts/main_configDB.py --host ${ENV_API_HOSTNAME} --port ${ENV_API_PORT} --upload-template - # upload a new stave to the "latest" runkey + ## upload a new stave to the "latest" runkey - python3 scripts/main_configDB.py --host ${ENV_API_HOSTNAME} --port ${ENV_API_PORT} --upload-stave NewStaveName examples/sr1-barrel-st-4staves/CERNSR1_stave0_testing_setup_small.json # test uploading a new stave directly from Python - python3 scripts/test_upload_a_stave.py ${ENV_API_HOSTNAME} ${ENV_API_PORT} diff --git a/scripts/main_configDB.py b/scripts/main_configDB.py index 485cfdf9337b71dda0fd8bd4d6a75253bebc9291..e9bdbca318c70c4e786455f0d12c651af9fd4b8c 100755 --- a/scripts/main_configDB.py +++ b/scripts/main_configDB.py @@ -442,7 +442,7 @@ if __name__ == "__main__": runkey_dict_template['children'].append(staves_node) runkey_id = configDB.stage_create(data=runkey_dict_template, comment="upload an blank runkey") logging.info(f"Staged id: {runkey_id}") - ids = configDB.stage_commit(runkey_id, new_name=configDBname, comment=comment) + ids = configDB.stage_commit(runkey_id, new_name=configDBname+"_blankTemplate", comment=comment) logging.info(f"Committed ids: {ids}") if args.upload: @@ -473,54 +473,80 @@ if __name__ == "__main__": # Create RunkeyStave stave_name, stave_fpath = args.upload_stave - a_stave_runkey = fxt.build_stave_node_from_file(stave_name, stave_fpath) + stave_node = fxt.build_stave_node_from_file(stave_name, stave_fpath) # pretty-print the stave runkey to the trace log - logging.debug(f"stave runkey {stave_name}: {json.dumps(a_stave_runkey, indent=2)}") - - # TODO: the problem is that ConfigDB does not accept a runkey that you've just downloaded. - # This must be fixed on the ConfigDB side. - # In our standard basic example, we always translate the runkey into files - # before it is re-uploaded. - - # TODO: we also cannot dump the runkey into files and read them back - # because we work only with the standard SR1 files. - # We do not have more general routines now. - # TODO: Make the more general routines. - - #configDB = pyconfigdb.ConfigDB(dbKey=CONFIGDB_KEY, srUrl=configdb_url) - #logging.info(f"reading a runkey: {args.runkey_ref}") - #runkey_dict = configDB.tag_tree(args.runkey_ref, stage=False, payload_data=True) - ## TODO: check what's "stage=False" here? - - ## add it to the staves subtree - #runkey_root = runkey_dict['objects'][0] - ##logging.info(runkey_root) - #assert runkey_root['type'] == 'Root' - #assert any(tree['type'] == 'Staves' for tree in runkey_root['children']) - - ## translate the not-uploadable ConfigDB runkey into files - #temp_translate_dir = '.temp_translate/' - #yarr_connectivity = runkey_fiber_to_yarr_connectivity(get_subnode_of_type(runkey_root, 'Felix')) - #write_cfg_files(yarr_connectivity, temp_translate_dir) - # load the files back into an uploadble dictionary: - - # So, for now, let's just upload a blank template with a new stave added. - logging.info(f"Uploading a template runkey with an added new stave as: {args.runkey_ref}") - staves_node = { - "type": "Staves", - "payloads": [], - "children": [] - } - - staves_node['children'].append(a_stave_runkey) - comment = f"added stave {stave_name}" - - runkey_dict_template['children'].append(staves_node) - runkey_id = configDB.stage_create(data=runkey_dict_template, comment="upload an blank runkey with a new stave") - logging.info(f"Staged id: {runkey_id}") - ids = configDB.stage_commit(runkey_id, new_name=configDBname, comment=comment) - logging.info(f"Committed ids: {ids}") + logging.debug(f"stave runkey {stave_name}: {json.dumps(stave_node, indent=2)}") + + # + # TODO: `search` finds _payloads_ not the nodes??? + # we need nodes -- the functions `add_node` need IDs of parent nodes + #staves_tree_id = configDB.search(runkey_ref, object_type="Staves") + staves_subtree = configDB.search_subtree(args.runkey_ref, object_type="Staves") + logging.debug(f'Found Staves subtree: {staves_subtree}') + staves_tree_id = staves_subtree[0]['id'] + logging.info(f'Got Staves subtree ID: {staves_tree_id}') + assert len(staves_tree_id) > 0 + + # and add a new node under it + # ConfigDB does not provide an API to just upload a dictionary + # with payloads and children together. + # you have to create nodes one by one. + # Hence, our helper function: + def add_subtree(configdb, parent_id, subtree: dict, new_id_map={}, to_stage=False): + assert isinstance(subtree, dict), f'Not a dict: {subtree}' + + if 'reuse_id' in subtree: + # skip it + return + + if 'type' not in subtree: + raise RuntimeError(f'No "type" in {subtree}') + + # TODO: what to do with "reuse_id" nodes? + # TODO: or else, how to set node ID in add_node? + + # create the root node of this subtree + subtree_id = configdb.add_node(subtree['type'], + payloads=subtree['payloads'], + parents=[{'id': parent_id}], + children=[], + stage=to_stage) + + if 'children' not in subtree: + raise RuntimeError(f'No "children" in {subtree}') + + # create children nodes and save their IDs + children_ids = [] + for child_node in subtree['children']: + add_subtree(configdb, subtree_id, child_node, new_id_map) + + # update the children IDs in the root node + configdb.add_to_node(subtree_id, children=children_ids, stage=to_stage) + + # update the ID + current_id = subtree.get('id') + new_id_map[current_id] = subtree_id + subtree['id'] = subtree_id + + def update_references(configdb, parent_id, subtree: dict, new_id_map={}, to_stage=False): + if 'reuse_id' in subtree: + # find the new id of the reference + new_id = new_id_map[subtree['reuse_id']] + # add it to the node's children + configdb.add_to_node(parent_id, children=[new_id], stage=to_stage) + # and done + return + + uptodate_id = subtree['id'] + for child_node in subtree['children']: + update_references(configdb, uptodate_id, child_node, new_id_map, to_stage) + + id_mapping = {} + add_subtree(configDB, staves_tree_id, stave_node, id_mapping) + update_references(configDB, staves_tree_id, stave_node, id_mapping) + + logging.info(f'Uploaded a stave and updated references: {stave_name}') else: logging.info(" Not committing to configDB") diff --git a/scripts/test_upload_a_stave.py b/scripts/test_upload_a_stave.py index dea988362582e559fc351f3802c24a01d2f6cf39..5632fea6c3e57f1d5e1c4823ee8ea300fabdd495 100644 --- a/scripts/test_upload_a_stave.py +++ b/scripts/test_upload_a_stave.py @@ -4,7 +4,7 @@ import pyconfigdb import functions_configDB as fxt import logging import datetime -from configdb_stripsRunkeys import get_subnode_of_type +from configdb_stripsRunkeys import get_subnode_of_type, get_node_payloads_data logging.basicConfig(level=logging.INFO) @@ -26,7 +26,7 @@ stave_name = 'AnotherNewStave' # or use the stave serial number with open(full_stave_cfg_fname, 'r') as cfg_file: stave_cfg = json.load(cfg_file) -a_stave_runkey = fxt.build_stave_node_from_dict(stave_name, stave_cfg) +stave_node = fxt.build_stave_node_from_dict(stave_name, stave_cfg) # ConfigDB connection: _, hostname, port = argv @@ -41,61 +41,89 @@ configdb_url = f'http://{hostname}:{port}/api' CONFIGDB_KEY = "demi/default/itk-demo-configdb/api/url" configDB = pyconfigdb.ConfigDB(dbKey=CONFIGDB_KEY, srUrl=configdb_url) - -# TODO: here we try to download a runkey from ConfigDB, add something to it and upload. -# It does not work in ConfigDB currently. You cannot re-upload a just downloaded runkey. -# So, as a patch, we upload a template with a new stave added. -# This must be fixed. -#logging.info(f"reading a runkey: {runkey_ref}") -#runkey_dict = configDB.tag_tree(runkey_ref, stage=False, payload_data=True) -# -## add it to the staves subtree -#runkey_root = runkey_dict['objects'][0] -#assert runkey_root['type'] == 'Root', f"Didn't find Root node: {runkey_root['type']} among {len(runkey_dict['objects'])}\n{runkey_dict}" -#assert any(tree['type'] == 'Staves' for tree in runkey_root['children']) -#staves_tree = get_subnode_of_type(runkey_root, "Staves") -# -## check if a stave with the same name already exists -#if any(stv.get('name') == stave_name for stv in staves_tree['children']): -# # TODO: is stave name a "name" attribute or payload? -# # TODO: raise or substitute the previous stave node? -# # if you substitute the node - update all reference nodes? -# raise RuntimeError(f'Stave by name {stave_name} already exists in runkey {runkey_ref}') - -staves_tree = { - "type": "Staves", - "payloads": [], - "children": [] -} - -staves_tree['children'].append(a_stave_runkey) - -runkey_template = { - "type": "Root", - "children": [ - { - "type": "Felix", - "payloads": [ - {"type": "FID_DETECTOR_ID", "data": "0"} - ], - "children": [] - }, - - { - "type": "Geometry", - "payloads": [], - "children": [ ] - }, - - staves_tree - ] -} - -# upload the runkey back -logging.info(f"uploading a runkey with stave {stave_name}: {runkey_ref}") -comment = f"added stave {stave_name}" -runkey_id = configDB.stage_create(data=runkey_template, comment=comment) - -configDBname = "stripsRunkeyTestPython_" + datetime.datetime.now().strftime("%Y-%m-%d_%Hh%Mm%Ss") -ids = configDB.stage_commit(runkey_id, new_name=configDBname, comment=comment) -logging.info(f"uploaded a runkey with the new stave {stave_name}") +logging.info(f'ConfigDB is UP: {configDB.health()}') + +# TODO: `search` finds _payloads_ not the nodes??? +# we need nodes -- the functions `add_node` need IDs of parent nodes +#staves_tree_id = configDB.search(runkey_ref, object_type="Staves") +staves_subtree = configDB.search_subtree(runkey_ref, object_type="Staves") +logging.debug(f'Found Staves subtree: {staves_subtree}') +staves_tree_id = staves_subtree[0]['id'] +logging.info(f'Got Staves subtree ID: {staves_tree_id}') +assert len(staves_tree_id) > 0 + +# and add a new node under it +# ConfigDB does not provide an API to just upload a dictionary +# with payloads and children together. +# you have to create nodes one by one. +# Hence, our helper function: +def add_subtree(configdb, parent_id, subtree: dict, new_id_map={}, to_stage=False): + assert isinstance(subtree, dict), f'Not a dict: {subtree}' + + if 'reuse_id' in subtree: + # skip it + # the references are done with the special workaround: new_id_map + return + + if 'type' not in subtree: + raise RuntimeError(f'No "type" in {subtree}') + + # create the root node of this subtree + subtree_id = configdb.add_node(subtree['type'], + payloads=subtree['payloads'], + parents=[{'id': parent_id}], + children=[], + stage=to_stage) + + if 'children' not in subtree: + raise RuntimeError(f'No "children" in {subtree}') + + # create children nodes and save their IDs + children_ids = [] + for child_node in subtree['children']: + add_subtree(configdb, subtree_id, child_node, new_id_map) + + # update the children IDs in the root node + configdb.add_to_node(subtree_id, children=children_ids, stage=to_stage) + + # update the ID + current_id = subtree.get('id') + new_id_map[current_id] = subtree_id + subtree['id'] = subtree_id + +def update_references(configdb, parent_id, subtree: dict, new_id_map={}, to_stage=False): + if 'reuse_id' in subtree: + # find the new id of the reference + new_id = new_id_map[subtree['reuse_id']] + # add it to the node's children + configdb.add_to_node(parent_id, children=[new_id], stage=to_stage) + # and done + return + + uptodate_id = subtree['id'] + for child_node in subtree['children']: + update_references(configdb, uptodate_id, child_node, new_id_map, to_stage) + +id_mapping = {} +add_subtree(configDB, staves_tree_id, stave_node, id_mapping) +update_references(configDB, staves_tree_id, stave_node, id_mapping) + +# download the runkey and confirm the stave is there +tagTree = configDB.tag_tree(runkey_ref, stage=False, payload_data=True) +logging.info(f'Got full tag tree: {json.dumps(tagTree, indent=2)}') +assert tagTree['objects'][0]['type'] == 'Root' +assert any(ch['type'] == "Staves" for ch in tagTree['objects'][0]['children']) + +runkey_root = tagTree['objects'][0] +staves_subtree = get_subnode_of_type(runkey_root, "Staves") + +stave_names = [] +for ch in staves_subtree['children']: + assert ch['type'] == "Stave" + st_names = get_node_payloads_data(ch, 'staveName') + assert len(st_names) == 1 + stave_names.append(st_names[0]) + +logging.info(f'Found the following staves in the {runkey_ref} runkey: {stave_names}') +logging.info(f'And our stave {stave_name} is there: {stave_name in stave_names}') +assert stave_name in stave_names \ No newline at end of file