#!/usr/bin/env python # coding: utf-8 from pathlib import Path import yaml from collections import defaultdict #extract all node types and generate basic yaml config part for nodes def write_automated_schema(graph, file_path, manual_schema_path): schema_data = { 'nodes': {}, 'edges': {} } if Path(file_path).exists(): print("-- Using existing schema --") schema_data = load_manual_schema(file_path) elif isinstance(manual_schema_path, list): print("-- Using two schema files --") schema_data = schema_merger(manual_schema_path[0], manual_schema_path[1]) elif manual_schema_path: print("-- Using the manual schema --") schema_data = load_manual_schema(manual_schema_path) if graph is not None: for node in graph.nodes(): label = graph.nodes[node].get('label') if label == 'resource': label = graph.nodes[node].get('resourceType') label = label.capitalize() if label not in schema_data['nodes']: schema_data['nodes'][label] = {} if 'properties' not in schema_data['nodes'][label] or schema_data['nodes'][label]['properties'] is None: schema_data['nodes'][label]['properties'] = {} for k in graph.nodes[node].keys(): schema_data['nodes'][label]['properties'][k] = 'str' #schema_data['nodes'][label]['properties'].update(graph.nodes[node].keys()) file=open(file_path, 'w') for n in schema_data['nodes']: temp = n+':\n' if 'is_a' in schema_data['nodes'][n]: is_a_value = schema_data['nodes'][n]['is_a'] temp += ' is_a: ' + (', '.join(is_a_value) if isinstance(is_a_value, list) else str(is_a_value)) + '\n' else: temp += ' is_a: named thing\n' if 'represented_as' in schema_data['nodes'][n]: represented_as_value = schema_data['nodes'][n]['represented_as'] temp += ' represented_as: ' + (', '.join(represented_as_value) if isinstance(represented_as_value, list) else str(represented_as_value)) + '\n' else: temp += ' represented_as: node\n' if 'label_in_input' in schema_data['nodes'][n]: label_in_input_value = schema_data['nodes'][n]['label_in_input'] temp += ' label_in_input: ' + (', '.join(label_in_input_value) if isinstance(label_in_input_value, list) else str(label_in_input_value)) + '\n' if 'preferred_id' in schema_data['nodes'][n]: preferred_id_value = schema_data['nodes'][n]['preferred_id'] temp += ' preferred_id: ' + (', '.join(preferred_id_value) if isinstance(preferred_id_value, list) else str(preferred_id_value)) + '\n' else: temp += ' preferred_id: fhir_id\n' temp += ' label_in_input: ' + n + '\n' temp += ' properties:\n' # get property values from schema_data if exists #print("---------->", str(schema_data['nodes'][n])) if schema_data['nodes'][n]['properties'] is not None: for p_key in schema_data['nodes'][n]['properties']: prop_value = schema_data['nodes'][n]['properties'][p_key] temp += ' ' + p_key + ': ' + (', '.join(prop_value) if isinstance(prop_value, list) else str(prop_value)) + '\n' #elif schema_data['nodes']['properties']: #print("----> ", schema_data['nodes']['properties']) """ else: for attr in schema_data['nodes'][n]: temp += ' ' + attr + ': str\n' """ temp += '\n' file.write(temp) file.write('\n') #extract all relationship types and generate basic yaml config part for relationships #if not edgeTypes: edgeTypes = set() if graph is not None: for u, v, a in graph.edges(data=True): source_label = graph.nodes[u].get('label') target_label = graph.nodes[v].get('label') if source_label == 'resource': source_label = graph.nodes[u].get('resourceType', str(u)) elif source_label == 'dummy' or source_label == 'Dummy': source_label = graph.nodes[u].get('edge_label', str(u)) if target_label == 'resource': target_label = graph.nodes[v].get('resourceType', str(v)) elif target_label == 'dummy' or target_label == 'Dummy': target_label = graph.nodes[v].get('edge_label', str(v)) source_label = source_label.capitalize() target_label = target_label.capitalize() CONST_ASSOCIATION = ' association' if source_label + ' to ' + target_label + CONST_ASSOCIATION in schema_data['edges']: # add missing attributes continue elif source_label + ' derived from ' + target_label + CONST_ASSOCIATION in schema_data['edges']: continue elif source_label + ' has member ' + target_label + CONST_ASSOCIATION in schema_data['edges']: continue elif source_label + ' reasoned by ' + target_label + CONST_ASSOCIATION in schema_data['edges']: continue elif source_label + ' is ' + target_label + CONST_ASSOCIATION in schema_data['edges']: continue else: schema_data['edges'][source_label + ' to ' + target_label + ' association'] = { 'is_a': 'association', 'represented_as': 'edge', 'label_in_input': source_label + '_to_' + target_label, 'properties': a } for label in schema_data['edges']: temp = '' + label + ':\n' for key in schema_data['edges'][label]: if key == 'properties': if schema_data['edges'][label][key] is not None: temp += ' properties:\n' for prop in schema_data['edges'][label][key]: prop_value = schema_data['edges'][label][key][prop] temp += ' ' + prop + ': ' + (', '.join(prop_value) if isinstance(prop_value, list) else str(prop_value)) + '\n' else: field_value = schema_data['edges'][label][key] temp += ' ' + key + ': ' + (', '.join(field_value) if isinstance(field_value, list) else str(field_value)) + '\n' temp += '\n' file.write(temp) file.close() def load_manual_schema(path): schema_data = { 'nodes': {}, 'edges': {} } edgeTypes = set() with open(path, 'r') as file: # Load YAML with comments stripped data = yaml.safe_load(file) for label, attrs in data.items(): cLabel = label#.capitalize() # less n, e and p with capitalize if label != 'Title': if attrs["represented_as"] == 'node': if not hasattr(schema_data['nodes'], cLabel): schema_data['nodes'][cLabel] = set() #assuming uniqueness in schema file here. If the same node type exits twice, it will be overwritten. schema_data['nodes'][cLabel] = attrs #for a in attrs: #print(v) """ for k, v in attrs: if not k == '' schema_data['nodes'][label][k] = v """ else: if not hasattr(schema_data['edges'], cLabel): schema_data['edges'][cLabel] = set() #assuming uniqueness in schema file here. If the same node type exits twice, it will be overwritten. schema_data['edges'][cLabel] = attrs return schema_data def schema_merger(schema_path1, schema_path2): schema1 = load_manual_schema(schema_path1) schema2 = load_manual_schema(schema_path2) merged = { 'nodes': {**schema1['nodes'], **schema2['nodes']}, 'edges': {**schema1['edges'], **schema2['edges']} } return merged def schema_diff_report(manual_path, auto_path): #manual_path = "config/manual_schema_config.yaml" #auto_path = "config/automated_schema.yaml" man_schema = load_manual_schema(manual_path) automated_schema = load_manual_schema(auto_path) print("\nNODES:\n") for label, attributes in automated_schema['nodes'].items(): if 'nodes' in man_schema and label in man_schema['nodes']: # node already exists print("Found in man schema: ", label) for prop, val in attributes.items(): if prop in man_schema['nodes'][label]: # property exists if isinstance(val, dict): for attr, attr_val in val.items(): if attr not in man_schema['nodes'][label][prop]: print("Added the property " + attr + " to " + prop + " of " + label) elif attr_val != man_schema['nodes'][label][prop][attr]: print("Updated property value of " + label + "." + prop + "." + attr + "to: " + attr_val) elif val != man_schema['nodes'][label][prop]: # property value is not the same print("Updated property value of " + label + "." + prop + " to: " + val) else: print(label + "." + prop + " has not changed") else: print(label + ": added property " + prop + ". Value: " + val) else: # node not found in manual schema print('Resource type added: ' + label) print("\nEDGES:\n") for label, attributes in automated_schema['edges'].items(): if 'edges' in man_schema and label in man_schema['edges']: # node already exists print("Found in man schema: ", label) for prop, val in attributes.items(): if prop in man_schema['edges'][label]: # property exists if isinstance(val, dict): for attr, attr_val in val.items(): if attr not in man_schema['edges'][label][prop]: print("Added the property " + attr + " to " + prop) elif attr_val != man_schema['edges'][label][prop][attr]: print("Updated property value of " + label + "." + prop + "." + attr + "to: " + attr_val) elif val != man_schema['edges'][label][prop]: # property value is not the same print("Updated property value of " + label + "." + prop + " to: " + val) else: print(label + "." + prop + " has not changed") else: print(label + ": added property " + prop + ". Value: " + val) else: # node not found in manual schema print('Resource type added: ' + label)