One more test passing. rework EID updates to support the terser method

This commit is contained in:
Tadgh 2020-11-06 14:54:37 -05:00
parent 79793f9c40
commit 063d04cc79
4 changed files with 119 additions and 61 deletions

View File

@ -66,6 +66,7 @@ public class EmpiEidUpdateService {
EmpiUpdateContext updateContext = new EmpiUpdateContext(theMatchedSourceResourceCandidate, theResource);
if (updateContext.isRemainsMatchedToSamePerson()) {
// Copy over any new external EIDs which don't already exist.
// TODO NG - Eventually this call will use terser to clone data in, once the surviorship rules for copying data will be confirmed
// myPersonHelper.updatePersonFromUpdatedEmpiTarget(updateContext.getMatchedPerson(), theResource, theEmpiTransactionContext);
if (!updateContext.isIncomingResourceHasAnEid() || updateContext.isHasEidsInCommon()) {

View File

@ -131,21 +131,21 @@ public class EmpiMatchLinkSvc {
myEmpiLinkSvc.updateLink(newPerson, theResource, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
}
private void handleEmpiCreate(IAnyResource theResource, MatchedSourceResourceCandidate thePersonCandidate, EmpiTransactionContext theEmpiTransactionContext) {
private void handleEmpiCreate(IAnyResource theTargetResource, MatchedSourceResourceCandidate thePersonCandidate, EmpiTransactionContext theEmpiTransactionContext) {
log(theEmpiTransactionContext, "EMPI has narrowed down to one candidate for matching.");
IAnyResource person = myEmpiSourceResourceFindingSvc.getSourceResourceFromMatchedSourceResourceCandidate(thePersonCandidate, theEmpiTransactionContext.getResourceType());
if (myPersonHelper.isPotentialDuplicate(person, theResource)) {
IAnyResource sourceResource = myEmpiSourceResourceFindingSvc.getSourceResourceFromMatchedSourceResourceCandidate(thePersonCandidate, theEmpiTransactionContext.getResourceType());
if (myPersonHelper.isPotentialDuplicate(sourceResource, theTargetResource)) {
log(theEmpiTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs.");
IAnyResource newPerson = myPersonHelper.createSourceResourceFromEmpiTarget(theResource);
myEmpiLinkSvc.updateLink(newPerson, theResource, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
myEmpiLinkSvc.updateLink(newPerson, person, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
IAnyResource newSourceResource = myPersonHelper.createSourceResourceFromEmpiTarget(theTargetResource);
myEmpiLinkSvc.updateLink(newSourceResource, theTargetResource, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
myEmpiLinkSvc.updateLink(newSourceResource, sourceResource, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
} else {
if (thePersonCandidate.isMatch()) {
myPersonHelper.handleExternalEidAddition(person, theResource, theEmpiTransactionContext);
//
myPersonHelper.handleExternalEidAddition(sourceResource, theTargetResource, theEmpiTransactionContext);
// myPersonHelper.updatePersonFromNewlyCreatedEmpiTarget(person, theResource, theEmpiTransactionContext);
}
myEmpiLinkSvc.updateLink(person, theResource, thePersonCandidate.getMatchResult(), EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
myEmpiLinkSvc.updateLink(sourceResource, theTargetResource, thePersonCandidate.getMatchResult(), EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
}
}

View File

@ -28,6 +28,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Identifier;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class CanonicalEID {
@ -136,4 +137,15 @@ public class CanonicalEID {
.map(ibase -> new CanonicalEID(fhirPath, ibase))
.collect(Collectors.toList());
}
@Override
public boolean equals(Object o) {
if (!(o instanceof CanonicalEID)) {
return false;
}
CanonicalEID otherEid = (CanonicalEID)o;
return Objects.equals(otherEid.getSystem(), this.getSystem())
&& Objects.equals(otherEid.getValue(), this.getValue())
&& Objects.equals(otherEid.getUse(), this.getUse());
}
}

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.CanonicalEID;
import ca.uhn.fhir.empi.model.CanonicalIdentityAssuranceLevel;
import ca.uhn.fhir.empi.model.EmpiTransactionContext;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.util.FhirTerser;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -40,6 +41,7 @@ import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Address;
import org.hl7.fhir.r4.model.ContactPoint;
import org.hl7.fhir.r4.model.HumanName;
@ -223,53 +225,27 @@ public class PersonHelper {
* a randomly generated UUID EID will be created.
*
* @param <T> Supported MDM resource type (e.g. Patient, Practitioner)
* @param theSourceResource The resource that will be used as the starting point for the MDM linking.
* @param theIncomingResource The resource that will be used as the starting point for the MDM linking.
*/
public <T extends IAnyResource> T createSourceResourceFromEmpiTarget(T theSourceResource) {
ensureContextSupported();
public <T extends IAnyResource> T createSourceResourceFromEmpiTarget(T theIncomingResource) {
validateContextSupported();
// get a ref to the actual ID Field
RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theSourceResource);
RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theIncomingResource);
IBaseResource newSourceResource = resourceDefinition.newInstance();
// hapi has 2 metamodels: for children and types
BaseRuntimeChildDefinition sourceResourceIdentifier = resourceDefinition.getChildByName("identifier");
// FHIR choice types - fields within fhir where we have a choice of ids
BaseRuntimeElementCompositeDefinition<?> childIdentifier =
(BaseRuntimeElementCompositeDefinition<?>) sourceResourceIdentifier.getChildByName("identifier");
cloneAllExternalEidsIntoNewSourceResource(sourceResourceIdentifier, theIncomingResource, newSourceResource);
FhirTerser terser = myFhirContext.newTerser();
List<IBase> sourceResourceEids = sourceResourceIdentifier.getAccessor().getValues(theSourceResource);
for (IBase base : sourceResourceEids) {
IBase sourceResourceNewIdentifier = childIdentifier.newInstance();
terser.cloneInto(base, sourceResourceNewIdentifier, true);
addHapiEidIfNoExternalEidIsPresent(newSourceResource, sourceResourceIdentifier);
sourceResourceIdentifier.getMutator().addValue(newSourceResource, sourceResourceNewIdentifier);
}
List<CanonicalEID> eidsToApply = myEIDHelper.getExternalEid(theSourceResource);
if (eidsToApply.isEmpty()) {
eidsToApply.add(myEIDHelper.createHapiEid());
}
for (CanonicalEID eid : eidsToApply) {
switch (myFhirContext.getVersion().getVersion()) {
case R4:
sourceResourceIdentifier.getMutator().addValue(newSourceResource, eid.toR4());
break;
case DSTU3:
sourceResourceIdentifier.getMutator().addValue(newSourceResource, eid.toDSTU3());
break;
}
}
IBaseCoding tag = newSourceResource.getMeta().addTag();
tag.setSystem(EmpiConstants.SYSTEM_EMPI_MANAGED);
tag.setCode(EmpiConstants.CODE_HAPI_EMPI_MANAGED);
tag.setDisplay(EmpiConstants.DISPLAY_HAPI_EMPI_MANAGED);
populateMetaTag(newSourceResource);
return (T) newSourceResource;
// switch (myFhirContext.getVersion().getVersion()) {
// case R4:
// Person personR4 = new Person();
@ -293,7 +269,65 @@ public class PersonHelper {
// }
}
private void ensureContextSupported() {
/**
* If there are no external EIDs on the incoming resource, create a new HAPI EID on the new SourceResource.
*/
//TODO GGG ask james if there is any way we can convert this canonical EID into a generic STU-agnostic IBase.
private <T extends IAnyResource> void addHapiEidIfNoExternalEidIsPresent(IBaseResource theNewSourceResource, BaseRuntimeChildDefinition theSourceResourceIdentifier) {
List<CanonicalEID> eidsToApply = myEIDHelper.getExternalEid(theNewSourceResource);
if (eidsToApply.isEmpty()) {
CanonicalEID hapiEid = myEIDHelper.createHapiEid();
switch (myFhirContext.getVersion().getVersion()) {
case R4:
theSourceResourceIdentifier.getMutator().addValue(theNewSourceResource, hapiEid.toR4());
break;
case DSTU3:
theSourceResourceIdentifier.getMutator().addValue(theNewSourceResource, hapiEid.toDSTU3());
break;
}
}
}
/**
* Given an Child Definition of `identifier`, a R4/DSTU3 EID Identifier, and a new resource, clone the EID into that resources' identifier list.
*/
private void cloneExternalEidIntoNewSourceResource(BaseRuntimeChildDefinition sourceResourceIdentifier, IBase theEid, IBaseResource newSourceResource){
// FHIR choice types - fields within fhir where we have a choice of ids
BaseRuntimeElementCompositeDefinition<?> childIdentifier = (BaseRuntimeElementCompositeDefinition<?>) sourceResourceIdentifier.getChildByName("identifier");
FhirTerser terser = myFhirContext.newTerser();
IBase sourceResourceNewIdentifier = childIdentifier.newInstance();
terser.cloneInto(theEid, sourceResourceNewIdentifier, true);
sourceResourceIdentifier.getMutator().addValue(newSourceResource, sourceResourceNewIdentifier);
}
private void cloneAllExternalEidsIntoNewSourceResource(BaseRuntimeChildDefinition sourceResourceIdentifier, IBase theSourceResource, IBase newSourceResource){
// FHIR choice types - fields within fhir where we have a choice of ids
BaseRuntimeElementCompositeDefinition<?> childIdentifier = (BaseRuntimeElementCompositeDefinition<?>) sourceResourceIdentifier.getChildByName("identifier");
FhirTerser terser = myFhirContext.newTerser();
IFhirPath fhirPath = myFhirContext.newFhirPath();
List<IBase> sourceResourceIdentifiers = sourceResourceIdentifier.getAccessor().getValues(theSourceResource);
for (IBase base : sourceResourceIdentifiers) {
Optional<IPrimitiveType> system = fhirPath.evaluateFirst(base, "system", IPrimitiveType.class);
if (system.isPresent()) {
if (system.get().equals(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())) {
IBase sourceResourceNewIdentifier = childIdentifier.newInstance();
terser.cloneInto(base, sourceResourceNewIdentifier, true);
sourceResourceIdentifier.getMutator().addValue(newSourceResource, sourceResourceNewIdentifier);
}
}
}
}
private void populateMetaTag(IBaseResource theResource) {
IBaseCoding tag = theResource.getMeta().addTag();
tag.setSystem(EmpiConstants.SYSTEM_EMPI_MANAGED);
tag.setCode(EmpiConstants.CODE_HAPI_EMPI_MANAGED);
tag.setDisplay(EmpiConstants.DISPLAY_HAPI_EMPI_MANAGED);
}
private void validateContextSupported() {
FhirVersionEnum fhirVersion = myFhirContext.getVersion().getVersion();
if (fhirVersion == R4 || fhirVersion == DSTU3) {
return;
@ -442,7 +476,7 @@ public class PersonHelper {
if (!incomingTargetEid.isEmpty()) {
if (personOfficialEid.isEmpty() || !myEmpiConfig.isPreventMultipleEids()) {
log(theEmpiTransactionContext, "Incoming resource:" + theTargetResource.getIdElement().toUnqualifiedVersionless() + " + with EID " + incomingTargetEid.stream().map(CanonicalEID::toString).collect(Collectors.joining(",")) + " is applying this EIDs to its related Person, as this person does not yet have an external EID");
log(theEmpiTransactionContext, "Incoming resource:" + theTargetResource.getIdElement().toUnqualifiedVersionless() + " + with EID " + incomingTargetEid.stream().map(CanonicalEID::toString).collect(Collectors.joining(",")) + " is applying this EIDs to its related Source Resource, as this Source Resource does not yet have an external EID");
addCanonicalEidsToSourceResourceIfAbsent(theSourceResource, incomingTargetEid);
} else if (!personOfficialEid.isEmpty() && myEIDHelper.eidMatchExists(personOfficialEid, incomingTargetEid)) {
log(theEmpiTransactionContext, "incoming resource:" + theTargetResource.getIdElement().toVersionless() + " with EIDs " + incomingTargetEid.stream().map(CanonicalEID::toString).collect(Collectors.joining(",")) + " does not need to overwrite person, as this EID is already present");
@ -474,22 +508,42 @@ public class PersonHelper {
}
}
private void addCanonicalEidsToSourceResourceIfAbsent(IBaseResource theSourceResource, List<CanonicalEID> theIncomingTargetEid) {
for (CanonicalEID eid : theIncomingTargetEid) {
List<CanonicalEID> sourceResourceEids = myEIDHelper.getExternalEid(theSourceResource);
/**
* Given a list of incoming External EIDs, and a Source Resource, apply all the EIDs to this resource, which did not already exist on it.
*/
private void addCanonicalEidsToSourceResourceIfAbsent(IBaseResource theSourceResource, List<CanonicalEID> theIncomingTargetExternalEids) {
List<CanonicalEID> sourceResourceExternalEids = myEIDHelper.getExternalEid(theSourceResource);
}
switch (myFhirContext.getVersion().getVersion()) {
for (CanonicalEID incomingExternalEid : theIncomingTargetExternalEids) {
if (sourceResourceExternalEids.contains(incomingExternalEid)) {
continue;
} else {
// get a ref to the actual ID Field
RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theSourceResource);
// hapi has 2 metamodels: for children and types
BaseRuntimeChildDefinition sourceResourceIdentifier = resourceDefinition.getChildByName("identifier");
switch(myFhirContext.getVersion().getVersion()) {
case R4:
theIncomingTargetEid.forEach(eid -> addIdentifierIfAbsent((Person) theSourceResource, eid.toR4()));
cloneExternalEidIntoNewSourceResource(sourceResourceIdentifier, incomingExternalEid.toR4(), theSourceResource);
break;
case DSTU3:
theIncomingTargetEid.forEach(eid -> addIdentifierIfAbsent((org.hl7.fhir.dstu3.model.Person) theSourceResource, eid.toDSTU3()));
cloneExternalEidIntoNewSourceResource(sourceResourceIdentifier, incomingExternalEid.toDSTU3(), theSourceResource);
break;
}
}
}
/**
switch (myFhirContext.getVersion().getVersion()) {
case R4:
theIncomingTargetExternalEids.forEach(eid -> addIdentifierIfAbsent((Person) theSourceResource, eid.toR4()));
break;
case DSTU3:
theIncomingTargetExternalEids.forEach(eid -> addIdentifierIfAbsent((org.hl7.fhir.dstu3.model.Person) theSourceResource, eid.toDSTU3()));
break;
default:
throw new UnsupportedOperationException("Version not supported: " + myFhirContext.getVersion().getVersion());
}
}*/
}
/**
@ -508,15 +562,6 @@ public class PersonHelper {
}
}
private void addIdentifierIfAbsent(IAnyResource theSourceResource, Identifier theIdentifier) {
Optional<Identifier> first = theSourceResource.getIdentifier().stream().filter(identifier -> identifier.getSystem().equals(theIdentifier.getSystem())).filter(identifier -> identifier.getValue().equals(theIdentifier.getValue())).findFirst();
if (first.isPresent()) {
return;
} else {
theSourceResource.addIdentifier(theIdentifier);
}
}
public void mergePersonFields(IBaseResource theFromPerson, IBaseResource theToPerson) {
switch (myFhirContext.getVersion().getVersion()) {