public OpenEhrToFhirAdapterBase ProcessMessage(string message) { // get all classes which inherit OpenEhrToFhirAdapterBase // (this gets all adapters and passes the message into the adapter constructor which automatically processes the message using that adapter) var adapters = ReflectionHelper.GetEnumerableOfType <OpenEhrToFhirAdapterBase>(message); // set default highest confidence var highestConfidence = 0.0; // set default chosenOpenEhrToFhirAdapter OpenEhrToFhirAdapterBase chosenOpenEhrToFhirAdapter = null; // loop through all adapters foreach (var adapter in adapters) { // check if the confidence score of this adapter is higher than the current if (adapter.ConfidenceScore > highestConfidence || chosenOpenEhrToFhirAdapter == null) { // set the current highest confidence score highestConfidence = adapter.ConfidenceScore; // set the current adapter chosenOpenEhrToFhirAdapter = adapter; } } // log some info //Log.Info($"Received message and selected adapter: {chosenOpenEhrToFhirAdapter?.AdapterName} with confidence of {chosenOpenEhrToFhirAdapter?.ConfidenceScore}%.\n"); // return the chosen adapter return(chosenOpenEhrToFhirAdapter); }
public override OpenEhrToFhirAdapterBase ProcessMappings(OpenEhrToFhirAdapterBase adapter) { foreach (var mapping in adapter.Mappings.Where(x => x.MappingType == MappingType.Resource)) { // calculate number of resources of this type are required var number = adapter.OpenEhrRecord.FindMany(mapping, new List <Component>()).Count; for (var i = 0; i < number; i++) { var resource = (Resource)Activator.CreateInstance("Hl7.Fhir.STU3.Core", "Hl7.Fhir.Model." + ((ResourceMapping)mapping).ResourceTargetType).Unwrap(); adapter.ResourceList.Add(resource); //Log.Info($"Added resource of type {resource.GetType().Name} to ResourceList[{adapter.ResourceList.Count - 1}]."); } } return(adapter); }
public override OpenEhrToFhirAdapterBase ProcessMappings(OpenEhrToFhirAdapterBase adapter) { foreach (var mapping in adapter.Mappings.Where(x => x.MappingType == MappingType.Extension)) { var nodes = adapter.OpenEhrRecord.FindMany(mapping, new List <Component>()); // 2.2 Work out destination of this mapping item foreach (var node in nodes) { // check if the current node has been processed already if (adapter.ProcessedNodes.Contains(node)) { continue; } // get the resource that this node should be mapped to var rootResourceIndex = node.ParentResourceIndex(adapter.Mappings); // if this node has no parent resource index then we skip it if (rootResourceIndex == int.MinValue) { continue; } // Get parent object of attribute var resourceRoot = adapter.ResourceList[rootResourceIndex]; // gets the reference to a fhir field and also returns the index of the field if the field is a list item // if val is not null here then it means the field has been instantiated by a list node mapping operation and must be part of a list // if val is null then it means the field has not been instantiated previously. var oldValue = GetFhirObject(resourceRoot, adapter, node, out _); // constructs the value to place at the target field location var newValue = ConstructNewTargetValue(adapter, node, mapping, oldValue); // updates the target field in the fhir resource and returns the updated adapter adapter = SetFhirValue(resourceRoot, adapter, node, newValue, oldValue); } } return(adapter); }
public override OpenEhrToFhirAdapterBase ProcessMappings(OpenEhrToFhirAdapterBase adapter) { foreach (var mapping in adapter.Mappings.Where(x => x.MappingType == MappingType.Conditional)) { var nodes = adapter.OpenEhrRecord.FindMany(mapping, new List <Component>()); // 2.2 Work out destination of this mapping item foreach (var node in nodes) { // check if the current node has been processed already if (adapter.ProcessedNodes.Contains(node)) { continue; } // get the resource that this node should be mapped to var rootResourceIndex = node.ParentResourceIndex(adapter.Mappings); // if this node has no parent resource index then we skip it if (rootResourceIndex == int.MinValue) { continue; } // Get parent object of attribute var resourceRoot = adapter.ResourceList[rootResourceIndex]; var oldValue = GetFhirObject(resourceRoot, adapter, node, out var index); // constructs the value to place at the target field location var newValue = ConstructNewTargetValue(adapter, node, mapping, oldValue); adapter = SetFhirValue(resourceRoot, adapter, node, newValue, oldValue, index); } } return(adapter); }
/// <summary> /// gets the reference to a fhir field and also returns the index of the field if the field is a list item /// if oldValue is not null here then it means the field has been instantiated by a list node mapping operation and must be part of a list /// if oldValue is null then it means the field has not been instantiated previously. /// </summary> /// <param name="resourceRoot">The root resource to be searched i.e. AllergyIntolerance, Patient etc.</param> /// <param name="adapter">The adapter currently being used.</param> /// <param name="node">The current node being used in the process mapping method.</param> /// <param name="index">If the value of the field is inside a list then it's index in the list will be returned.</param> /// <returns>Returns the object found at the search location, also returns the index of the field if the field is a list item.</returns> public object GetFhirObject(Resource resourceRoot, OpenEhrToFhirAdapterBase adapter, Component node, out int index) { // get the path inside the resource where this property should be set var finalRoute = node.InnerNetPath(adapter.Mappings); if (string.IsNullOrEmpty(finalRoute)) { finalRoute = node.NetPath(adapter.Mappings); } // get the index of the parent list to put the property inside var start = finalRoute.LastIndexOf('[') + 1; var end = finalRoute.LastIndexOf(']'); index = int.MinValue; if (start != -1 && end != -1) { var indexStr = finalRoute.Substring(start, end - start); if (!int.TryParse(indexStr, out index)) { throw new Exception("Failed to parse index."); } } if (finalRoute.Split('.').Last().EndsWith("]")) { finalRoute = finalRoute.Remove(finalRoute.LastIndexOf('[')); } var value = resourceRoot.GetPropertyValue(finalRoute); if (value == null) { // recursively check through all properties for one matching the end node of the mapping } return(value); }
public override OpenEhrToFhirAdapterBase ProcessMappings(OpenEhrToFhirAdapterBase adapter) { foreach (var mapping in adapter.Mappings.Where(x => x.MappingType == MappingType.List)) { var listNodes = adapter.OpenEhrRecord.FindMany(mapping, new List <Component>()); foreach (var node in listNodes) { var netPath = node.NetPath(adapter.Mappings); var innerNetPath = node.InnerNetPath(adapter.Mappings); var pi = adapter.GetPropertyInfo(netPath); var componentType = pi.PropertyType.GetGenericArguments()[0]; var constructedListType = typeof(List <>).MakeGenericType(componentType); var listInstance = (IList)Activator.CreateInstance(constructedListType); var subNodes = ((Composite)node.Parent).FindMany(mapping, new List <Component>()).ToList(); var counter = 0; while (counter < subNodes.Count) { var subNode = subNodes[counter]; if (Regex.Match(subNode.Name, ":\\d+\\|+").Success) { var regex = $"{subNode.Name.Split(':')[0]}:{subNode.ListIndex}+\\|+"; var siblings = subNodes.Where(x => Regex.Match(x.Name, regex).Success && x.ParentResourceIndex(adapter.Mappings) == subNode.ParentResourceIndex(adapter.Mappings)).ToList(); var passes = siblings.DistinctBy(x => x.Parent.NetPath(adapter.Mappings)).Count(); for (var i = 0; i < passes; i++) { var system = (Composite)siblings.Where(x => x.Name.Contains("terminology")).ToList()[i]; var code = (Composite)siblings.Where(x => x.Name.Contains("code")).ToList()[i]; var text = (Composite)siblings.Where(x => x.Name.Contains("value")).ToList()[i]; if (system != null) { counter++; } if (code != null) { counter++; } if (text != null) { counter++; } listInstance.Add(Activator.CreateInstance(componentType, null)); } } else { counter++; listInstance.Add(Activator.CreateInstance(componentType, null)); } } var obj = adapter.GetPropertyValue(netPath); if (!ReflectionHelper.SetValue(obj, innerNetPath, listInstance)) { var innerPi = adapter.GetPropertyInfo(netPath); obj = adapter.GetPropertyValue(netPath.Remove(netPath.LastIndexOf('.'))); try { innerPi.SetValue(obj, listInstance); } catch { //Log.Error($"Failed to map list {mapping.OpenEhrFieldPath} to {mapping.FhirFieldPath}."); } } } } return(adapter); }
/// <summary> /// Constructs the object which will be placed at the target FHIR location according the mapping rules and openEHR message values. /// </summary> /// <param name="adapter">The current adapter being processed.</param> /// <param name="node">The current node being processed.</param> /// <returns>Returns the constructed value to be placed at the target FHIR location.</returns> public object ConstructNewTargetValue(OpenEhrToFhirAdapterBase adapter, Component node, BaseMapping mapping, object oldValue = null) { object newTargetValue = null; // fhir resource destination var destination = node.NetPath(adapter.Mappings); // list + CodeableConcept combined logic i.e. substance:0|code or substance|code if (Regex.Match(node.Name, ":\\d+\\|+").Success || Regex.Match(node.Name, $"{mapping.OpenEhrFieldPath}\\|+").Success || Regex.Match(node.Name, $"{mapping.OpenEhrFieldPath}:\\d+").Success) { // Get all components of this codeable concept var siblingNodes = ((Composite)node.Parent).FindMany(mapping, new List <Component>()).Where(x => x.NetPath(adapter.Mappings) == destination).ToList(); var system = ((Composite)siblingNodes.FirstOrDefault(x => x.Name.Contains("terminology")))?.Children[0].Name; var code = ((Composite)siblingNodes.FirstOrDefault(x => x.Name.Contains("code")))?.Children[0].Name; var text = ((Composite)siblingNodes.FirstOrDefault(x => x.Name.Contains("value")))?.Children[0].Name; if (system == null && code == null && text == null) { newTargetValue = StringHelper.ParseOpenEhrCcString(((Composite)node).Children.First().Name); } else { newTargetValue = new CodeableConcept(system, code, text); } if (GetType() == typeof(ConditionalMapping)) { // the type of codeable concept type to search for i.e. Terminology/Code/Value var conditionalSearchType = ((ConditionalMapping)mapping).Conditions.First().Item1; // the actual value to search for in order to match the conditional mapping var conditionalSearchValue = string.Empty; switch (conditionalSearchType) { case "code": { conditionalSearchValue = code; break; } case "terminology": { conditionalSearchValue = system; break; } case "value": { conditionalSearchValue = text; break; } } var metConditions = ((ConditionalMapping)mapping).Conditions.Where(x => string.Equals(x.Item2, conditionalSearchValue, StringComparison.InvariantCultureIgnoreCase)) .ToList(); var destinationIsCollection = oldValue is IEnumerable; if (destinationIsCollection) { if (((IList)oldValue).Count == 0) { var listGenericType = oldValue.GetType().GenericTypeArguments[0]; var codeGenericType = metConditions[0].Item3.GetType(); var list = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(listGenericType), null); foreach (var metCondition in metConditions) { var obj = Activator.CreateInstance(typeof(Code <>).MakeGenericType(codeGenericType), metCondition.Item3); list.Add(obj); } newTargetValue = list; } } else { newTargetValue = metConditions[0].Item3; } } else if (GetType() == typeof(ExtensionMapping)) { if (oldValue != null) { ((List <Extension>)oldValue).Add(new Extension( ((ExtensionMapping)mapping).ExtensionFhirStructureDefinitionUrl, new CodeableConcept(system, code, text))); newTargetValue = oldValue; } } // mark these nodes as processed so skip duplicate processing adapter.ProcessedNodes.AddRange(siblingNodes); } else { var siblingNodes = ((Composite)node.Parent).FindMany(mapping, new List <Component>()).Where(x => x.NetPath(adapter.Mappings) == destination).ToList(); if (GetType() == typeof(ExtensionMapping)) { var childLeafValue = ((Composite)node).Children[0] as Composite; ((List <Extension>)oldValue).Add(new Extension(((ExtensionMapping)mapping).ExtensionFhirStructureDefinitionUrl, new FhirString(childLeafValue?.Children[0].Name))); newTargetValue = oldValue; } else { if (siblingNodes.Count == 1) { newTargetValue = StringHelper.ParseOpenEhrCcString(((Composite)siblingNodes[0]).Children[0].Name); } else { var index = node.ParentResourceIndex(adapter.Mappings); var t_node = (Composite)siblingNodes.Where(x => x.NetPath(adapter.Mappings) == destination).ToList()[index]; newTargetValue = StringHelper.ParseOpenEhrCcString(t_node.Children[0].Name); } } adapter.ProcessedNodes.AddRange(siblingNodes); } return(newTargetValue); }
/// <summary> /// Execute the processing of this mapping type. /// </summary> /// <param name="adapter">The adapter adapter which contains the mappings you want to execute.</param> /// <returns>An updated version of the adapter containing the results of the executed mappings.</returns> public abstract OpenEhrToFhirAdapterBase ProcessMappings(OpenEhrToFhirAdapterBase adapter);
/// <summary> /// /// </summary> /// <param name="resourceRoot"></param> /// <param name="adapter"></param> /// <param name="node"></param> /// <param name="newValue"></param> /// <param name="oldValue"></param> /// <param name="index"></param> /// <returns></returns> public OpenEhrToFhirAdapterBase SetFhirValue(Resource resourceRoot, OpenEhrToFhirAdapterBase adapter, Component node, object newValue, object oldValue = null, int index = int.MinValue) { var points = 1; // setting a not null property which is inside a list if (oldValue != null && index != int.MinValue) { var setListItemMethod = oldValue.GetType().GetMethod("set_Item"); if (setListItemMethod != null) { setListItemMethod.Invoke(oldValue, new[] { index, newValue }); adapter.SuccessfulMappingCount += points; //Log.Info($"Mapped {GetType().Name} from {OpenEhrFieldPath} to {node.InnerNetPath(adapter.Mappings)} & yielded +{points} for total of {adapter.SuccessfulMappingCount} successes for this adapter."); } } // setting a null property which is not in a list else { var childPathFromParent = node.InnerNetPath(adapter.Mappings); object parentObject; if (childPathFromParent.LastIndexOf('.') < 0) { parentObject = resourceRoot; } else { var parentObjectPath = childPathFromParent.Remove(childPathFromParent.LastIndexOf('.')); parentObject = resourceRoot.GetPropertyValue(parentObjectPath); } // get the property name that we want to set var childProperty = childPathFromParent.Split('.').Last(); // set codeable concept in parent object which isn't at the root resource i.e. Resource.Property[0].AnotherProperty[1] if (ReflectionHelper.SetValue(parentObject, childProperty, newValue)) { adapter.SuccessfulMappingCount += points; } // set primitive data type in parent object which isn't at the root resource i.e. Resource.Property[0].AnotherProperty else if (ReflectionHelper.SetValue(parentObject, childProperty, ((Composite)node).Children[0].Name)) { adapter.SuccessfulMappingCount += points; } // set primitive data type in parent object which is at the root resource i.e. Resource.Property else { if (ReflectionHelper.SetValue(resourceRoot, childProperty, ((Composite)node).Children[0].Name)) { adapter.SuccessfulMappingCount += points; } else { //Log.Error($"Failed to map attribute with mapping {OpenEhrFieldPath} to {FhirFieldPath}."); } } //Log.Info($"Mapped {GetType().Name} from {OpenEhrFieldPath} to {childPathFromParent} & yielded +{points} for total of {adapter.SuccessfulMappingCount} successes for this adapter."); } return(adapter); }