Removed person refs

This commit is contained in:
Nick Goupinets 2020-12-02 17:41:42 -05:00
parent 417ae55cb4
commit ffee7a894f
64 changed files with 508 additions and 624 deletions

View File

@ -53,7 +53,6 @@ public class MdmLink {
private static final int LINK_SOURCE_LENGTH = 16;
public static final int TARGET_TYPE_LENGTH = 40;
@SequenceGenerator(name = "SEQ_EMPI_LINK_ID", sequenceName = "SEQ_EMPI_LINK_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_EMPI_LINK_ID")
@Id

View File

@ -44,6 +44,7 @@ import org.springframework.stereotype.Service;
@Service
public class MdmMessageHandler implements MessageHandler {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
@ -76,7 +77,8 @@ public class MdmMessageHandler implements MessageHandler {
throw e;
}
}
public void matchMdmAndUpdateLinks(ResourceModifiedMessage theMsg) {
private void matchMdmAndUpdateLinks(ResourceModifiedMessage theMsg) {
String resourceType = theMsg.getId(myFhirContext).getResourceType();
validateResourceType(resourceType);
MdmTransactionContext mdmContext = createMdmContext(theMsg, resourceType);

View File

@ -128,7 +128,7 @@ public class MdmConsumerConfig {
}
@Bean
MdmGoldenResourceFindingSvc mdmPersonFindingSvc() {
MdmGoldenResourceFindingSvc mdmGoldenResourceFindingSvc() {
return new MdmGoldenResourceFindingSvc();
}
@ -163,7 +163,7 @@ public class MdmConsumerConfig {
}
@Bean
IGoldenResourceMergerSvc mdmPersonMergerSvc() {
IGoldenResourceMergerSvc mdmGoldenResourceMergerSvc() {
return new GoldenResourceMergerSvcImpl();
}

View File

@ -33,32 +33,34 @@ import org.springframework.stereotype.Service;
@Service
public class MdmSearchParameterLoader {
public static final String MDM_PERSON_ASSURANCE_SEARCH_PARAMETER_ID = "person-assurance";
public static final String MDM_PERSON_ACTIVE_SEARCH_PARAMETER_ID = "person-active";
@Autowired
public FhirContext myFhirContext;
@Autowired
public DaoRegistry myDaoRegistry;
synchronized public void daoUpdateMdmSearchParameters() {
IBaseResource personAssurance;
IBaseResource personActive;
IBaseResource goldenResourceAssurance;
IBaseResource goldenResourceActive;
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
personAssurance = buildAssuranceMdmSearchParameterDstu3();
personActive = buildActiveMdmSearchParameterDstu3();
goldenResourceAssurance = buildAssuranceMdmSearchParameterDstu3();
goldenResourceActive = buildActiveMdmSearchParameterDstu3();
break;
case R4:
personAssurance = buildAssuranceMdmSearchParameterR4();
personActive = buildActiveMdmSearchParameterR4();
goldenResourceAssurance = buildAssuranceMdmSearchParameterR4();
goldenResourceActive = buildActiveMdmSearchParameterR4();
break;
default:
throw new ConfigurationException("MDM not supported for FHIR version " + myFhirContext.getVersion().getVersion());
}
IFhirResourceDao<IBaseResource> searchParameterDao = myDaoRegistry.getResourceDao("SearchParameter");
searchParameterDao.update(personAssurance);
searchParameterDao.update(personActive);
searchParameterDao.update(goldenResourceAssurance);
searchParameterDao.update(goldenResourceActive);
}
private org.hl7.fhir.dstu3.model.SearchParameter buildAssuranceMdmSearchParameterDstu3() {

View File

@ -21,14 +21,14 @@ package ca.uhn.fhir.jpa.mdm.dao;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
@ -92,7 +92,6 @@ public class MdmLinkDaoSvc {
} else {
MdmLink newLink = myMdmLinkFactory.newMdmLink();
newLink.setGoldenResourcePid(theGoldenResourcePid);
newLink.setPersonPid(theGoldenResourcePid);
newLink.setTargetPid(theTargetResourcePid);
return newLink;
}
@ -160,16 +159,16 @@ public class MdmLinkDaoSvc {
}
/**
* Given a person a target and a match result, return the matching {@link MdmLink}, if it exists.
* Given a golden resource a target and a match result, return the matching {@link MdmLink}, if it exists.
*
* @param thePersonPid The Pid of the Person in the relationship
* @param theGoldenResourcePid The Pid of the Golden Resource in the relationship
* @param theTargetPid The Pid of the target in the relationship
* @param theMatchResult The MatchResult you are looking for.
* @return an Optional {@link MdmLink} containing the matched link if it exists.
*/
public Optional<MdmLink> getMdmLinksByPersonPidTargetPidAndMatchResult(Long thePersonPid, Long theTargetPid, MdmMatchResultEnum theMatchResult) {
public Optional<MdmLink> getMdmLinksByGoldenResourcePidTargetPidAndMatchResult(Long theGoldenResourcePid, Long theTargetPid, MdmMatchResultEnum theMatchResult) {
MdmLink exampleLink = myMdmLinkFactory.newMdmLink();
exampleLink.setGoldenResourcePid(thePersonPid);
exampleLink.setGoldenResourcePid(theGoldenResourcePid);
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(theMatchResult);
Example<MdmLink> example = Example.of(exampleLink);
@ -179,7 +178,7 @@ public class MdmLinkDaoSvc {
/**
* Get all {@link MdmLink} which have {@link MdmMatchResultEnum#POSSIBLE_DUPLICATE} as their match result.
*
* @return A list of {@link MdmLink} that hold potential duplicate persons.
* @return A list of {@link MdmLink} that hold potential duplicate golden resources.
*/
public List<MdmLink> getPossibleDuplicates() {
MdmLink exampleLink = myMdmLinkFactory.newMdmLink();
@ -210,10 +209,10 @@ public class MdmLinkDaoSvc {
}
/**
* Given a Golden Resource , return all links in which they are the source Person of the {@link MdmLink}
* Given a Golden Resource, return all links in which they are the source Golden Resource of the {@link MdmLink}
*
* @param theGoldenResource The {@link IBaseResource} Person who's links you would like to retrieve.
* @return A list of all {@link MdmLink} entities in which theGoldenResource is the source Person.
* @param theGoldenResource The {@link IBaseResource} Golden Resource who's links you would like to retrieve.
* @return A list of all {@link MdmLink} entities in which theGoldenResource is the source Golden Resource
*/
public List<MdmLink> findMdmLinksByGoldenResource(IBaseResource theGoldenResource) {
Long pid = myIdHelperService.getPidOrNull(theGoldenResource);
@ -228,7 +227,7 @@ public class MdmLinkDaoSvc {
/**
* Delete all {@link MdmLink} entities, and return all resource PIDs from the source of the relationship.
*
* @return A list of Long representing the related Person Pids.
* @return A list of Long representing the related Golden Resource Pids.
*/
@Transactional
public List<Long> deleteAllMdmLinksAndReturnGoldenResourcePids() {
@ -237,26 +236,26 @@ public class MdmLinkDaoSvc {
}
private List<Long> deleteMdmLinksAndReturnGoldenResourcePids(List<MdmLink> theLinks) {
Set<Long> persons = theLinks.stream().map(MdmLink::getGoldenResourcePid).collect(Collectors.toSet());
Set<Long> goldenResources = theLinks.stream().map(MdmLink::getGoldenResourcePid).collect(Collectors.toSet());
//TODO GGG this is probably invalid... we are essentially looking for GOLDEN -> GOLDEN links, which are either POSSIBLE_DUPLICATE
//and REDIRECT
//persons.addAll(theLinks.stream().filter(link -> "Person".equals(link.getEmpiTargetType())).map(EmpiLink::getTargetPid).collect(Collectors.toSet()));
persons.addAll(theLinks.stream()
//goldenResources.addAll(theLinks.stream().filter(link -> "Person".equals(link.getEmpiTargetType())).map(EmpiLink::getTargetPid).collect(Collectors.toSet()));
goldenResources.addAll(theLinks.stream()
.filter(link -> link.getMatchResult().equals(MdmMatchResultEnum.REDIRECT)
|| link.getMatchResult().equals(MdmMatchResultEnum.POSSIBLE_DUPLICATE))
.map(MdmLink::getTargetPid).collect(Collectors.toSet()));
ourLog.info("Deleting {} MDM link records...", theLinks.size());
myMdmLinkDao.deleteAll(theLinks);
ourLog.info("{} MDM link records deleted", theLinks.size());
return new ArrayList<>(persons);
return new ArrayList<>(goldenResources);
}
/**
* Given a valid {@link String}, delete all {@link MdmLink} entities for that type, and get the Pids
* for the Person resources which were the sources of the links.
* for the Golden Resources which were the sources of the links.
*
* @param theTargetType the type of relationship you would like to delete.
* @return A list of longs representing the Pids of the Person resources used as the sources of the relationships that were deleted.
* @return A list of longs representing the Pids of the Golden Resources resources used as the sources of the relationships that were deleted.
*/
public List<Long> deleteAllMdmLinksOfTypeAndReturnGoldenResourcePids(String theTargetType) {
MdmLink link = new MdmLink();

View File

@ -78,8 +78,7 @@ public class MdmStorageInterceptor implements IMdmStorageInterceptor {
}
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED)
public void blockManualPersonManipulationOnUpdate(IBaseResource theOldResource, IBaseResource theUpdatedResource, RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) {
public void blockManualGoldenResourceManipulationOnUpdate(IBaseResource theOldResource, IBaseResource theUpdatedResource, RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) {
//If running in single EID mode, forbid multiple eids.
if (myMdmSettings.isPreventMultipleEids()) {
forbidIfHasMultipleEids(theUpdatedResource);
@ -87,7 +86,7 @@ public class MdmStorageInterceptor implements IMdmStorageInterceptor {
//TODO GGG MDM: Check if this is actually handled already in mdm update code or not.
if (myGoldenResourceHelper.isDeactivated(theUpdatedResource)) {
ourLog.debug("Deleting MDM links to deactivated Person {}", theUpdatedResource.getIdElement().toUnqualifiedVersionless());
ourLog.debug("Deleting MDM links to deactivated Golden resource {}", theUpdatedResource.getIdElement().toUnqualifiedVersionless());
int deleted = myMdmLinkDeleteSvc.deleteNonRedirectWithWithAnyReferenceTo(theUpdatedResource);
if (deleted > 0) {
ourLog.debug("Deleted {} MDM links", deleted);

View File

@ -34,8 +34,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* This class is responsible for clearing out existing MDM links, as well as deleting all persons related to those MDM Links.
*
* This class is responsible for clearing out existing MDM links, as well as deleting all Golden Resources related to those MDM Links.
*/
public class MdmClearSvcImpl implements IMdmExpungeSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@ -55,10 +54,10 @@ public class MdmClearSvcImpl implements IMdmExpungeSvc {
public long expungeAllMdmLinksOfTargetType(String theResourceType, ServletRequestDetails theRequestDetails) {
throwExceptionIfInvalidTargetType(theResourceType);
ourLog.info("Clearing all MDM Links for resource type {}...", theResourceType);
List<Long> personPids = myMdmLinkDaoSvc.deleteAllMdmLinksOfTypeAndReturnGoldenResourcePids(theResourceType);
DeleteMethodOutcome deleteOutcome = myMdmGoldenResourceDeletingSvcImpl.expungeGoldenResourcePids(personPids, theRequestDetails);
ourLog.info("MDM clear operation complete. Removed {} MDM links and {} Person resources.", personPids.size(), deleteOutcome.getExpungedResourcesCount());
return personPids.size();
List<Long> goldenResourcePids = myMdmLinkDaoSvc.deleteAllMdmLinksOfTypeAndReturnGoldenResourcePids(theResourceType);
DeleteMethodOutcome deleteOutcome = myMdmGoldenResourceDeletingSvcImpl.expungeGoldenResourcePids(goldenResourcePids, theResourceType, theRequestDetails);
ourLog.info("MDM clear operation complete. Removed {} MDM links and {} Golden Resources.", goldenResourcePids.size(), deleteOutcome.getExpungedResourcesCount());
return goldenResourcePids.size();
}
private void throwExceptionIfInvalidTargetType(String theResourceType) {
@ -70,10 +69,16 @@ public class MdmClearSvcImpl implements IMdmExpungeSvc {
@Override
public long expungeAllMdmLinks(ServletRequestDetails theRequestDetails) {
ourLog.info("Clearing all MDM Links...");
long retVal = 0;
for(String mdmType : myMdmSettings.getMdmRules().getMdmTypes()) {
List<Long> goldenResourcePids = myMdmLinkDaoSvc.deleteAllMdmLinksAndReturnGoldenResourcePids();
DeleteMethodOutcome deleteOutcome = myMdmGoldenResourceDeletingSvcImpl.expungeGoldenResourcePids(goldenResourcePids, theRequestDetails);
ourLog.info("MDM clear operation complete. Removed {} MDM links and expunged {} Golden resources.", goldenResourcePids.size(), deleteOutcome.getExpungedResourcesCount());
return goldenResourcePids.size();
DeleteMethodOutcome deleteOutcome = myMdmGoldenResourceDeletingSvcImpl.expungeGoldenResourcePids(goldenResourcePids, null, theRequestDetails);
ourLog.info("MDM clear operation on type {} complete. Removed {} MDM links and expunged {} Golden resources.", mdmType, goldenResourcePids.size(), deleteOutcome.getExpungedResourcesCount());
retVal += goldenResourcePids.size();
}
ourLog.info("MDM clear complete expunged a total of golden resources.", retVal);
return retVal;
}
}

View File

@ -54,47 +54,47 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
IMdmLinkUpdaterSvc myIMdmLinkUpdaterSvc;
@Override
public IAnyResource mergeGoldenResources(String theFromPersonId, String theToPersonId, MdmTransactionContext theMdmTransactionContext) {
IAnyResource fromPerson = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPersonId);
IAnyResource toPerson = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPersonId);
myMdmControllerHelper.validateMergeResources(fromPerson, toPerson);
myMdmControllerHelper.validateSameVersion(fromPerson, theFromPersonId);
myMdmControllerHelper.validateSameVersion(toPerson, theToPersonId);
public IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, MdmTransactionContext theMdmTransactionContext) {
IAnyResource fromGoldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId);
IAnyResource toGoldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToGoldenResourceId);
myMdmControllerHelper.validateMergeResources(fromGoldenResource, toGoldenResource);
myMdmControllerHelper.validateSameVersion(fromGoldenResource, theFromGoldenResourceId);
myMdmControllerHelper.validateSameVersion(toGoldenResource, theToGoldenResourceId);
return myGoldenResourceMergerSvc.mergeGoldenResources(fromPerson, toPerson, theMdmTransactionContext);
return myGoldenResourceMergerSvc.mergeGoldenResources(fromGoldenResource, toGoldenResource, theMdmTransactionContext);
}
@Override
public Stream<MdmLinkJson> queryLinks(@Nullable String thePersonId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext) {
IIdType personId = MdmControllerUtil.extractPersonIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, thePersonId);
public Stream<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext) {
IIdType goldenResourceId = MdmControllerUtil.extractGoldenResourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IIdType targetId = MdmControllerUtil.extractTargetIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theTargetId);
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
MdmLinkSourceEnum linkSource = MdmControllerUtil.extractLinkSourceOrNull(theLinkSource);
return myMdmLinkQuerySvc.queryLinks(personId, targetId, matchResult, linkSource, theMdmTransactionContext);
return myMdmLinkQuerySvc.queryLinks(goldenResourceId, targetId, matchResult, linkSource, theMdmTransactionContext);
}
@Override
public Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext) {
return myMdmLinkQuerySvc.getDuplicatePersons(theMdmTransactionContext);
return myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext);
}
@Override
public IAnyResource updateLink(String theGoldenResourceId, String theTargetId, String theMatchResult, MdmTransactionContext theMdmTransactionContext) {
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
IAnyResource person = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IAnyResource goldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IAnyResource target = myMdmControllerHelper.getLatestTargetFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetId);
myMdmControllerHelper.validateSameVersion(person, theGoldenResourceId);
myMdmControllerHelper.validateSameVersion(goldenResource, theGoldenResourceId);
myMdmControllerHelper.validateSameVersion(target, theTargetId);
return myIMdmLinkUpdaterSvc.updateLink(person, target, matchResult, theMdmTransactionContext);
return myIMdmLinkUpdaterSvc.updateLink(goldenResource, target, matchResult, theMdmTransactionContext);
}
@Override
public void notDuplicateGoldenResource(String thePersonId, String theTargetPersonId, MdmTransactionContext theMdmTransactionContext) {
IAnyResource person = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, thePersonId);
IAnyResource target = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetPersonId);
public void notDuplicateGoldenResource(String theGoldenResourceId, String theTargetGoldenResourceId, MdmTransactionContext theMdmTransactionContext) {
IAnyResource goldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IAnyResource target = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetGoldenResourceId);
myIMdmLinkUpdaterSvc.notDuplicatePerson(person, target, theMdmTransactionContext);
myIMdmLinkUpdaterSvc.notDuplicateGoldenResource(goldenResource, target, theMdmTransactionContext);
}
}

View File

@ -64,7 +64,7 @@ public class MdmEidUpdateService {
void handleMdmUpdate(IAnyResource theResource, MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext) {
MdmUpdateContext updateContext = new MdmUpdateContext(theMatchedGoldenResourceCandidate, theResource);
if (updateContext.isRemainsMatchedToSamePerson()) {
if (updateContext.isRemainsMatchedToSameGoldenResource()) {
// 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);
@ -75,20 +75,20 @@ public class MdmEidUpdateService {
handleNoEidsInCommon(theResource, theMatchedGoldenResourceCandidate, theMdmTransactionContext, updateContext);
}
} else {
//This is a new linking scenario. we have to break the existing link and link to the new person. For now, we create duplicate.
//updated patient has an EID that matches to a new candidate. Link them, and set the persons possible duplicates
linkToNewPersonAndFlagAsDuplicate(theResource, updateContext.getExistingPerson(), updateContext.getMatchedGoldenResource(), theMdmTransactionContext);
//This is a new linking scenario. we have to break the existing link and link to the new Golden Resource. For now, we create duplicate.
//updated patient has an EID that matches to a new candidate. Link them, and set the Golden Resources possible duplicates
linkToNewGoldenResourceAndFlagAsDuplicate(theResource, updateContext.getExistingGoldenResource(), updateContext.getMatchedGoldenResource(), theMdmTransactionContext);
}
}
private void handleNoEidsInCommon(IAnyResource theResource, MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext, MdmUpdateContext theUpdateContext) {
// the user is simply updating their EID. We propagate this change to the Person.
//overwrite. No EIDS in common, but still same person.
// the user is simply updating their EID. We propagate this change to the GoldenResource.
//overwrite. No EIDS in common, but still same GoldenResource.
if (myMdmSettings.isPreventMultipleEids()) {
if (myMdmLinkDaoSvc.findMdmMatchLinksBySource(theUpdateContext.getMatchedGoldenResource()).size() <= 1) { // If there is only 0/1 link on the person, we can safely overwrite the EID.
if (myMdmLinkDaoSvc.findMdmMatchLinksBySource(theUpdateContext.getMatchedGoldenResource()).size() <= 1) { // If there is only 0/1 link on the GoldenResource, we can safely overwrite the EID.
handleExternalEidOverwrite(theUpdateContext.getMatchedGoldenResource(), theResource, theMdmTransactionContext);
} else { // If the person has multiple patients tied to it, we can't just overwrite the EID, so we split the person.
createNewPersonAndFlagAsDuplicate(theResource, theMdmTransactionContext, theUpdateContext.getExistingPerson());
} else { // If the GoldenResource has multiple targets tied to it, we can't just overwrite the EID, so we split the GoldenResource.
createNewGoldenResourceAndFlagAsDuplicate(theResource, theMdmTransactionContext, theUpdateContext.getExistingGoldenResource());
}
} else {
myGoldenResourceHelper.handleExternalEidAddition(theUpdateContext.getMatchedGoldenResource(), theResource, theMdmTransactionContext);
@ -96,31 +96,31 @@ public class MdmEidUpdateService {
myMdmLinkSvc.updateLink(theUpdateContext.getMatchedGoldenResource(), theResource, theMatchedGoldenResourceCandidate.getMatchResult(), MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void handleExternalEidOverwrite(IAnyResource thePerson, IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
private void handleExternalEidOverwrite(IAnyResource theGoldenResource, IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
List<CanonicalEID> eidFromResource = myEIDHelper.getExternalEid(theResource);
if (!eidFromResource.isEmpty()) {
myGoldenResourceHelper.overwriteExternalEids(thePerson, eidFromResource);
myGoldenResourceHelper.overwriteExternalEids(theGoldenResource, eidFromResource);
}
}
private boolean candidateIsSameAsMdmLinkPerson(MdmLink theExistingMatchLink, MatchedGoldenResourceCandidate thePersonCandidate) {
return theExistingMatchLink.getGoldenResourcePid().equals(thePersonCandidate.getCandidatePersonPid().getIdAsLong());
private boolean candidateIsSameAsMdmLinkGoldenResource(MdmLink theExistingMatchLink, MatchedGoldenResourceCandidate theGoldenResourceCandidate) {
return theExistingMatchLink.getGoldenResourcePid().equals(theGoldenResourceCandidate.getCandidateGoldenResourcePid().getIdAsLong());
}
private void createNewPersonAndFlagAsDuplicate(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext, IAnyResource theOldPerson) {
private void createNewGoldenResourceAndFlagAsDuplicate(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext, IAnyResource theOldGoldenResource) {
log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs.");
IAnyResource newPerson = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(theResource);
IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(theResource);
myMdmLinkSvc.updateLink(newPerson, theResource, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newPerson, theOldPerson, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newGoldenResource, theOldGoldenResource, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void linkToNewPersonAndFlagAsDuplicate(IAnyResource theResource, IAnyResource theOldPerson, IAnyResource theNewPerson, MdmTransactionContext theMdmTransactionContext) {
private void linkToNewGoldenResourceAndFlagAsDuplicate(IAnyResource theResource, IAnyResource theOldGoldenResource, IAnyResource theNewGoldenResource, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, "Changing a match link!");
myMdmLinkSvc.deleteLink(theOldPerson, theResource, theMdmTransactionContext);
myMdmLinkSvc.updateLink(theNewPerson, theResource, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.deleteLink(theOldGoldenResource, theResource, theMdmTransactionContext);
myMdmLinkSvc.updateLink(theNewGoldenResource, theResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs.");
myMdmLinkSvc.updateLink(theNewPerson, theOldPerson, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(theNewGoldenResource, theOldGoldenResource, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void log(MdmTransactionContext theMdmTransactionContext, String theMessage) {
@ -132,10 +132,11 @@ public class MdmEidUpdateService {
* Data class to hold context surrounding an update operation for an MDM target.
*/
class MdmUpdateContext {
private final boolean myHasEidsInCommon;
private final boolean myIncomingResourceHasAnEid;
private IAnyResource myExistingPerson;
private boolean myRemainsMatchedToSamePerson;
private IAnyResource myExistingGoldenResource;
private boolean myRemainsMatchedToSameGoldenResource;
private final IAnyResource myMatchedGoldenResource;
public IAnyResource getMatchedGoldenResource() {
@ -150,15 +151,15 @@ public class MdmEidUpdateService {
myIncomingResourceHasAnEid = !myEIDHelper.getExternalEid(theResource).isEmpty();
Optional<MdmLink> theExistingMatchLink = myMdmLinkDaoSvc.getMatchedLinkForTarget(theResource);
myExistingPerson = null;
myExistingGoldenResource = null;
if (theExistingMatchLink.isPresent()) {
MdmLink mdmLink = theExistingMatchLink.get();
Long existingPersonPid = mdmLink.getGoldenResourcePid();
myExistingPerson = myMdmResourceDaoSvc.readGoldenResourceByPid(new ResourcePersistentId(existingPersonPid), resourceType);
myRemainsMatchedToSamePerson = candidateIsSameAsMdmLinkPerson(mdmLink, theMatchedGoldenResourceCandidate);
Long existingGoldenResourcePid = mdmLink.getGoldenResourcePid();
myExistingGoldenResource = myMdmResourceDaoSvc.readGoldenResourceByPid(new ResourcePersistentId(existingGoldenResourcePid), resourceType);
myRemainsMatchedToSameGoldenResource = candidateIsSameAsMdmLinkGoldenResource(mdmLink, theMatchedGoldenResourceCandidate);
} else {
myRemainsMatchedToSamePerson = false;
myRemainsMatchedToSameGoldenResource = false;
}
}
@ -170,12 +171,12 @@ public class MdmEidUpdateService {
return myIncomingResourceHasAnEid;
}
public IAnyResource getExistingPerson() {
return myExistingPerson;
public IAnyResource getExistingGoldenResource() {
return myExistingGoldenResource;
}
public boolean isRemainsMatchedToSamePerson() {
return myRemainsMatchedToSamePerson;
public boolean isRemainsMatchedToSameGoldenResource() {
return myRemainsMatchedToSameGoldenResource;
}
}
}

View File

@ -36,6 +36,7 @@ import java.util.List;
@Service
public class MdmGoldenResourceDeletingSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
/**
@ -50,7 +51,7 @@ public class MdmGoldenResourceDeletingSvc {
@Autowired
DeleteExpungeService myDeleteExpungeService;
public DeleteMethodOutcome expungeGoldenResourcePids(List<Long> thePersonPids, ServletRequestDetails theRequestDetails) {
return myDeleteExpungeService.expungeByResourcePids(ProviderConstants.MDM_CLEAR, "Person", new SliceImpl<>(thePersonPids), theRequestDetails);
public DeleteMethodOutcome expungeGoldenResourcePids(List<Long> theGoldenResourcePids, String theResourceType, ServletRequestDetails theRequestDetails) {
return myDeleteExpungeService.expungeByResourcePids(ProviderConstants.MDM_CLEAR, theResourceType, new SliceImpl<>(theGoldenResourcePids), theRequestDetails);
}
}

View File

@ -53,7 +53,7 @@ public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc {
}
@Override
public Stream<MdmLinkJson> getDuplicatePersons(MdmTransactionContext theMdmContext) {
public Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext) {
Example<MdmLink> exampleLink = exampleLinkFromParameters(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null);
return myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink).stream().map(this::toJson);
}
@ -76,10 +76,10 @@ public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc {
return retval;
}
private Example<MdmLink> exampleLinkFromParameters(IIdType thePersonId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource) {
private Example<MdmLink> exampleLinkFromParameters(IIdType theGoldenResourceId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource) {
MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink();
if (thePersonId != null) {
mdmLink.setGoldenResourcePid(myIdHelperService.getPidOrThrowException(thePersonId));
if (theGoldenResourceId != null) {
mdmLink.setGoldenResourcePid(myIdHelperService.getPidOrThrowException(theGoldenResourceId));
}
if (theTargetId != null) {
mdmLink.setTargetPid(myIdHelperService.getPidOrThrowException(theTargetId));

View File

@ -29,7 +29,6 @@ import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -45,6 +44,7 @@ import java.util.Optional;
*/
@Service
public class MdmLinkSvcImpl implements IMdmLinkSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
@ -52,15 +52,13 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc {
@Autowired
private MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
private GoldenResourceHelper myGoldenResourceHelper;
@Autowired
private IdHelperService myIdHelperService;
@Override
@Transactional
public void updateLink(IAnyResource thePerson, IAnyResource theTarget, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) {
if (theMatchOutcome.isPossibleDuplicate() && personsLinkedAsNoMatch(thePerson, theTarget)) {
log(theMdmTransactionContext, thePerson.getIdElement().toUnqualifiedVersionless() +
public void updateLink(IAnyResource theGoldenResource, IAnyResource theTarget, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) {
if (theMatchOutcome.isPossibleDuplicate() && goldenResourceLinkedAsNoMatch(theGoldenResource, theTarget)) {
log(theMdmTransactionContext, theGoldenResource.getIdElement().toUnqualifiedVersionless() +
" is linked as NO_MATCH with " +
theTarget.getIdElement().toUnqualifiedVersionless() +
" not linking as POSSIBLE_DUPLICATE.");
@ -68,18 +66,18 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc {
}
MdmMatchResultEnum matchResultEnum = theMatchOutcome.getMatchResultEnum();
validateRequestIsLegal(thePerson, theTarget, matchResultEnum, theLinkSource);
validateRequestIsLegal(theGoldenResource, theTarget, matchResultEnum, theLinkSource);
myMdmResourceDaoSvc.upsertGoldenResource(thePerson, theMdmTransactionContext.getResourceType());
createOrUpdateLinkEntity(thePerson, theTarget, theMatchOutcome, theLinkSource, theMdmTransactionContext);
myMdmResourceDaoSvc.upsertGoldenResource(theGoldenResource, theMdmTransactionContext.getResourceType());
createOrUpdateLinkEntity(theGoldenResource, theTarget, theMatchOutcome, theLinkSource, theMdmTransactionContext);
}
private boolean personsLinkedAsNoMatch(IAnyResource thePerson, IAnyResource theTarget) {
Long personId = myIdHelperService.getPidOrThrowException(thePerson);
private boolean goldenResourceLinkedAsNoMatch(IAnyResource theGoldenResource, IAnyResource theTarget) {
Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
Long targetId = myIdHelperService.getPidOrThrowException(theTarget);
// TODO perf collapse into one query
return myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(personId, targetId, MdmMatchResultEnum.NO_MATCH).isPresent() ||
myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(targetId, personId, MdmMatchResultEnum.NO_MATCH).isPresent();
return myMdmLinkDaoSvc.getMdmLinksByGoldenResourcePidTargetPidAndMatchResult(goldenResourceId, targetId, MdmMatchResultEnum.NO_MATCH).isPresent() ||
myMdmLinkDaoSvc.getMdmLinksByGoldenResourcePidTargetPidAndMatchResult(targetId, goldenResourceId, MdmMatchResultEnum.NO_MATCH).isPresent();
}
@Override
@ -120,12 +118,12 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc {
return theIncomingSource == MdmLinkSourceEnum.AUTO && theExistingSource.isManual();
}
private Optional<MdmLink> getMdmLinkForGoldenResourceTargetPair(IAnyResource thePerson, IAnyResource theCandidate) {
if (thePerson.getIdElement().getIdPart() == null || theCandidate.getIdElement().getIdPart() == null) {
private Optional<MdmLink> getMdmLinkForGoldenResourceTargetPair(IAnyResource theGoldenResource, IAnyResource theCandidate) {
if (theGoldenResource.getIdElement().getIdPart() == null || theCandidate.getIdElement().getIdPart() == null) {
return Optional.empty();
} else {
return myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndTargetResourcePid(
myIdHelperService.getPidOrNull(thePerson),
myIdHelperService.getPidOrNull(theGoldenResource),
myIdHelperService.getPidOrNull(theCandidate)
);
}

View File

@ -91,7 +91,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
myMdmLinkDaoSvc.save(mdmLink);
myMdmResourceDaoSvc.upsertGoldenResource(theGoldenResource, theMdmContext.getResourceType());
if (theMatchResult == MdmMatchResultEnum.NO_MATCH) {
// Need to find a new Person to link this target to
// Need to find a new Golden Resource to link this target to
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(theTarget, theMdmContext);
}
return theGoldenResource;
@ -128,20 +128,20 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
@Transactional
@Override
public void notDuplicatePerson(IAnyResource thePerson, IAnyResource theTarget, MdmTransactionContext theMdmContext) {
validateNotDuplicatePersonRequest(thePerson, theTarget);
public void notDuplicateGoldenResource(IAnyResource theGoldenResource, IAnyResource theTarget, MdmTransactionContext theMdmContext) {
validateNotDuplicateGoldenResourceRequest(theGoldenResource, theTarget);
Long personId = myIdHelperService.getPidOrThrowException(thePerson);
Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
Long targetId = myIdHelperService.getPidOrThrowException(theTarget);
Optional<MdmLink> oMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndTargetResourcePid(personId, targetId);
Optional<MdmLink> oMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndTargetResourcePid(goldenResourceId, targetId);
if (!oMdmLink.isPresent()) {
throw new InvalidRequestException("No link exists between " + thePerson.getIdElement().toVersionless() + " and " + theTarget.getIdElement().toVersionless());
throw new InvalidRequestException("No link exists between " + theGoldenResource.getIdElement().toVersionless() + " and " + theTarget.getIdElement().toVersionless());
}
MdmLink mdmLink = oMdmLink.get();
if (!mdmLink.isPossibleDuplicate()) {
throw new InvalidRequestException(thePerson.getIdElement().toVersionless() + " and " + theTarget.getIdElement().toVersionless() + " are not linked as POSSIBLE_DUPLICATE.");
throw new InvalidRequestException(theGoldenResource.getIdElement().toVersionless() + " and " + theTarget.getIdElement().toVersionless() + " are not linked as POSSIBLE_DUPLICATE.");
}
mdmLink.setMatchResult(MdmMatchResultEnum.NO_MATCH);
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
@ -151,7 +151,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
/**
* Ensure that the two resources are of the same type and both are managed by HAPI-MDM
*/
private void validateNotDuplicatePersonRequest(IAnyResource theGoldenResource, IAnyResource theTarget) {
private void validateNotDuplicateGoldenResourceRequest(IAnyResource theGoldenResource, IAnyResource theTarget) {
String goldenResourceType = myFhirContext.getResourceType(theGoldenResource);
String targetType = myFhirContext.getResourceType(theTarget);
if (!goldenResourceType.equalsIgnoreCase(targetType)) {
@ -159,7 +159,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
}
if (!MdmUtil.isMdmManaged(theGoldenResource) || !MdmUtil.isMdmManaged(theTarget)) {
throw new InvalidRequestException("Only MDM Managed Golden Resources may be updated via this operation. The resource provided is not tagged as managed by hapi-mdm");
throw new InvalidRequestException("Only MDM Managed Golden Resources may be updated via this operation. The resource provided is not tagged as managed by HAPI-MDM");
}
}
}

View File

@ -41,8 +41,8 @@ import java.util.List;
/**
* MdmMatchLinkSvc is the entrypoint for HAPI's MDM system. An incoming resource can call
* updateMdmLinksForMdmTarget and the underlying MDM system will take care of matching it to a person, or creating a
* new Person if a suitable one was not found.
* updateMdmLinksForMdmTarget and the underlying MDM system will take care of matching it to a GoldenResource, or creating a
* new GoldenResource if a suitable one was not found.
*/
@Service
public class MdmMatchLinkSvc {
@ -58,7 +58,7 @@ public class MdmMatchLinkSvc {
private MdmEidUpdateService myEidUpdateService;
/**
* Given an MDM Target (consisting of either a Patient or a Practitioner), find a suitable Person candidate for them,
* Given an MDM Target (consisting of either a Patient or a Practitioner), find a suitable Golden Resource candidate for them,
* or create one if one does not exist. Performs matching based on rules defined in mdm-rules.json.
* Does nothing if resource is determined to be not managed by MDM.
*
@ -89,33 +89,33 @@ public class MdmMatchLinkSvc {
private void handleMdmWithMultipleCandidates(IAnyResource theResource, CandidateList theCandidateList, MdmTransactionContext theMdmTransactionContext) {
MatchedGoldenResourceCandidate firstMatch = theCandidateList.getFirstMatch();
Long samplePersonPid = firstMatch.getCandidatePersonPid().getIdAsLong();
boolean allSamePerson = theCandidateList.stream()
.allMatch(candidate -> candidate.getCandidatePersonPid().getIdAsLong().equals(samplePersonPid));
Long sampleGoldenResourcePid = firstMatch.getCandidateGoldenResourcePid().getIdAsLong();
boolean allSameGoldenResource = theCandidateList.stream()
.allMatch(candidate -> candidate.getCandidateGoldenResourcePid().getIdAsLong().equals(sampleGoldenResourcePid));
if (allSamePerson) {
log(theMdmTransactionContext, "MDM received multiple match candidates, but they are all linked to the same person.");
if (allSameGoldenResource) {
log(theMdmTransactionContext, "MDM received multiple match candidates, but they are all linked to the same Golden Resource.");
handleMdmWithSingleCandidate(theResource, firstMatch, theMdmTransactionContext);
} else {
log(theMdmTransactionContext, "MDM received multiple match candidates, that were linked to different Persons. Setting POSSIBLE_DUPLICATES and POSSIBLE_MATCHES.");
log(theMdmTransactionContext, "MDM received multiple match candidates, that were linked to different Golden Resources. Setting POSSIBLE_DUPLICATES and POSSIBLE_MATCHES.");
//Set them all as POSSIBLE_MATCH
List<IAnyResource> persons = new ArrayList<>();
List<IAnyResource> goldenResources = new ArrayList<>();
for (MatchedGoldenResourceCandidate matchedGoldenResourceCandidate : theCandidateList.getCandidates()) {
IAnyResource person = myMdmGoldenResourceFindingSvc
IAnyResource goldenResource = myMdmGoldenResourceFindingSvc
.getGoldenResourceFromMatchedGoldenResourceCandidate(matchedGoldenResourceCandidate, theMdmTransactionContext.getResourceType());
MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_MATCH;
outcome.setEidMatch(theCandidateList.isEidMatch());
myMdmLinkSvc.updateLink(person, theResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
persons.add(person);
myMdmLinkSvc.updateLink(goldenResource, theResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
goldenResources.add(goldenResource);
}
//Set all Persons as POSSIBLE_DUPLICATE of the last person.
IAnyResource firstPerson = persons.get(0);
persons.subList(1, persons.size())
.forEach(possibleDuplicatePerson -> {
//Set all GoldenResources as POSSIBLE_DUPLICATE of the last GoldenResource.
IAnyResource firstGoldenResource = goldenResources.get(0);
goldenResources.subList(1, goldenResources.size())
.forEach(possibleDuplicateGoldenResource -> {
MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_DUPLICATE;
outcome.setEidMatch(theCandidateList.isEidMatch());
myMdmLinkSvc.updateLink(firstPerson, possibleDuplicatePerson, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(firstGoldenResource, possibleDuplicateGoldenResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
});
}
}
@ -128,33 +128,33 @@ public class MdmMatchLinkSvc {
// 2. Create source resoruce for the MDM target
// 3. UPDATE MDM LINK TABLE
myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void handleMdmCreate(IAnyResource theTargetResource, MatchedGoldenResourceCandidate thePersonCandidate, MdmTransactionContext theMdmTransactionContext) {
private void handleMdmCreate(IAnyResource theTargetResource, MatchedGoldenResourceCandidate theGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, "MDM has narrowed down to one candidate for matching.");
IAnyResource golenResource = myMdmGoldenResourceFindingSvc.getGoldenResourceFromMatchedGoldenResourceCandidate(thePersonCandidate, theMdmTransactionContext.getResourceType());
IAnyResource golenResource = myMdmGoldenResourceFindingSvc.getGoldenResourceFromMatchedGoldenResourceCandidate(theGoldenResourceCandidate, theMdmTransactionContext.getResourceType());
if (myGoldenResourceHelper.isPotentialDuplicate(golenResource, theTargetResource)) {
log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs.");
IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(theTargetResource);
myMdmLinkSvc.updateLink(newGoldenResource, theTargetResource, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newGoldenResource, theTargetResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newGoldenResource, golenResource, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
} else {
if (thePersonCandidate.isMatch()) {
if (theGoldenResourceCandidate.isMatch()) {
myGoldenResourceHelper.handleExternalEidAddition(golenResource, theTargetResource, theMdmTransactionContext);
//TODO MDM GGG/NG: eventually we need to add survivorship rules of attributes here. Currently no data is copied over except EIDs.
}
myMdmLinkSvc.updateLink(golenResource, theTargetResource, thePersonCandidate.getMatchResult(), MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(golenResource, theTargetResource, theGoldenResourceCandidate.getMatchResult(), MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
}
private void handleMdmWithSingleCandidate(IAnyResource theResource, MatchedGoldenResourceCandidate thePersonCandidate, MdmTransactionContext theMdmTransactionContext) {
private void handleMdmWithSingleCandidate(IAnyResource theResource, MatchedGoldenResourceCandidate theGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, "MDM has narrowed down to one candidate for matching.");
if (theMdmTransactionContext.getRestOperation().equals(MdmTransactionContext.OperationType.UPDATE_RESOURCE)) {
myEidUpdateService.handleMdmUpdate(theResource, thePersonCandidate, theMdmTransactionContext);
myEidUpdateService.handleMdmUpdate(theResource, theGoldenResourceCandidate, theMdmTransactionContext);
} else {
handleMdmCreate(theResource, thePersonCandidate, theMdmTransactionContext);
handleMdmCreate(theResource, theGoldenResourceCandidate, theMdmTransactionContext);
}
}

View File

@ -43,7 +43,7 @@ import java.util.Optional;
@Service
public class MdmResourceDaoSvc {
private static final int MAX_MATCHING_PERSONS = 1000;
private static final int MAX_MATCHING_GOLDEN_RESOURCES = 1000;
@Autowired
DaoRegistry myDaoRegistry;
@ -80,14 +80,14 @@ public class MdmResourceDaoSvc {
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
IBundleProvider search = resourceDao.search(map);
List<IBaseResource> resources = search.getResources(0, MAX_MATCHING_PERSONS);
List<IBaseResource> resources = search.getResources(0, MAX_MATCHING_GOLDEN_RESOURCES);
if (resources.isEmpty()) {
return Optional.empty();
} else if (resources.size() > 1) {
throw new InternalErrorException("Found more than one active " +
MdmConstants.CODE_HAPI_MDM_MANAGED +
" Person with EID " +
" Golden Resource with EID " +
theEid +
": " +
resources.get(0).getIdElement().getValue() +

View File

@ -51,12 +51,12 @@ public class FindCandidateByEidSvc extends BaseCandidateFinder {
List<CanonicalEID> eidFromResource = myEIDHelper.getExternalEid(theBaseResource);
if (!eidFromResource.isEmpty()) {
for (CanonicalEID eid : eidFromResource) {
Optional<IAnyResource> oFoundPerson = myMdmResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType());
if (oFoundPerson.isPresent()) {
IAnyResource foundPerson = oFoundPerson.get();
Long pidOrNull = myIdHelperService.getPidOrNull(foundPerson);
Optional<IAnyResource> oFoundGoldenResource = myMdmResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType());
if (oFoundGoldenResource.isPresent()) {
IAnyResource foundGoldenResource = oFoundGoldenResource.get();
Long pidOrNull = myIdHelperService.getPidOrNull(foundGoldenResource);
MatchedGoldenResourceCandidate mpc = new MatchedGoldenResourceCandidate(new ResourcePersistentId(pidOrNull), MdmMatchOutcome.EID_MATCH);
ourLog.debug("Matched {} by EID {}", foundPerson.getIdElement(), eid);
ourLog.debug("Matched {} by EID {}", foundGoldenResource.getIdElement(), eid);
retval.add(mpc);
}
}

View File

@ -36,9 +36,9 @@ public class FindCandidateByLinkSvc extends BaseCandidateFinder {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
/**
* Attempt to find a currently matching Person, based on the presence of an {@link MdmLink} entity.
* Attempt to find a currently matching Golden Resource, based on the presence of an {@link MdmLink} entity.
*
* @param theTarget the {@link IAnyResource} that we want to find candidate Persons for.
* @param theTarget the {@link IAnyResource} that we want to find candidate Golden Resources for.
* @return an Optional list of {@link MatchedGoldenResourceCandidate} indicating matches.
*/
@Override
@ -49,9 +49,9 @@ public class FindCandidateByLinkSvc extends BaseCandidateFinder {
if (targetPid != null) {
Optional<MdmLink> oLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(targetPid);
if (oLink.isPresent()) {
ResourcePersistentId personPid = new ResourcePersistentId(oLink.get().getGoldenResourcePid());
ResourcePersistentId goldenResourcePid = new ResourcePersistentId(oLink.get().getGoldenResourcePid());
ourLog.debug("Resource previously linked. Using existing link.");
retval.add(new MatchedGoldenResourceCandidate(personPid, oLink.get()));
retval.add(new MatchedGoldenResourceCandidate(goldenResourcePid, oLink.get()));
}
}
return retval;

View File

@ -54,25 +54,25 @@ public class FindCandidateByScoreSvc extends BaseCandidateFinder {
private IMdmMatchFinderSvc myMdmMatchFinderSvc;
/**
* Attempt to find matching Persons by resolving them from similar Matching target resources, where target resource
* can be either Patient or Practitioner. Runs MDM logic over the existing Patient/Practitioners, then finds their
* Attempt to find matching Golden Resources by resolving them from similar Matching target resources, where target resource
* can be either Patient or Practitioner. Runs MDM logic over the existing target resources, then finds their
* entries in the MdmLink table, and returns all the matches found therein.
*
* @param theTarget the {@link IBaseResource} which we want to find candidate Persons for.
* @param theTarget the {@link IBaseResource} which we want to find candidate Golden Resources for.
* @return an Optional list of {@link MatchedGoldenResourceCandidate} indicating matches.
*/
@Override
protected List<MatchedGoldenResourceCandidate> findMatchGoldenResourceCandidates(IAnyResource theTarget) {
List<MatchedGoldenResourceCandidate> retval = new ArrayList<>();
List<Long> personPidsToExclude = getNoMatchPersonPids(theTarget);
List<Long> goldenResourcePidsToExclude = getNoMatchGoldenResourcePids(theTarget);
List<MatchedTarget> matchedCandidates = myMdmMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget);
//Convert all possible match targets to their equivalent Persons by looking up in the MdmLink table,
//Convert all possible match targets to their equivalent Golden Resources by looking up in the MdmLink table,
//while ensuring that the matches aren't in our NO_MATCH list.
// The data flow is as follows ->
// MatchedTargetCandidate -> Person -> MdmLink -> MatchedPersonCandidate
// MatchedTargetCandidate -> Golden Resource -> MdmLink -> MatchedGoldenResourceCandidate
matchedCandidates = matchedCandidates.stream().filter(mc -> mc.isMatch() || mc.isPossibleMatch()).collect(Collectors.toList());
for (MatchedTarget match : matchedCandidates) {
Optional<MdmLink> optionalMdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(match.getTarget()));
@ -81,8 +81,8 @@ public class FindCandidateByScoreSvc extends BaseCandidateFinder {
}
MdmLink matchMdmLink = optionalMdmLink.get();
if (personPidsToExclude.contains(matchMdmLink.getGoldenResourcePid())) {
ourLog.info("Skipping MDM on candidate person with PID {} due to manual NO_MATCH", matchMdmLink.getGoldenResourcePid());
if (goldenResourcePidsToExclude.contains(matchMdmLink.getGoldenResourcePid())) {
ourLog.info("Skipping MDM on candidate Golden Resource with PID {} due to manual NO_MATCH", matchMdmLink.getGoldenResourcePid());
continue;
}
@ -92,7 +92,7 @@ public class FindCandidateByScoreSvc extends BaseCandidateFinder {
return retval;
}
private List<Long> getNoMatchPersonPids(IBaseResource theBaseResource) {
private List<Long> getNoMatchGoldenResourcePids(IBaseResource theBaseResource) {
Long targetPid = myIdHelperService.getPidOrNull(theBaseResource);
return myMdmLinkDaoSvc.getMdmLinksByTargetPidAndMatchResult(targetPid, MdmMatchResultEnum.NO_MATCH)
.stream()
@ -100,8 +100,8 @@ public class FindCandidateByScoreSvc extends BaseCandidateFinder {
.collect(Collectors.toList());
}
private ResourcePersistentId getResourcePersistentId(Long thePersonPid) {
return new ResourcePersistentId(thePersonPid);
private ResourcePersistentId getResourcePersistentId(Long theGoldenResourcePid) {
return new ResourcePersistentId(theGoldenResourcePid);
}
@Override

View File

@ -34,12 +34,12 @@ public class MatchedGoldenResourceCandidate {
myMdmMatchOutcome = theMdmMatchOutcome;
}
public MatchedGoldenResourceCandidate(ResourcePersistentId thePersonPid, MdmLink theMdmLink) {
myCandidateGoldenResourcePid = thePersonPid;
public MatchedGoldenResourceCandidate(ResourcePersistentId theGoldenResourcePid, MdmLink theMdmLink) {
myCandidateGoldenResourcePid = theGoldenResourcePid;
myMdmMatchOutcome = new MdmMatchOutcome(theMdmLink.getVector(), theMdmLink.getScore()).setMatchResultEnum(theMdmLink.getMatchResult());
}
public ResourcePersistentId getCandidatePersonPid() {
public ResourcePersistentId getCandidateGoldenResourcePid() {
return myCandidateGoldenResourcePid;
}

View File

@ -36,6 +36,7 @@ import java.util.stream.Collectors;
@Service
public class MdmCandidateSearchCriteriaBuilderSvc {
@Autowired
private MdmSearchParamSvc myMdmSearchParamSvc;
@ -52,7 +53,7 @@ public class MdmCandidateSearchCriteriaBuilderSvc {
// If there are candidate search params, then make use of them, otherwise, search with only the filters.
if (resourceSearchParam != null) {
resourceSearchParam.iterator().forEachRemaining(searchParam -> {
//to compare it to all known PERSON objects, using the overlapping search parameters that they have.
//to compare it to all known GOLDEN_RESOURCE objects, using the overlapping search parameters that they have.
List<String> valuesFromResourceForSearchParam = myMdmSearchParamSvc.getValueFromResourceForSearchParam(theResource, searchParam);
if (!valuesFromResourceForSearchParam.isEmpty()) {
criteria.add(buildResourceMatchQuery(searchParam, valuesFromResourceForSearchParam));

View File

@ -47,18 +47,18 @@ public class MdmGoldenResourceFindingSvc {
private FindCandidateByScoreSvc myFindCandidateByScoreSvc;
/**
* Given an incoming IBaseResource, limited to Patient/Practitioner, return a list of {@link MatchedGoldenResourceCandidate}
* indicating possible candidates for a matching Person. Uses several separate methods for finding candidates:
* Given an incoming IBaseResource, limited to the supported MDM type, return a list of {@link MatchedGoldenResourceCandidate}
* indicating possible candidates for a matching Golden Resource. Uses several separate methods for finding candidates:
* <p>
* 0. First, check the incoming Resource for an EID. If it is present, and we can find a Person with this EID, it automatically matches.
* 1. First, check link table for any entries where this baseresource is the target of a person. If found, return.
* 2. If none are found, attempt to find Person Resources which link to this theResource.
* 3. If none are found, attempt to find Person Resources similar to our incoming resource based on the MDM rules and field matchers.
* 4. If none are found, attempt to find Persons that are linked to Patients/Practitioners that are similar to our incoming resource based on the MDM rules and
* 0. First, check the incoming Resource for an EID. If it is present, and we can find a Golden Resource with this EID, it automatically matches.
* 1. First, check link table for any entries where this baseresource is the target of a Golden Resource. If found, return.
* 2. If none are found, attempt to find Golden Resources which link to this theResource.
* 3. If none are found, attempt to find Golden Resources similar to our incoming resource based on the MDM rules and field matchers.
* 4. If none are found, attempt to find Golden Resources that are linked to targets that are similar to our incoming resource based on the MDM rules and
* field matchers.
*
* @param theResource the {@link IBaseResource} we are attempting to find matching candidate Persons for.
* @return A list of {@link MatchedGoldenResourceCandidate} indicating all potential Person matches.
* @param theResource the {@link IBaseResource} we are attempting to find matching candidate Golden Resources for.
* @return A list of {@link MatchedGoldenResourceCandidate} indicating all potential Golden Resource matches.
*/
public CandidateList findGoldenResourceCandidates(IAnyResource theResource) {
CandidateList matchedGoldenResourceCandidates = myFindCandidateByEidSvc.findCandidates(theResource);
@ -77,7 +77,7 @@ public class MdmGoldenResourceFindingSvc {
}
public IAnyResource getGoldenResourceFromMatchedGoldenResourceCandidate(MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, String theResourceType) {
ResourcePersistentId personPid = theMatchedGoldenResourceCandidate.getCandidatePersonPid();
return myMdmResourceDaoSvc.readGoldenResourceByPid(personPid, theResourceType);
ResourcePersistentId goldenResourcePid = theMatchedGoldenResourceCandidate.getCandidateGoldenResourcePid();
return myMdmResourceDaoSvc.readGoldenResourceByPid(goldenResourcePid, theResourceType);
}
}

View File

@ -21,7 +21,7 @@ import ca.uhn.fhir.jpa.mdm.config.MdmSubmitterConfig;
import ca.uhn.fhir.jpa.mdm.config.TestMdmConfigR4;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.mdm.matcher.IsLinkedTo;
import ca.uhn.fhir.jpa.mdm.matcher.IsMatchedToAPerson;
import ca.uhn.fhir.jpa.mdm.matcher.IsMatchedToAGoldenResource;
import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleDuplicateOf;
import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleLinkedTo;
import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleMatchWith;
@ -414,7 +414,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
}
protected Matcher<IAnyResource> matchedToAGoldenResource() {
return IsMatchedToAPerson.matchedToAGoldenResource(myIdHelperService, myMdmLinkDaoSvc);
return IsMatchedToAGoldenResource.matchedToAGoldenResource(myIdHelperService, myMdmLinkDaoSvc);
}
protected Patient getOnlyGoldenPatient() {

View File

@ -1,13 +1,13 @@
package ca.uhn.fhir.jpa.mdm.interceptor;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
@ -19,7 +19,6 @@ import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Medication;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Person;
import org.hl7.fhir.r4.model.SearchParameter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -73,7 +72,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
}
@Test
public void testDeletePersonDeletesLinks() throws InterruptedException {
public void testDeleteGoldenResourceDeletesLinks() throws InterruptedException {
myMdmHelper.createWithLatch(buildPaulPatient());
assertLinkCount(1);
Patient sourcePatient = getOnlyGoldenPatient();
@ -83,7 +82,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
@Test
public void testCreatePatientWithMdmTagForbidden() throws InterruptedException {
//Creating a person with the MDM-MANAGED tag should fail
//Creating a golden resource with the MDM-MANAGED tag should fail
Patient patient = new Patient();
patient.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM");
try {
@ -119,7 +118,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
}
@Test
public void testCreatingPersonWithInsufficentMDMAttributesIsNotMDMProcessed() throws InterruptedException {
public void testCreatingGoldenResourceWithInsufficentMDMAttributesIsNotMDMProcessed() throws InterruptedException {
myMdmHelper.doCreateResource(new Patient(), true);
assertLinkCount(0);
}
@ -158,7 +157,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
}
@Test
public void testPersonRecordsManagedByMdmAllShareSameTag() throws InterruptedException {
public void testGoldenResourceRecordsManagedByMdmAllShareSameTag() throws InterruptedException {
myMdmHelper.createWithLatch(buildJanePatient());
myMdmHelper.createWithLatch(buildPaulPatient());
@ -166,22 +165,22 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
IBundleProvider search = myPatientDao.search(buildGoldenResourceSearchParameterMap());
List<IBaseResource> resources = search.getResources(0, search.size());
for (IBaseResource person : resources) {
assertThat(person.getMeta().getTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED), is(notNullValue()));
for (IBaseResource r : resources) {
assertThat(r.getMeta().getTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED), is(notNullValue()));
}
}
@Test
public void testNonMdmManagedPersonCannotHaveMdmManagedTagAddedToThem() {
//Person created manually.
Person person = new Person();
DaoMethodOutcome daoMethodOutcome = myMdmHelper.doCreateResource(person, true);
public void testNonMdmManagedGoldenResourceCannotHaveMdmManagedTagAddedToThem() {
// GoldenResource created manually.
Patient patient = new Patient();
DaoMethodOutcome daoMethodOutcome = myMdmHelper.doCreateResource(patient, true);
assertNotNull(daoMethodOutcome.getId());
//Updating that person to set them as MDM managed is not allowed.
person.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM");
//Updating that patient to set them as MDM managed is not allowed.
patient.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM");
try {
myMdmHelper.doUpdateResource(person, true);
myMdmHelper.doUpdateResource(patient, true);
fail();
} catch (ForbiddenOperationException e) {
assertEquals("The HAPI-MDM tag on a resource may not be changed once created.", e.getMessage());
@ -189,8 +188,8 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
}
@Test
public void testMdmManagedPersonCannotBeModifiedByPersonUpdateRequest() throws InterruptedException {
// When MDM is enabled, only the MDM system is allowed to modify Person links of Persons with the MDM-MANAGED tag.
public void testMdmManagedGoldenResourceCannotBeModifiedByGoldenResourceUpdateRequest() throws InterruptedException {
// When MDM is enabled, only the MDM system is allowed to modify GoldenResource links of GoldenResources with the MDM-MANAGED tag.
Patient patient = new Patient();
IIdType patientId = myMdmHelper.createWithLatch(buildPaulPatient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless();
@ -223,7 +222,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
}
@Test
public void testWhenASingularPatientUpdatesExternalEidThatPersonEidIsUpdated() throws InterruptedException {
public void testWhenASingularPatientUpdatesExternalEidThatGoldenResourceEidIsUpdated() throws InterruptedException {
Patient jane = addExternalEID(buildJanePatient(), "some_eid");
MdmHelperR4.OutcomeAndLogMessageWrapper latch = myMdmHelper.createWithLatch(jane);
jane.setId(latch.getDaoMethodOutcome().getId());
@ -231,8 +230,8 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
jane = addExternalEID(jane, "some_new_eid");
MdmHelperR4.OutcomeAndLogMessageWrapper outcomeWrapper = myMdmHelper.updateWithLatch(jane);
IAnyResource person = getGoldenResourceFromTargetResource(jane);
List<CanonicalEID> externalEids = myEIDHelper.getExternalEid(person);
IAnyResource patient = getGoldenResourceFromTargetResource(jane);
List<CanonicalEID> externalEids = myEIDHelper.getExternalEid(patient);
assertThat(externalEids, hasSize(1));
assertThat("some_new_eid", is(equalTo(externalEids.get(0).getValue())));
}

View File

@ -11,13 +11,13 @@ import java.util.stream.Collectors;
/**
* A Matcher which allows us to check that a target patient/practitioner at a given link level.
* is linked to a set of patients/practitioners via a person.
* is linked to a set of patients/practitioners via a golden resource.
*
*/
public class IsLinkedTo extends BaseGoldenResourceMatcher {
private List<Long> baseResourcePersonPids;
private Long incomingResourcePersonPid;
private List<Long> baseResourceGoldenResourcePids;
private Long incomingResourceGoldenResourcePid;
protected IsLinkedTo(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
@ -26,16 +26,16 @@ public class IsLinkedTo extends BaseGoldenResourceMatcher {
@Override
protected boolean matchesSafely(IAnyResource theIncomingResource) {
incomingResourcePersonPid = getMatchedResourcePidFromResource(theIncomingResource);
incomingResourceGoldenResourcePid = getMatchedResourcePidFromResource(theIncomingResource);
//OK, lets grab all the person pids of the resources passed in via the constructor.
baseResourcePersonPids = myBaseResources.stream()
//OK, lets grab all the golden resource pids of the resources passed in via the constructor.
baseResourceGoldenResourcePids = myBaseResources.stream()
.map(this::getMatchedResourcePidFromResource)
.collect(Collectors.toList());
//The resources are linked if all person pids match the incoming person pid.
return baseResourcePersonPids.stream()
.allMatch(pid -> pid.equals(incomingResourcePersonPid));
//The resources are linked if all golden resource pids match the incoming golden resource pid.
return baseResourceGoldenResourcePids.stream()
.allMatch(pid -> pid.equals(incomingResourceGoldenResourcePid));
}
@Override

View File

@ -10,12 +10,12 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
import java.util.Optional;
public class IsMatchedToAPerson extends TypeSafeMatcher<IAnyResource> {
public class IsMatchedToAGoldenResource extends TypeSafeMatcher<IAnyResource> {
private final IdHelperService myIdHelperService;
private final MdmLinkDaoSvc myMdmLinkDaoSvc;
public IsMatchedToAPerson(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc) {
public IsMatchedToAGoldenResource(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc) {
myIdHelperService = theIdHelperService;
myMdmLinkDaoSvc = theMdmLinkDaoSvc;
}
@ -28,10 +28,10 @@ public class IsMatchedToAPerson extends TypeSafeMatcher<IAnyResource> {
@Override
public void describeTo(Description theDescription) {
theDescription.appendText("patient/practitioner was not linked to a Person.");
theDescription.appendText("target was not linked to a Golden Resource.");
}
public static Matcher<IAnyResource> matchedToAGoldenResource(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc) {
return new IsMatchedToAPerson(theIdHelperService, theMdmLinkDaoSvc);
return new IsMatchedToAGoldenResource(theIdHelperService, theMdmLinkDaoSvc);
}
}

View File

@ -13,11 +13,12 @@ import java.util.Optional;
import java.util.stream.Collectors;
public class IsPossibleDuplicateOf extends BaseGoldenResourceMatcher {
/**
* Matcher with tells us if there is an MdmLink with between these two resources that are considered POSSIBLE DUPLICATE.
* For use only on persons.
* For use only on GoldenResource.
*/
private Long incomingPersonPid;
private Long incomingGoldenResourcePid;
protected IsPossibleDuplicateOf(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
@ -25,20 +26,19 @@ public class IsPossibleDuplicateOf extends BaseGoldenResourceMatcher {
@Override
protected boolean matchesSafely(IAnyResource theIncomingResource) {
incomingGoldenResourcePid = getMatchedResourcePidFromResource(theIncomingResource);
incomingPersonPid = getMatchedResourcePidFromResource(theIncomingResource);
List<Long> personPidsToMatch = myBaseResources.stream()
List<Long> goldenResourcePidsToMatch = myBaseResources.stream()
.map(this::getMatchedResourcePidFromResource)
.collect(Collectors.toList());
//Returns true if there is a POSSIBLE_DUPLICATE between the incoming resource, and all of the resources passed in via the constructor.
return personPidsToMatch.stream()
return goldenResourcePidsToMatch.stream()
.map(baseResourcePid -> {
Optional<MdmLink> duplicateLink = myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(baseResourcePid, incomingPersonPid, MdmMatchResultEnum.POSSIBLE_DUPLICATE);
Optional<MdmLink> duplicateLink = myMdmLinkDaoSvc.getMdmLinksByGoldenResourcePidTargetPidAndMatchResult(baseResourcePid, incomingGoldenResourcePid, MdmMatchResultEnum.POSSIBLE_DUPLICATE);
if (!duplicateLink.isPresent()) {
duplicateLink = myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(incomingPersonPid, baseResourcePid, MdmMatchResultEnum.POSSIBLE_DUPLICATE);
duplicateLink = myMdmLinkDaoSvc.getMdmLinksByGoldenResourcePidTargetPidAndMatchResult(incomingGoldenResourcePid, baseResourcePid, MdmMatchResultEnum.POSSIBLE_DUPLICATE);
}
return duplicateLink;
}).allMatch(Optional::isPresent);
@ -46,7 +46,7 @@ public class IsPossibleDuplicateOf extends BaseGoldenResourceMatcher {
@Override
public void describeTo(Description theDescription) {
theDescription.appendText("Resource was not duplicate of Resource/" + incomingPersonPid);
theDescription.appendText("Resource was not duplicate of Resource/" + incomingGoldenResourcePid);
}
@Override

View File

@ -10,14 +10,13 @@ import java.util.List;
import java.util.stream.Collectors;
/**
* A Matcher which allows us to check that a target patient/practitioner at a given link level.
* is linked to a set of patients/practitioners via a person.
*
* A Matcher which allows us to check that a target resource at a given link level
* is linked to a set of target resources via a golden resource.
*/
public class IsPossibleLinkedTo extends BaseGoldenResourceMatcher {
private List<Long> baseResourcePersonPids;
private Long incomingResourcePersonPid;
private List<Long> baseResourceGoldenResourcePids;
private Long incomingResourceGoldenResourcePid;
protected IsPossibleLinkedTo(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theTargetResources) {
super(theIdHelperService, theMdmLinkDaoSvc, theTargetResources);
@ -25,16 +24,16 @@ public class IsPossibleLinkedTo extends BaseGoldenResourceMatcher {
@Override
protected boolean matchesSafely(IAnyResource theGoldenResource) {
incomingResourcePersonPid = myIdHelperService.getPidOrNull(theGoldenResource);
incomingResourceGoldenResourcePid = myIdHelperService.getPidOrNull(theGoldenResource);
//OK, lets grab all the person pids of the resources passed in via the constructor.
baseResourcePersonPids = myBaseResources.stream()
//OK, lets grab all the golden resource pids of the resources passed in via the constructor.
baseResourceGoldenResourcePids = myBaseResources.stream()
.flatMap(iBaseResource -> getPossibleMatchedGoldenResourcePidsFromTarget(iBaseResource).stream())
.collect(Collectors.toList());
//The resources are linked if all person pids match the incoming person pid.
return baseResourcePersonPids.stream()
.allMatch(pid -> pid.equals(incomingResourcePersonPid));
//The resources are linked if all golden resource pids match the incoming golden resource pid.
return baseResourceGoldenResourcePids.stream()
.allMatch(pid -> pid.equals(incomingResourceGoldenResourcePid));
}
@Override

View File

@ -25,13 +25,13 @@ public class IsPossibleMatchWith extends BaseGoldenResourceMatcher {
protected boolean matchesSafely(IAnyResource theIncomingResource) {
List<MdmLink> mdmLinks = getMdmLinksForTarget(theIncomingResource, MdmMatchResultEnum.POSSIBLE_MATCH);
List<Long> personPidsToMatch = myBaseResources.stream()
List<Long> goldenResourcePidsToMatch = myBaseResources.stream()
.map(this::getMatchedResourcePidFromResource)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (personPidsToMatch.isEmpty()) {
personPidsToMatch = myBaseResources.stream()
if (goldenResourcePidsToMatch.isEmpty()) {
goldenResourcePidsToMatch = myBaseResources.stream()
.flatMap(iBaseResource -> getPossibleMatchedGoldenResourcePidsFromTarget(iBaseResource).stream())
.collect(Collectors.toList());
}
@ -40,7 +40,7 @@ public class IsPossibleMatchWith extends BaseGoldenResourceMatcher {
.stream().map(MdmLink::getGoldenResourcePid)
.collect(Collectors.toList());
return mdmLinkGoldenResourcePids.containsAll(personPidsToMatch);
return mdmLinkGoldenResourcePids.containsAll(goldenResourcePidsToMatch);
}
@Override

View File

@ -30,6 +30,7 @@ import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.fail;
public class MdmProviderClearLinkR4Test extends BaseLinkR4Test {
protected Practitioner myPractitioner;
protected StringType myPractitionerId;
protected IAnyResource myPractitionerGoldenResource;
@ -78,29 +79,29 @@ public class MdmProviderClearLinkR4Test extends BaseLinkR4Test {
}
@Test
public void testPersonsWithMultipleHistoricalVersionsCanBeDeleted() {
public void testGoldenResourceWithMultipleHistoricalVersionsCanBeDeleted() {
createPatientAndUpdateLinks(buildJanePatient());
createPatientAndUpdateLinks(buildJanePatient());
createPatientAndUpdateLinks(buildJanePatient());
createPatientAndUpdateLinks(buildJanePatient());
Patient patientAndUpdateLinks = createPatientAndUpdateLinks(buildJanePatient());
IAnyResource person = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
assertThat(person, is(notNullValue()));
IAnyResource goldenResource = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
assertThat(goldenResource, is(notNullValue()));
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
assertNoPatientLinksExist();
person = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
assertThat(person, is(nullValue()));
goldenResource = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
assertThat(goldenResource, is(nullValue()));
}
@Test
public void testPersonWithLinksToOtherPersonsCanBeDeleted() {
public void testGoldenResourceWithLinksToOtherGoldenResourcesCanBeDeleted() {
createPatientAndUpdateLinks(buildJanePatient());
Patient patientAndUpdateLinks1 = createPatientAndUpdateLinks(buildJanePatient());
Patient patientAndUpdateLinks = createPatientAndUpdateLinks(buildPaulPatient());
IAnyResource personFromTarget = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
IAnyResource personFromTarget2 = getGoldenResourceFromTargetResource(patientAndUpdateLinks1);
linkPersons(personFromTarget, personFromTarget2);
IAnyResource goldenResourceFromTarget = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
IAnyResource goldenResourceFromTarget2 = getGoldenResourceFromTargetResource(patientAndUpdateLinks1);
linkGoldenResources(goldenResourceFromTarget, goldenResourceFromTarget2);
//SUT
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
@ -119,19 +120,19 @@ public class MdmProviderClearLinkR4Test extends BaseLinkR4Test {
}
@Test
public void testPersonsWithCircularReferenceCanBeCleared() {
public void testGoldenResourceWithCircularReferenceCanBeCleared() {
Patient patientAndUpdateLinks = createPatientAndUpdateLinks(buildPaulPatient());
Patient patientAndUpdateLinks1 = createPatientAndUpdateLinks(buildJanePatient());
Patient patientAndUpdateLinks2 = createPatientAndUpdateLinks(buildFrankPatient());
IAnyResource personFromTarget = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
IAnyResource personFromTarget1 = getGoldenResourceFromTargetResource(patientAndUpdateLinks1);
IAnyResource personFromTarget2 = getGoldenResourceFromTargetResource(patientAndUpdateLinks2);
IAnyResource goldenResourceFromTarget = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
IAnyResource goldenResourceFromTarget1 = getGoldenResourceFromTargetResource(patientAndUpdateLinks1);
IAnyResource goldenResourceFromTarget2 = getGoldenResourceFromTargetResource(patientAndUpdateLinks2);
// A -> B -> C -> A linkages.
linkPersons(personFromTarget, personFromTarget1);
linkPersons(personFromTarget1, personFromTarget2);
linkPersons(personFromTarget2, personFromTarget);
linkGoldenResources(goldenResourceFromTarget, goldenResourceFromTarget1);
linkGoldenResources(goldenResourceFromTarget1, goldenResourceFromTarget2);
linkGoldenResources(goldenResourceFromTarget2, goldenResourceFromTarget);
//SUT
Parameters parameters = myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
@ -145,7 +146,7 @@ public class MdmProviderClearLinkR4Test extends BaseLinkR4Test {
}
//TODO GGG unclear if we actually need to reimplement this.
private void linkPersons(IAnyResource theGoldenResource, IAnyResource theTargetResource) {
private void linkGoldenResources(IAnyResource theGoldenResource, IAnyResource theTargetResource) {
// TODO NG - Should be ok to leave this - not really
// throw new UnsupportedOperationException("We need to fix this!");
myMdmLinkDaoSvc.createOrUpdateLinkEntity(theGoldenResource, theTargetResource, POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));

View File

@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class MdmProviderMergePersonsR4Test extends BaseProviderR4Test {
public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
private Patient myFromGoldenPatient;
private StringType myFromGoldenPatientId;
@ -70,10 +70,10 @@ public class MdmProviderMergePersonsR4Test extends BaseProviderR4Test {
@Test
public void testUnmanagedMerge() {
StringType fromPersonId = new StringType(createPatient().getIdElement().getValue());
StringType toPersonId = new StringType(createPatient().getIdElement().getValue());
StringType fromGoldenResourceId = new StringType(createPatient().getIdElement().getValue());
StringType toGoldenResourceId = new StringType(createPatient().getIdElement().getValue());
try {
myMdmProviderR4.mergeGoldenResources(fromPersonId, toPersonId, myRequestDetails);
myMdmProviderR4.mergeGoldenResources(fromGoldenResourceId, toGoldenResourceId, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("Only MDM managed resources can be merged. MDM managed resources must have the HAPI-MDM tag.", e.getMessage());
@ -105,24 +105,31 @@ public class MdmProviderMergePersonsR4Test extends BaseProviderR4Test {
@Test
public void testBadParams() {
try {
myMdmProviderR4.mergeGoldenResources(new StringType("Person/1"), new StringType("Person/1"), myRequestDetails);
myMdmProviderR4.mergeGoldenResources(new StringType("Patient/1"), new StringType("Patient/1"), myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("fromPersonId must be different from toPersonId", e.getMessage());
assertEquals("fromGoldenResourceId must be different from toGoldenResourceId", e.getMessage());
}
try {
myMdmProviderR4.mergeGoldenResources(new StringType("Person/abc"), myToGoldenPatientId, myRequestDetails);
myMdmProviderR4.mergeGoldenResources(new StringType("Patient/abc"), myToGoldenPatientId, myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Person/abc is not known", e.getMessage());
assertEquals("Resource Patient/abc is not known", e.getMessage());
}
try {
myMdmProviderR4.mergeGoldenResources(myFromGoldenPatientId, new StringType("Person/abc"), myRequestDetails);
myMdmProviderR4.mergeGoldenResources(new StringType("Organization/abc"), myToGoldenPatientId, myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Person/abc is not known", e.getMessage());
assertEquals("Resource Organization/abc is not known", e.getMessage());
}
try {
myMdmProviderR4.mergeGoldenResources(myFromGoldenPatientId, new StringType("Patient/abc"), myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Patient/abc is not known", e.getMessage());
}
}
}

View File

@ -29,8 +29,8 @@ import static org.junit.jupiter.api.Assertions.fail;
public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(MdmProviderQueryLinkR4Test.class);
private StringType myLinkSource;
private StringType myPerson1Id;
private StringType myPerson2Id;
private StringType myGoldenResource1Id;
private StringType myGoldenResource2Id;
@Override
@BeforeEach
@ -43,10 +43,10 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
// Add a possible duplicate
myLinkSource = new StringType(MdmLinkSourceEnum.AUTO.name());
Patient sourcePatient1 = createGoldenPatient();
myPerson1Id = new StringType(sourcePatient1.getIdElement().toVersionless().getValue());
myGoldenResource1Id = new StringType(sourcePatient1.getIdElement().toVersionless().getValue());
Long sourcePatient1Pid = myIdHelperService.getPidOrNull(sourcePatient1);
Patient sourcePatient2 = createGoldenPatient();
myPerson2Id = new StringType(sourcePatient2.getIdElement().toVersionless().getValue());
myGoldenResource2Id = new StringType(sourcePatient2.getIdElement().toVersionless().getValue());
Long sourcePatient2Pid = myIdHelperService.getPidOrNull(sourcePatient2);
MdmLink possibleDuplicateMdmLink = myMdmLinkDaoSvc.newMdmLink().setGoldenResourcePid(sourcePatient1Pid).setTargetPid(sourcePatient2Pid).setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(MdmLinkSourceEnum.AUTO);
@ -69,15 +69,15 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
// Add a third patient
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
IdType patientId = patient.getIdElement().toVersionless();
IAnyResource person = getGoldenResourceFromTargetResource(patient);
IIdType personId = person.getIdElement().toVersionless();
IAnyResource goldenResource = getGoldenResourceFromTargetResource(patient);
IIdType goldenResourceId = goldenResource.getIdElement().toVersionless();
Parameters result = myMdmProviderR4.queryLinks(null, null, null, myLinkSource, myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(3));
List<Parameters.ParametersParameterComponent> part = list.get(2).getPart();
assertMdmLink(7, part, personId.getValue(), patientId.getValue(), MdmMatchResultEnum.MATCH, "false", "false", "2");
assertMdmLink(7, part, goldenResourceId.getValue(), patientId.getValue(), MdmMatchResultEnum.MATCH, "false", "false", "2");
}
@Test
@ -87,7 +87,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1));
List<Parameters.ParametersParameterComponent> part = list.get(0).getPart();
assertMdmLink(2, part, myPerson1Id.getValue(), myPerson2Id.getValue(), MdmMatchResultEnum.POSSIBLE_DUPLICATE, "false", "false", null);
assertMdmLink(2, part, myGoldenResource1Id.getValue(), myGoldenResource2Id.getValue(), MdmMatchResultEnum.POSSIBLE_DUPLICATE, "false", "false", null);
}
@Test
@ -98,7 +98,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
assertThat(list, hasSize(1));
}
{
Parameters result = myMdmProviderR4.notDuplicate(myPerson1Id, myPerson2Id, myRequestDetails);
Parameters result = myMdmProviderR4.notDuplicate(myGoldenResource1Id, myGoldenResource2Id, myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
assertEquals("success", result.getParameterFirstRep().getName());
assertTrue(((BooleanType) (result.getParameterFirstRep().getValue())).booleanValue());
@ -111,17 +111,17 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test
public void testNotDuplicateBadId() {
try {
myMdmProviderR4.notDuplicate(myPerson1Id, new StringType("Person/notAnId123"), myRequestDetails);
myMdmProviderR4.notDuplicate(myGoldenResource1Id, new StringType("Patient/notAnId123"), myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Person/notAnId123 is not known", e.getMessage());
assertEquals("Resource Patient/notAnId123 is not known", e.getMessage());
}
}
private void assertMdmLink(int theExpectedSize, List<Parameters.ParametersParameterComponent> thePart, String thePersonId, String theTargetId, MdmMatchResultEnum theMatchResult, String theEidMatch, String theNewPerson, String theScore) {
private void assertMdmLink(int theExpectedSize, List<Parameters.ParametersParameterComponent> thePart, String theGoldenResourceId, String theTargetId, MdmMatchResultEnum theMatchResult, String theEidMatch, String theNewGoldenResource, String theScore) {
assertThat(thePart, hasSize(theExpectedSize));
assertThat(thePart.get(0).getName(), is("goldenResourceId"));
assertThat(thePart.get(0).getValue().toString(), is(removeVersion(thePersonId)));
assertThat(thePart.get(0).getValue().toString(), is(removeVersion(theGoldenResourceId)));
assertThat(thePart.get(1).getName(), is("targetResourceId"));
assertThat(thePart.get(1).getValue().toString(), is(removeVersion(theTargetId)));
if (theExpectedSize > 2) {
@ -134,7 +134,7 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
assertThat(thePart.get(4).getValue().primitiveValue(), is(theEidMatch));
assertThat(thePart.get(5).getName(), is("hadToCreateNewResource"));
assertThat(thePart.get(5).getValue().primitiveValue(), is(theNewPerson));
assertThat(thePart.get(5).getValue().primitiveValue(), is(theNewGoldenResource));
assertThat(thePart.get(6).getName(), is("score"));
assertThat(thePart.get(6).getValue().primitiveValue(), is(theScore));

View File

@ -140,10 +140,10 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
}
@Test
public void testUpdateStrangePerson() {
Patient person = createPatient();
public void testUpdateStrangePatient() {
Patient patient = createPatient();
try {
myMdmProviderR4.updateLink(new StringType(person.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(new StringType(patient.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
String expectedMessage = myMessageHelper.getMessageForUnmanagedResource();
@ -152,7 +152,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
}
@Test
public void testExcludedPerson() {
public void testExcludedGoldenResource() {
Patient patient = new Patient();
patient.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_NO_MDM_MANAGED);
createPatient(patient);

View File

@ -37,7 +37,8 @@ import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
public class MdmGoldenResourceMergerSvcTest extends BaseMdmR4Test {
public static final String GIVEN_NAME = "Jenn";
public static final String FAMILY_NAME = "Chan";
public static final String POSTAL_CODE = "M6G 1B4";
@ -114,7 +115,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
}
private MdmTransactionContext createMdmContext() {
MdmTransactionContext mdmTransactionContext = new MdmTransactionContext(TransactionLogMessages.createFromTransactionGuid(UUID.randomUUID().toString()), MdmTransactionContext.OperationType.MERGE_PERSONS);
MdmTransactionContext mdmTransactionContext = new MdmTransactionContext(TransactionLogMessages.createFromTransactionGuid(UUID.randomUUID().toString()), MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES);
mdmTransactionContext.setResourceType("Patient");
return mdmTransactionContext;
}
@ -149,7 +150,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
@Test
public void fullFromEmptyTo() {
populatePerson(myFromGoldenPatient);
populatePatient(myFromGoldenPatient);
Patient mergedSourcePatient = mergeGoldenPatients();
// TODO NG - Revisit when rules are ready
@ -162,7 +163,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
@Test
public void emptyFromFullTo() {
myFromGoldenPatient.getName().add(new HumanName().addGiven(BAD_GIVEN_NAME));
populatePerson(myToGoldenPatient);
populatePatient(myToGoldenPatient);
Patient mergedSourcePatient = mergeGoldenPatients();
HumanName returnedName = mergedSourcePatient.getNameFirstRep();
@ -176,7 +177,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
createMdmLink(myFromGoldenPatient, myTargetPatient1);
Patient mergedGoldenPatient = mergeGoldenPatients();
List<MdmLink> links = getNonRedirectLinksByPerson(mergedGoldenPatient);
List<MdmLink> links = getNonRedirectLinksByGoldenResource(mergedGoldenPatient);
assertEquals(1, links.size());
assertThat(mergedGoldenPatient, is(possibleLinkedTo(myTargetPatient1)));
}
@ -186,7 +187,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
createMdmLink(myToGoldenPatient, myTargetPatient1);
Patient mergedSourcePatient = mergeGoldenPatients();
List<MdmLink> links = getNonRedirectLinksByPerson(mergedSourcePatient);
List<MdmLink> links = getNonRedirectLinksByGoldenResource(mergedSourcePatient);
assertEquals(1, links.size());
assertThat(mergedSourcePatient, is(possibleLinkedTo(myTargetPatient1)));
}
@ -201,12 +202,12 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
createMdmLink(myToGoldenPatient, myTargetPatient1);
mergeGoldenPatients();
List<MdmLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
List<MdmLink> links = getNonRedirectLinksByGoldenResource(myToGoldenPatient);
assertEquals(1, links.size());
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
}
private List<MdmLink> getNonRedirectLinksByPerson(Patient theGoldenPatient) {
private List<MdmLink> getNonRedirectLinksByGoldenResource(Patient theGoldenPatient) {
return myMdmLinkDaoSvc.findMdmLinksByGoldenResource(theGoldenPatient).stream()
.filter(link -> !link.isRedirect())
.collect(Collectors.toList());
@ -223,7 +224,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
createMdmLink(myToGoldenPatient, myTargetPatient1);
mergeGoldenPatients();
List<MdmLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
List<MdmLink> links = getNonRedirectLinksByGoldenResource(myToGoldenPatient);
assertEquals(1, links.size());
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
@ -239,7 +240,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
saveLink(toLink);
mergeGoldenPatients();
List<MdmLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
List<MdmLink> links = getNonRedirectLinksByGoldenResource(myToGoldenPatient);
assertEquals(1, links.size());
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
@ -434,7 +435,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
return myMdmLinkDaoSvc.createOrUpdateLinkEntity(theSourcePatient, theTargetPatient, POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
}
private void populatePerson(Patient theSourcePatient) {
private void populatePatient(Patient theSourcePatient) {
theSourcePatient.addName(new HumanName().addGiven(GIVEN_NAME).setFamily(FAMILY_NAME));
theSourcePatient.setGender(Enumerations.AdministrativeGender.FEMALE);
theSourcePatient.setBirthDateElement(new DateType("1981-01-01"));

View File

@ -95,8 +95,8 @@ public class MdmLinkSvcTest extends BaseMdmR4Test {
saveNoMatchLink(goldenPatient1Pid, goldenPatient2Pid);
myMdmLinkSvc.updateLink(goldenPatient1, goldenPatient2, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Person"));
assertFalse(myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(goldenPatient1Pid, goldenPatient2Pid, MdmMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
myMdmLinkSvc.updateLink(goldenPatient1, goldenPatient2, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
assertFalse(myMdmLinkDaoSvc.getMdmLinksByGoldenResourcePidTargetPidAndMatchResult(goldenPatient1Pid, goldenPatient2Pid, MdmMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
assertLinkCount(1);
}
@ -113,8 +113,8 @@ public class MdmLinkSvcTest extends BaseMdmR4Test {
saveNoMatchLink(goldenPatient2Pid, goldenPatient1Pid);
myMdmLinkSvc.updateLink(goldenPatient1, goldenPatient2, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Person"));
assertFalse(myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(goldenPatient1Pid, goldenPatient2Pid, MdmMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
myMdmLinkSvc.updateLink(goldenPatient1, goldenPatient2, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
assertFalse(myMdmLinkDaoSvc.getMdmLinksByGoldenResourcePidTargetPidAndMatchResult(goldenPatient1Pid, goldenPatient2Pid, MdmMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
assertLinkCount(1);
}
@ -135,7 +135,7 @@ public class MdmLinkSvcTest extends BaseMdmR4Test {
myMdmLinkSvc.updateLink(goldenPatient, patient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
try {
myMdmLinkSvc.updateLink(goldenPatient, patient, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, null);
myMdmLinkSvc.updateLink(goldenPatient, patient, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, null);
fail();
} catch (InternalErrorException e) {
assertThat(e.getMessage(), is(equalTo("MDM system is not allowed to modify links on manually created links")));
@ -165,7 +165,7 @@ public class MdmLinkSvcTest extends BaseMdmR4Test {
Patient patient2 = createPatient(buildJanePatient());
assertEquals(0, myMdmLinkDao.count());
myMdmLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient1, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myMdmLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient1, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myMdmLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient2, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
List<MdmLink> targets = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(goldenPatient);

View File

@ -43,15 +43,16 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
}
@Test
public void testIncomingPatientWithEIDThatMatchesPersonWithHapiEidAddsExternalEidsToPerson() {
// Existing Person with system-assigned EID found linked from matched Patient. incoming Patient has EID. Replace Person system-assigned EID with Patient EID.
public void testIncomingPatientWithEIDThatMatchesGoldenResourceWithHapiEidAddsExternalEidsToGoldenResource() {
// Existing GoldenResource with system-assigned EID found linked from matched Patient. incoming Patient has EID.
// Replace GoldenResource system-assigned EID with Patient EID.
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
assertLinksMatchResult(MATCH);
assertLinksCreatedNewResource(true);
assertLinksMatchedByEid(false);
IAnyResource janePerson = getGoldenResourceFromTargetResource(patient);
List<CanonicalEID> hapiEid = myEidHelper.getHapiEid(janePerson);
IAnyResource janeGoldenResource = getGoldenResourceFromTargetResource(patient);
List<CanonicalEID> hapiEid = myEidHelper.getHapiEid(janeGoldenResource);
String foundHapiEid = hapiEid.get(0).getValue();
Patient janePatient = buildJanePatient();
@ -62,7 +63,7 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
assertLinksCreatedNewResource(true, false);
assertLinksMatchedByEid(false, false);
//We want to make sure the patients were linked to the same person.
//We want to make sure the patients were linked to the same GoldenResource.
assertThat(patient, is(sameGoldenResourceAs(janePatient)));
Patient sourcePatient = (Patient) getGoldenResourceFromTargetResource(patient);
@ -111,9 +112,9 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
clearExternalEIDs(patient2);
addExternalEID(patient2, "id_6");
//At this point, there should be 5 EIDs on the person
Patient personFromTarget = (Patient) getGoldenResourceFromTargetResource(patient2);
assertThat(personFromTarget.getIdentifier(), hasSize(5));
//At this point, there should be 5 EIDs on the GoldenResource
Patient patientFromTarget = (Patient) getGoldenResourceFromTargetResource(patient2);
assertThat(patientFromTarget.getIdentifier(), hasSize(5));
updatePatientAndUpdateLinks(patient2);
assertLinksMatchResult(MATCH, MATCH);
@ -122,13 +123,12 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
assertThat(patient1, is(sameGoldenResourceAs(patient2)));
personFromTarget = (Patient) getGoldenResourceFromTargetResource(patient2);
assertThat(personFromTarget.getIdentifier(), hasSize(6));
patientFromTarget = (Patient) getGoldenResourceFromTargetResource(patient2);
assertThat(patientFromTarget.getIdentifier(), hasSize(6));
}
@Test
public void testDuplicatePersonLinkIsCreatedWhenAnIncomingPatientArrivesWithEIDThatMatchesAnotherEIDPatient() {
public void testDuplicateGoldenResourceLinkIsCreatedWhenAnIncomingPatientArrivesWithEIDThatMatchesAnotherEIDPatient() {
Patient patient1 = buildJanePatient();
addExternalEID(patient1, "eid-1");
addExternalEID(patient1, "eid-11");
@ -153,7 +153,7 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
.map(myIdHelperService::getPidOrNull)
.collect(Collectors.toList());
//The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE MdmLink.
//The two GoldenResources related to the patients should both show up in the only existing POSSIBLE_DUPLICATE MdmLink.
MdmLink mdmLink = possibleDuplicates.get(0);
assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
assertThat(mdmLink.getTargetPid(), is(in(duplicatePids)));
@ -161,7 +161,7 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
@Test
// Test Case #5
public void testWhenPatientEidUpdateWouldCauseALinkChangeThatDuplicatePersonIsCreatedInstead() {
public void testWhenPatientEidUpdateWouldCauseALinkChangeThatDuplicateGoldenResourceIsCreatedInstead() {
Patient patient1 = buildJanePatient();
addExternalEID(patient1, "eid-1");
addExternalEID(patient1, "eid-11");
@ -185,12 +185,12 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
assertLinksCreatedNewResource(true, true, false);
assertLinksMatchedByEid(false, false, true);
//Now, Patient 2 and 3 are linked, and the person has 2 eids.
//Now, Patient 2 and 3 are linked, and the GoldenResource has 2 eids.
assertThat(patient2, is(sameGoldenResourceAs(patient3)));
//Now lets change one of the EIDs on the second patient to one that matches our original patient.
//This should create a situation in which the incoming EIDs are matched to _two_ different persons. In this case, we want to
//set them all to possible_match, and set the two persons as possible duplicates.
//This should create a situation in which the incoming EIDs are matched to _two_ different GoldenResources. In this case, we want to
//set them all to possible_match, and set the two GoldenResources as possible duplicates.
patient2.getIdentifier().clear();
addExternalEID(patient2, "eid-11");
addExternalEID(patient2, "eid-22");

View File

@ -63,7 +63,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testAddPatientLinksToNewPersonIfNoneFound() {
public void testAddPatientLinksToNewGoldenResourceIfNoneFound() {
createPatientAndUpdateLinks(buildJanePatient());
assertLinkCount(1);
assertLinksMatchResult(MATCH);
@ -97,7 +97,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testAddPatientLinksToExistingPersonIfMatch() {
public void testAddPatientLinksToExistingGoldenResourceIfMatch() {
Patient patient1 = createPatientAndUpdateLinks(buildJanePatient());
assertLinkCount(1);
@ -111,18 +111,18 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testWhenMatchOccursOnPersonThatHasBeenManuallyNOMATCHedThatItIsBlocked() {
public void testWhenMatchOccursOnGoldenResourceThatHasBeenManuallyNOMATCHedThatItIsBlocked() {
Patient originalJane = createPatientAndUpdateLinks(buildJanePatient());
IAnyResource janePerson = getGoldenResourceFromTargetResource(originalJane);
IAnyResource janeGoldenResource = getGoldenResourceFromTargetResource(originalJane);
//Create a manual NO_MATCH between janePerson and unmatchedJane.
//Create a manual NO_MATCH between janeGoldenResource and unmatchedJane.
Patient unmatchedJane = createPatient(buildJanePatient());
myMdmLinkSvc.updateLink(janePerson, unmatchedJane, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myMdmLinkSvc.updateLink(janeGoldenResource, unmatchedJane, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
//rerun MDM rules against unmatchedJane.
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(unmatchedJane, createContextForCreate("Patient"));
assertThat(unmatchedJane, is(not(sameGoldenResourceAs(janePerson))));
assertThat(unmatchedJane, is(not(sameGoldenResourceAs(janeGoldenResource))));
assertThat(unmatchedJane, is(not(linkedTo(originalJane))));
assertLinksMatchResult(MATCH, NO_MATCH, MATCH);
@ -131,23 +131,23 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testWhenPOSSIBLE_MATCHOccursOnPersonThatHasBeenManuallyNOMATCHedThatItIsBlocked() {
public void testWhenPOSSIBLE_MATCHOccursOnGoldenResourceThatHasBeenManuallyNOMATCHedThatItIsBlocked() {
Patient originalJane = createPatientAndUpdateLinks(buildJanePatient());
IBundleProvider search = myPatientDao.search(buildGoldenRecordSearchParameterMap());
IAnyResource janePerson = (IAnyResource) search.getResources(0, 1).get(0);
IAnyResource janeGoldenResource = (IAnyResource) search.getResources(0, 1).get(0);
Patient unmatchedPatient = createPatient(buildJanePatient());
// This simulates an admin specifically saying that unmatchedPatient does NOT match janePerson.
myMdmLinkSvc.updateLink(janePerson, unmatchedPatient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
// This simulates an admin specifically saying that unmatchedPatient does NOT match janeGoldenResource.
myMdmLinkSvc.updateLink(janeGoldenResource, unmatchedPatient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
// TODO change this so that it will only partially match.
//Now normally, when we run update links, it should link to janePerson. However, this manual NO_MATCH link
//should cause a whole new Person to be created.
//Now normally, when we run update links, it should link to janeGoldenResource. However, this manual NO_MATCH link
//should cause a whole new GoldenResource to be created.
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(unmatchedPatient, createContextForCreate("Patient"));
assertThat(unmatchedPatient, is(not(sameGoldenResourceAs(janePerson))));
assertThat(unmatchedPatient, is(not(sameGoldenResourceAs(janeGoldenResource))));
assertThat(unmatchedPatient, is(not(linkedTo(originalJane))));
assertLinksMatchResult(MATCH, NO_MATCH, MATCH);
@ -156,7 +156,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testWhenPatientIsCreatedWithEIDThatItPropagatesToNewPerson() {
public void testWhenPatientIsCreatedWithEIDThatItPropagatesToNewGoldenResource() {
String sampleEID = "sample-eid";
Patient janePatient = addExternalEID(buildJanePatient(), sampleEID);
janePatient = createPatientAndUpdateLinks(janePatient);
@ -172,7 +172,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testWhenPatientIsCreatedWithoutAnEIDThePersonGetsAutomaticallyAssignedOne() {
public void testWhenPatientIsCreatedWithoutAnEIDTheGoldenResourceGetsAutomaticallyAssignedOne() {
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
MdmLink mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong()).get();
@ -183,7 +183,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testPatientAttributesAreCopiedOverWhenPersonIsCreatedFromPatient() {
public void testPatientAttributesAreCopiedOverWhenGoldenResourceIsCreatedFromPatient() {
Patient patient = createPatientAndUpdateLinks(buildPatientWithNameIdAndBirthday("Gary", "GARY_ID", new Date()));
Optional<MdmLink> mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong());
@ -199,25 +199,26 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testPatientMatchingAnotherPatientLinksToSamePerson() {
public void testPatientMatchingAnotherPatientLinksToSameGoldenResource() {
Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
Patient sameJanePatient = createPatientAndUpdateLinks(buildJanePatient());
assertThat(janePatient, is(sameGoldenResourceAs(sameJanePatient)));
}
@Test
public void testIncomingPatientWithEIDThatMatchesPersonWithHapiEidAddsExternalEidToPerson() {
// Existing Person with system-assigned EID found linked from matched Patient. incoming Patient has EID. Replace Person system-assigned EID with Patient EID.
public void testIncomingPatientWithEIDThatMatchesGoldenResourceWithHapiEidAddsExternalEidToGoldenResource() {
// Existing GoldenResource with system-assigned EID found linked from matched Patient. incoming Patient has EID.
// Replace GoldenResource system-assigned EID with Patient EID.
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
IAnyResource janePerson = getGoldenResourceFromTargetResource(patient);
List<CanonicalEID> hapiEid = myEidHelper.getHapiEid(janePerson);
IAnyResource janeGoldenResource = getGoldenResourceFromTargetResource(patient);
List<CanonicalEID> hapiEid = myEidHelper.getHapiEid(janeGoldenResource);
String foundHapiEid = hapiEid.get(0).getValue();
Patient janePatient = addExternalEID(buildJanePatient(), "12345");
createPatientAndUpdateLinks(janePatient);
//We want to make sure the patients were linked to the same person.
//We want to make sure the patients were linked to the same Golden Resource.
assertThat(patient, is(sameGoldenResourceAs(janePatient)));
Patient sourcePatient = (Patient) getGoldenResourceFromTargetResource(patient);
@ -267,7 +268,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testDuplicatePersonLinkIsCreatedWhenAnIncomingPatientArrivesWithEIDThatMatchesAnotherEIDPatient() {
public void testDuplicateGoldenResourceLinkIsCreatedWhenAnIncomingPatientArrivesWithEIDThatMatchesAnotherEIDPatient() {
Patient patient1 = addExternalEID(buildJanePatient(), "eid-1");
patient1 = createPatientAndUpdateLinks(patient1);
@ -284,7 +285,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
.map(myIdHelperService::getPidOrNull)
.collect(Collectors.toList());
//The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE MdmLink.
//The two GoldenResources related to the patients should both show up in the only existing POSSIBLE_DUPLICATE MdmLink.
MdmLink mdmLink = possibleDuplicates.get(0);
assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
assertThat(mdmLink.getTargetPid(), is(in(duplicatePids)));
@ -318,9 +319,9 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testWhenThereAreNoMATCHOrPOSSIBLE_MATCHOutcomesThatANewPersonIsCreated() {
public void testWhenThereAreNoMATCHOrPOSSIBLE_MATCHOutcomesThatANewGoldenResourceIsCreated() {
/**
* CASE 1: No MATCHED and no PROBABLE_MATCHED outcomes -> a new Person resource
* CASE 1: No MATCHED and no PROBABLE_MATCHED outcomes -> a new GoldenResource resource
* is created and linked to that Pat/Prac.
*/
assertLinkCount(0);
@ -330,10 +331,10 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testWhenAllMATCHResultsAreToSamePersonThatTheyAreLinked() {
public void testWhenAllMATCHResultsAreToSameGoldenResourceThatTheyAreLinked() {
/**
* CASE 2: All of the MATCHED Pat/Prac resources are already linked to the same Person ->
* a new Link is created between the new Pat/Prac and that Person and is set to MATCHED.
* CASE 2: All of the MATCHED Pat/Prac resources are already linked to the same GoldenResource ->
* a new Link is created between the new Pat/Prac and that GoldenResource and is set to MATCHED.
*/
Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
Patient janePatient2 = createPatientAndUpdateLinks(buildJanePatient());
@ -347,27 +348,27 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testMATCHResultWithMultipleCandidatesCreatesPOSSIBLE_DUPLICATELinksAndNoPersonIsCreated() {
public void testMATCHResultWithMultipleCandidatesCreatesPOSSIBLE_DUPLICATELinksAndNoGoldenResourceIsCreated() {
/**
* CASE 3: The MATCHED Pat/Prac resources link to more than one Person -> Mark all links as POSSIBLE_MATCH.
* All other Person resources are marked as POSSIBLE_DUPLICATE of this first Person.
* CASE 3: The MATCHED Pat/Prac resources link to more than one GoldenResource -> Mark all links as POSSIBLE_MATCH.
* All other GoldenResource resources are marked as POSSIBLE_DUPLICATE of this first GoldenResource.
*/
Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
Patient janePatient2 = createPatient(buildJanePatient());
//In a normal situation, janePatient2 would just match to jane patient, but here we need to hack it so they are their
//own individual Persons for the purpose of this test.
IAnyResource person = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(janePatient2);
myMdmLinkSvc.updateLink(person, janePatient2, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
//own individual GoldenResource for the purpose of this test.
IAnyResource goldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(janePatient2);
myMdmLinkSvc.updateLink(goldenResource, janePatient2, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
assertThat(janePatient, is(not(sameGoldenResourceAs(janePatient2))));
//In theory, this will match both Persons!
//In theory, this will match both GoldenResources!
Patient incomingJanePatient = createPatientAndUpdateLinks(buildJanePatient());
//There should now be a single POSSIBLE_DUPLICATE link with
assertThat(janePatient, is(possibleDuplicateOf(janePatient2)));
//There should now be 2 POSSIBLE_MATCH links with this person.
//There should now be 2 POSSIBLE_MATCH links with this goldenResource.
assertThat(incomingJanePatient, is(possibleMatchWith(janePatient, janePatient2)));
//Ensure there is no successful MATCH links for incomingJanePatient
@ -384,7 +385,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
public void testWhenAllMatchResultsArePOSSIBLE_MATCHThattheyAreLinkedAndNoGoldenResourceIsCreated() {
/**
* CASE 4: Only POSSIBLE_MATCH outcomes -> In this case, mdm-link records are created with POSSIBLE_MATCH
* outcome and await manual assignment to either NO_MATCH or MATCHED. Person link is added.
* outcome and await manual assignment to either NO_MATCH or MATCHED. GoldenResource link is added.
*/
Patient patient = buildJanePatient();
patient.getNameFirstRep().setFamily("familyone");
@ -469,7 +470,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
//Case #1
@Test
public void testPatientUpdateOverwritesPersonDataOnChanges() {
public void testPatientUpdateOverwritesGoldenResourceDataOnChanges() {
Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
Patient janeSourcePatient = (Patient) getGoldenResourceFromTargetResource(janePatient);
@ -480,7 +481,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
assertThat(janeSourcePatient, is(sameGoldenResourceAs(janePaulPatient)));
//Ensure the related person was updated with new info.
//Ensure the related GoldenResource was updated with new info.
Patient sourcePatientFromTarget = (Patient) getGoldenResourceFromTargetResource(janePaulPatient);
HumanName nameFirstRep = sourcePatientFromTarget.getNameFirstRep();
@ -489,7 +490,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testPatientCreateDoesNotOverwritePersonAttributesThatAreInvolvedInLinking() {
public void testPatientCreateDoesNotOverwriteGoldenResourceAttributesThatAreInvolvedInLinking() {
Patient paul = buildPaulPatient();
paul.setGender(Enumerations.AdministrativeGender.MALE);
paul = createPatientAndUpdateLinks(paul);
@ -505,14 +506,15 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
assertThat(paul2, is(sameGoldenResourceAs(paul)));
//Newly matched patients aren't allowed to overwrite Person Attributes unless they are empty, so gender should still be set to male.
Patient paul2Person = (Patient) getGoldenResourceFromTargetResource(paul2);
// assertThat(paul2Person.getGender(), is(equalTo(Enumerations.AdministrativeGender.MALE)));
//Newly matched patients aren't allowed to overwrite GoldenResource Attributes unless they are empty,
// so gender should still be set to male.
Patient paul2GoldenResource = (Patient) getGoldenResourceFromTargetResource(paul2);
// assertThat(paul2GoldenResource.getGender(), is(equalTo(Enumerations.AdministrativeGender.MALE)));
}
@Test
//Test Case #1
public void testPatientUpdatesOverwritePersonData() {
public void testPatientUpdatesOverwriteGoldenResourceData() {
Patient paul = buildPaulPatient();
String incorrectBirthdate = "1980-06-27";
paul.getBirthDateElement().setValueAsString(incorrectBirthdate);
@ -554,7 +556,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
}
@Test
public void testSinglyLinkedPersonThatGetsAnUpdatedEidSimplyUpdatesEID() {
public void testSinglyLinkedGoldenResourceThatGetsAnUpdatedEidSimplyUpdatesEID() {
//Use Case # 2
String EID_1 = "123";
String EID_2 = "456";
@ -597,18 +599,18 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
addExternalEID(patient3, "eid-2");
patient3 = createPatientAndUpdateLinks(patient3);
//Now, Patient 2 and 3 are linked, and the person has 2 eids.
//Now, Patient 2 and 3 are linked, and the GoldenResource has 2 eids.
assertThat(patient2, is(sameGoldenResourceAs(patient3)));
assertNoDuplicates();
// Person A -> {P1}
// Person B -> {P2, P3}
// GoldenResource A -> {P1}
// GoldenResource B -> {P2, P3}
patient2.getIdentifier().clear();
addExternalEID(patient2, "eid-1");
patient2 = updatePatientAndUpdateLinks(patient2);
// Person A -> {P1, P2}
// Person B -> {P3}
// GoldenResource A -> {P1, P2}
// GoldenResource B -> {P3}
// Possible duplicates A<->B
assertThat(patient2, is(sameGoldenResourceAs(patient1)));

View File

@ -35,13 +35,13 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
MdmUtil.setGoldenResourceRedirected(badSourcePatient);
myPatientDao.update(badSourcePatient);
Optional<IAnyResource> foundPerson = myResourceDaoSvc.searchGoldenResourceByEID(TEST_EID, "Patient");
assertTrue(foundPerson.isPresent());
assertThat(foundPerson.get().getIdElement().toUnqualifiedVersionless().getValue(), is(goodSourcePatient.getIdElement().toUnqualifiedVersionless().getValue()));
Optional<IAnyResource> foundGoldenResource = myResourceDaoSvc.searchGoldenResourceByEID(TEST_EID, "Patient");
assertTrue(foundGoldenResource.isPresent());
assertThat(foundGoldenResource.get().getIdElement().toUnqualifiedVersionless().getValue(), is(goodSourcePatient.getIdElement().toUnqualifiedVersionless().getValue()));
}
@Test
public void testSearchPersonByEidExcludesNonMdmManaged() {
public void testSearcGoldenResourceByEidExcludesNonMdmManaged() {
Patient goodSourcePatient = addExternalEID(createGoldenPatient(), TEST_EID);
myPatientDao.update(goodSourcePatient);

View File

@ -25,10 +25,11 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
public interface IGoldenResourceMergerSvc {
/**
* Move all links from the theFromGoldenResource to theToGoldenResource and then set active=false on theFromGoldenResource. Merge all Person
* fields.
* @param theFromGoldenResource the person we are merging from
* @param theToGoldenResource the person we are merging to
* Move all links from the theFromGoldenResource to theToGoldenResource and then set active=false on theFromGoldenResource.
* Merge all Golden Resource fields subject to survivorship rules.
*
* @param theFromGoldenResource the golden resource we are merging from
* @param theToGoldenResource the golden resource we are merging to
* @return updated theToGoldenResource with the merged fields and links.
*/
IAnyResource mergeGoldenResources(IAnyResource theFromGoldenResource, IAnyResource theToGoldenResource, MdmTransactionContext theMdmTransactionContext);

View File

@ -28,13 +28,13 @@ import java.util.stream.Stream;
public interface IMdmControllerSvc {
Stream<MdmLinkJson> queryLinks(@Nullable String thePersonId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext);
Stream<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext);
Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext);
void notDuplicateGoldenResource(String thePersonId, String theTargetPersonId, MdmTransactionContext theMdmTransactionContext);
void notDuplicateGoldenResource(String theGoldenResourceId, String theTargetGoldenResourceId, MdmTransactionContext theMdmTransactionContext);
IAnyResource mergeGoldenResources(String theFromPersonId, String theToPersonId, MdmTransactionContext theMdmTransactionContext);
IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, MdmTransactionContext theMdmTransactionContext);
IAnyResource updateLink(String thePersonId, String theTargetId, String theMatchResult, MdmTransactionContext theMdmTransactionContext);
IAnyResource updateLink(String theGoldenResourceId, String theTargetId, String theMatchResult, MdmTransactionContext theMdmTransactionContext);
}

View File

@ -25,18 +25,16 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
public interface IMdmExpungeSvc {
/**
* Given a resource type, delete the underlying MDM links, and their related person objects.
* Given a resource type, delete the underlying MDM links, and their related golden resource objects.
*
* @param theResourceType The type of resources
*
* @param theRequestDetails
* @return the count of deleted MDM links
*/
long expungeAllMdmLinksOfTargetType(String theResourceType, ServletRequestDetails theRequestDetails);
/**
* Delete all MDM links, and their related Person objects.
*
* Delete all MDM links, and their related golden resource objects.
*
* @return the count of deleted MDM links
* @param theRequestDetails

View File

@ -30,5 +30,5 @@ import java.util.stream.Stream;
*/
public interface IMdmLinkQuerySvc {
Stream<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext);
Stream<MdmLinkJson> getDuplicatePersons(MdmTransactionContext theMdmContext);
Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext);
}

View File

@ -26,20 +26,22 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
public interface IMdmLinkSvc {
/**
* Update a link between a Person record and its target Patient/Practitioner record. If a link does not exist between
* Update a link between a Golden Resource record and its target record. If a link does not exist between
* these two records, create it.
* @param thePerson the Person to link the target resource to.
* @param theTargetResource the target resource, which is a Patient or Practitioner
*
* @param theGoldenResource the Golden Resource to link the target resource to.
* @param theTargetResource the target resource, which can be of the MDM supported types
* @param theMatchResult the current status of the match to set the link to.
* @param theLinkSource MANUAL or AUTO: what caused the link.
* @param theMdmTransactionContext
*/
void updateLink(IAnyResource thePerson, IAnyResource theTargetResource, MdmMatchOutcome theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext);
void updateLink(IAnyResource theGoldenResource, IAnyResource theTargetResource, MdmMatchOutcome theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext);
/**
* Delete a link between given Person and target patient/practitioner
* @param theExistingPerson
* Delete a link between given Golden Resource and the corresponing target
*
* @param theExistingGoldenResource
* @param theResource
*/
void deleteLink(IAnyResource theExistingPerson, IAnyResource theResource, MdmTransactionContext theMdmTransactionContext);
void deleteLink(IAnyResource theExistingGoldenResource, IAnyResource theResource, MdmTransactionContext theMdmTransactionContext);
}

View File

@ -25,5 +25,5 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
public interface IMdmLinkUpdaterSvc {
IAnyResource updateLink(IAnyResource theGoldenResource, IAnyResource theTarget, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext);
void notDuplicatePerson(IAnyResource theGoldenResource, IAnyResource theTarget, MdmTransactionContext theMdmContext);
void notDuplicateGoldenResource(IAnyResource theGoldenResource, IAnyResource theTarget, MdmTransactionContext theMdmContext);
}

View File

@ -27,7 +27,7 @@ import java.util.List;
public interface IMdmMatchFinderSvc {
/**
* Retrieve a list of possible Patient/Practitioner candidates for matching, based on the given {@link IAnyResource}
* Retrieve a list of possible target candidates for matching, based on the given {@link IAnyResource}
* Internally, performs all MDM matching rules on the type of the resource.
*
* @param theResourceType the type of the resource.

View File

@ -22,12 +22,12 @@ package ca.uhn.fhir.mdm.api;
public class MdmConstants {
/**
* TAG system for Person resources which are managed by HAPI MDM.
* TAG system for Golden Resources which are managed by HAPI MDM.
*/
public static final String SYSTEM_MDM_MANAGED = "https://hapifhir.org/NamingSystem/managing-mdm-system";
public static final String CODE_HAPI_MDM_MANAGED = "HAPI-MDM";
public static final String DISPLAY_HAPI_MDM_MANAGED = "This Person can only be modified by Smile CDR's MDM system.";
public static final String DISPLAY_HAPI_MDM_MANAGED = "This Golden Resource can only be modified by Smile CDR's MDM system.";
public static final String CODE_NO_MDM_MANAGED = "NO-MDM";
public static final String HAPI_ENTERPRISE_IDENTIFIER_SYSTEM = "http://hapifhir.io/fhir/NamingSystem/mdm-golden-resource-enterprise-id";
public static final String ALL_RESOURCE_SEARCH_PARAM_TYPE = "*";

View File

@ -26,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date;
public class MdmLinkJson implements IModelJson {
@JsonProperty("goldenResourceId")
private String myGoldenResourceId;
@ -51,7 +52,7 @@ public class MdmLinkJson implements IModelJson {
@JsonProperty("eidMatch")
private Boolean myEidMatch;
/** This link created a new person **/
/** This link created a new golden resource **/
@JsonProperty("linkCreatedNewGoldenResource")
private Boolean myLinkCreatedNewResource;

View File

@ -21,16 +21,15 @@ package ca.uhn.fhir.mdm.api;
*/
public enum MdmLinkSourceEnum {
/**
* Link was created or last modified by an algorithm
*/
AUTO,
/**
* Link was created or last modified by a person
* Link was created or last modified manually by a system user
*/
MANUAL
// Stored in database as ORDINAL. Only add new values to bottom!
}

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.mdm.api;
*/
public class MdmMatchEvaluation {
public final boolean match;
public final double score;

View File

@ -29,7 +29,7 @@ public final class MdmMatchOutcome {
public static final MdmMatchOutcome POSSIBLE_DUPLICATE = new MdmMatchOutcome(null, null).setMatchResultEnum(MdmMatchResultEnum.POSSIBLE_DUPLICATE);
public static final MdmMatchOutcome NO_MATCH = new MdmMatchOutcome(null, null).setMatchResultEnum(MdmMatchResultEnum.NO_MATCH);
public static final MdmMatchOutcome NEW_PERSON_MATCH = new MdmMatchOutcome(null, null).setMatchResultEnum(MdmMatchResultEnum.MATCH).setCreatedNewResource(true);
public static final MdmMatchOutcome NEW_GOLDEN_RESOURCE_MATCH = new MdmMatchOutcome(null, null).setMatchResultEnum(MdmMatchResultEnum.MATCH).setCreatedNewResource(true);
public static final MdmMatchOutcome EID_MATCH = new MdmMatchOutcome(null, null).setMatchResultEnum(MdmMatchResultEnum.MATCH).setEidMatch(true);
public static final MdmMatchOutcome POSSIBLE_MATCH = new MdmMatchOutcome(null, null).setMatchResultEnum(MdmMatchResultEnum.POSSIBLE_MATCH);
@ -45,7 +45,7 @@ public final class MdmMatchOutcome {
public final Double score;
/**
* Did the MDM match operation result in creating a new Person resource?
* Did the MDM match operation result in creating a new golden resource resource?
*/
private boolean myCreatedNewResource;
@ -95,7 +95,7 @@ public final class MdmMatchOutcome {
return myCreatedNewResource;
}
/** @param theCreatedNewResource this match is creating a new person */
/** @param theCreatedNewResource this match is creating a new golden resource */
public MdmMatchOutcome setCreatedNewResource(boolean theCreatedNewResource) {
myCreatedNewResource = theCreatedNewResource;
return this;
@ -152,7 +152,7 @@ public final class MdmMatchOutcome {
return new ToStringBuilder(this)
.append("vector", vector)
.append("score", score)
.append("myNewPerson", myCreatedNewResource)
.append("myCreatedNewResource", myCreatedNewResource)
.append("myEidMatch", myEidMatch)
.append("myMatchResultEnum", myMatchResultEnum)
.toString();

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.mdm.api;
*/
public enum MdmMatchResultEnum {
/**
* Manually confirmed to not be a match.
*/
@ -37,21 +38,21 @@ public enum MdmMatchResultEnum {
MATCH,
/**
* Link between two Person resources indicating they may be duplicates.
* Link between two Golden Records resources indicating they may be duplicates.
*/
POSSIBLE_DUPLICATE,
/**
* Link between Person and Target pointing to the Golden Record for that Person
* Link between Golden Record and Target pointing to the Golden Record for that Target
*/
GOLDEN_RECORD,
/**
* Link between two Person resources resulting from a merge. The Person points to the active person after the merge
* and the Target points to the inactive person after the merge.
* Link between two Golden Resources resulting from a merge. One golden resource is deactivated. The inactive golden
* resource points to the active golden resource after the merge. The target resource points to the inactive golden
* resource after the merge.
*/
REDIRECT
// Stored in database as ORDINAL. Only add new values to bottom!
}

View File

@ -32,7 +32,7 @@ public class MdmTransactionContext {
UPDATE_LINK,
DUPLICATE_GOLDEN_RESOURCES,
NOT_DUPLICATE,
MERGE_PERSONS
MERGE_GOLDEN_RESOURCES
}
/**

View File

@ -48,7 +48,7 @@ public abstract class BaseMdmProvider {
validateNotNull(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId);
validateNotNull(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToGoldenResourceId);
if (theFromGoldenResourceId.getValue().equals(theToGoldenResourceId.getValue())) {
throw new InvalidRequestException("fromPersonId must be different from toPersonId");
throw new InvalidRequestException("fromGoldenResourceId must be different from toGoldenResourceId");
}
}

View File

@ -77,9 +77,9 @@ public class MdmControllerHelper {
return (IAnyResource) myResourceLoader.load(resourceClass, theResourceId);
}
public void validateMergeResources(IAnyResource theFromPerson, IAnyResource theToPerson) {
validateIsMdmManaged(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPerson);
validateIsMdmManaged(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPerson);
public void validateMergeResources(IAnyResource theFromGoldenResource, IAnyResource theToGoldenResource) {
validateIsMdmManaged(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResource);
validateIsMdmManaged(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToGoldenResource);
}
public String toJson(IAnyResource theAnyResource) {

View File

@ -41,11 +41,11 @@ public class MdmControllerUtil {
return MdmLinkSourceEnum.valueOf(theLinkSource);
}
public static IIdType extractPersonIdDtOrNull(String theName, String thePersonId) {
if (thePersonId == null) {
public static IIdType extractGoldenResourceIdDtOrNull(String theName, String theGoldenResourceId) {
if (theGoldenResourceId == null) {
return null;
}
return getGoldenIdDtOrThrowException(theName, thePersonId);
return getGoldenIdDtOrThrowException(theName, theGoldenResourceId);
}
public static IIdType extractTargetIdDtOrNull(String theName, String theTargetId) {

View File

@ -118,7 +118,7 @@ public class MdmProviderDstu3 extends BaseMdmProvider {
String resourceType = getResourceType(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId);
return myMdmControllerSvc.mergeGoldenResources(theFromGoldenResourceId.getValue(), theToGoldenResourceId.getValue(),
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.MERGE_PERSONS, resourceType));
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES, resourceType));
}
@Operation(name = ProviderConstants.MDM_UPDATE_LINK)

View File

@ -151,7 +151,7 @@ public class MdmProviderR4 extends BaseMdmProvider {
validateMergeParameters(theFromGoldenResourceId, theToGoldenResourceId);
return myMdmControllerSvc.mergeGoldenResources(theFromGoldenResourceId.getValue(), theToGoldenResourceId.getValue(),
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.MERGE_PERSONS,
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES,
getResourceType(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId))
);
}

View File

@ -45,8 +45,8 @@ public enum MdmMatcherEnum {
SUBSTRING(new HapiStringMatcher(new SubstringStringMatcher())),
DATE(new HapiDateMatcher()),
NAME_ANY_ORDER(new NameMatcher(MdmPersonNameMatchModeEnum.ANY_ORDER)),
NAME_FIRST_AND_LAST(new NameMatcher(MdmPersonNameMatchModeEnum.FIRST_AND_LAST)),
NAME_ANY_ORDER(new NameMatcher(MdmNameMatchModeEnum.ANY_ORDER)),
NAME_FIRST_AND_LAST(new NameMatcher(MdmNameMatchModeEnum.FIRST_AND_LAST)),
IDENTIFIER(new IdentifierMatcher());

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.mdm.rules.matcher;
* #L%
*/
public enum MdmPersonNameMatchModeEnum {
public enum MdmNameMatchModeEnum {
ANY_ORDER,
FIRST_AND_LAST
}

View File

@ -34,9 +34,9 @@ import java.util.stream.Collectors;
*/
public class NameMatcher implements IMdmFieldMatcher {
private final MdmPersonNameMatchModeEnum myMatchMode;
private final MdmNameMatchModeEnum myMatchMode;
public NameMatcher(MdmPersonNameMatchModeEnum theMatchMode) {
public NameMatcher(MdmNameMatchModeEnum theMatchMode) {
myMatchMode = theMatchMode;
}
@ -63,7 +63,7 @@ public class NameMatcher implements IMdmFieldMatcher {
for (String leftGivenName : leftGivenNames) {
for (String rightGivenName : rightGivenNames) {
match |= leftGivenName.equals(rightGivenName) && leftFamilyName.equals(rightFamilyName);
if (myMatchMode == MdmPersonNameMatchModeEnum.ANY_ORDER) {
if (myMatchMode == MdmNameMatchModeEnum.ANY_ORDER) {
match |= leftGivenName.equals(rightFamilyName) && leftFamilyName.equals(rightGivenName);
}
}

View File

@ -27,7 +27,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
/**
* Helper class to determine assurance level based on Link Source and Match Result.
* This is strictly for use in populating Person links.
* This is strictly for use in populating Golden Resource links.
*/
public final class AssuranceLevelUtil {

View File

@ -36,6 +36,7 @@ import java.util.stream.Collectors;
@Service
public final class EIDHelper {
private final FhirContext myFhirContext;
private final IMdmSettings myMdmSettings;
@ -54,7 +55,7 @@ public final class EIDHelper {
}
/**
* Given an {@link IAnyResource} representing a patient/practitioner/person, retrieve their externally-assigned EID,
* Given an {@link IAnyResource} representing a type supported by MDM, retrieve their externally-assigned EID,
* represented as a {@link CanonicalEID}
*
* @param theResource the resource to extract the EID from.
@ -66,7 +67,7 @@ public final class EIDHelper {
}
/**
* Given an {@link IAnyResource} representing a patient/practitioner/person, retrieve their internally-assigned EID,
* Given an {@link IAnyResource} representing a type supported by MDM, retrieve their internally-assigned EID,
* represented as a {@link CanonicalEID}
*
* @param theResource the resource to extract the EID from.
@ -96,16 +97,12 @@ public final class EIDHelper {
}
/**
* An incoming resource is a potential duplicate if it matches a Patient that has a Person with an official EID, but
* the incoming resource also has an EID that does not match.
*
* @param theExistingPerson
* @param theComparingPerson
* @return
* An incoming resource is a potential duplicate if it matches a target resource that has a golden resource with an
* official EID, but the incoming resource also has an EID that does not match.
*/
public boolean hasEidOverlap(IAnyResource theExistingPerson, IAnyResource theComparingPerson) {
List<CanonicalEID> firstEids = this.getExternalEid(theExistingPerson);
List<CanonicalEID> secondEids = this.getExternalEid(theComparingPerson);
public boolean hasEidOverlap(IAnyResource theExistingGoldenResource, IAnyResource theComparingGoldenResource) {
List<CanonicalEID> firstEids = this.getExternalEid(theExistingGoldenResource);
List<CanonicalEID> secondEids = this.getExternalEid(theComparingGoldenResource);
if (firstEids.isEmpty() || secondEids.isEmpty()) {
return false;
}

View File

@ -25,26 +25,17 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.model.CanonicalIdentityAssuranceLevel;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.util.FhirTerser;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
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.BooleanType;
import org.hl7.fhir.r4.model.ContactPoint;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Person;
import org.hl7.fhir.r4.model.Reference;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -53,8 +44,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.stream.Collectors;
import static ca.uhn.fhir.context.FhirVersionEnum.DSTU3;
@ -216,7 +205,7 @@ public class GoldenResourceHelper {
/**
* Updates EID on Golden Resource, based on the incoming target resource. If the incoming resource has an external EID, it is applied
* to the Golden Resource, unless that person already has an external EID which does not match, in which case throw {@link IllegalArgumentException}
* to the Golden Resource, unless that golden resource already has an external EID which does not match, in which case throw {@link IllegalArgumentException}
* <p>
* If running in multiple EID mode, then incoming EIDs are simply added to the Golden Resource without checking for matches.
*
@ -228,17 +217,22 @@ public class GoldenResourceHelper {
theTargetResource, MdmTransactionContext theMdmTransactionContext) {
//This handles overwriting an automatically assigned EID if a patient that links is coming in with an official EID.
List<CanonicalEID> incomingTargetEid = myEIDHelper.getExternalEid(theTargetResource);
List<CanonicalEID> personOfficialEid = myEIDHelper.getExternalEid(theGoldenResource);
List<CanonicalEID> goldenResourceOfficialEid = myEIDHelper.getExternalEid(theGoldenResource);
if (!incomingTargetEid.isEmpty()) {
if (personOfficialEid.isEmpty() || !myMdmSettings.isPreventMultipleEids()) {
log(theMdmTransactionContext, "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");
addCanonicalEidsToGoldenResourceIfAbsent(theGoldenResource, incomingTargetEid);
} else if (!personOfficialEid.isEmpty() && myEIDHelper.eidMatchExists(personOfficialEid, incomingTargetEid)) {
log(theMdmTransactionContext, "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");
} else {
throw new IllegalArgumentException("This would create a duplicate person!");
if (incomingTargetEid.isEmpty()) {
return theGoldenResource;
}
if (goldenResourceOfficialEid.isEmpty() || !myMdmSettings.isPreventMultipleEids()) {
log(theMdmTransactionContext, "Incoming resource:" + theTargetResource.getIdElement().toUnqualifiedVersionless() + " + with EID " + incomingTargetEid.stream().map(CanonicalEID::toString).collect(Collectors.joining(","))
+ " is applying this EIDs to its related Target Resource, as this Target Resource does not yet have an external EID");
addCanonicalEidsToGoldenResourceIfAbsent(theGoldenResource, incomingTargetEid);
} else if (!goldenResourceOfficialEid.isEmpty() && myEIDHelper.eidMatchExists(goldenResourceOfficialEid, incomingTargetEid)) {
log(theMdmTransactionContext, "incoming resource:" + theTargetResource.getIdElement().toVersionless() + " with EIDs " + incomingTargetEid.stream().map(CanonicalEID::toString).collect(Collectors.joining(",")) + " does not need to overwrite Golden Resource, as this EID is already present");
} else {
throw new IllegalArgumentException(
String.format("Target EIDs %s would create a duplicate golden resource, as EIDs %s already exist!",
incomingTargetEid.toString(), goldenResourceOfficialEid.toString()));
}
return theGoldenResource;
}
@ -334,155 +328,30 @@ public class GoldenResourceHelper {
throw new IllegalStateException("Unsupported FHIR version " + myFhirContext.getVersion().getVersion());
}
/**
* To avoid adding duplicate
*
* @param thePerson
* @param theIdentifier
*/
private void addIdentifierIfAbsent(org.hl7.fhir.dstu3.model.Person thePerson, org.hl7.fhir.dstu3.model.Identifier
theIdentifier) {
Optional<org.hl7.fhir.dstu3.model.Identifier> first = thePerson.getIdentifier().stream().filter(identifier -> identifier.getSystem().equals(theIdentifier.getSystem())).filter(identifier -> identifier.getValue().equals(theIdentifier.getValue())).findFirst();
if (first.isPresent()) {
return;
} else {
thePerson.addIdentifier(theIdentifier);
}
}
public void mergeFields(IBaseResource theFromPerson, IBaseResource theToPerson) {
public void mergeFields(IBaseResource theFromGoldenResource, IBaseResource theToGoldenResource) {
// TODO NG - Revisit when merge rules are defined
cloneCompositeField(theFromPerson, theToPerson, FIELD_NAME_IDENTIFIER);
cloneCompositeField(theFromGoldenResource, theToGoldenResource, FIELD_NAME_IDENTIFIER);
// switch (myFhirContext.getVersion().getVersion()) {
// case R4:
// mergeR4PersonFields(theFromPerson, theToPerson);
// mergeR4PersonFields(theFromGoldenResource, theToGoldenResource);
// break;
// case DSTU3:
// mergeDstu3PersonFields(theFromPerson, theToPerson);
// mergeDstu3PersonFields(theFromGoldenResource, theToGoldenResource);
// break;
// default:
// throw new UnsupportedOperationException("Version not supported: " + myFhirContext.getVersion().getVersion());
// }
}
private void mergeR4PersonFields(IBaseResource theFromPerson, IBaseResource theToPerson) {
Person fromPerson = (Person) theFromPerson;
Person toPerson = (Person) theToPerson;
mergeElementList(fromPerson, toPerson, HumanName.class, Person::getName, HumanName::equalsDeep);
mergeElementList(fromPerson, toPerson, Identifier.class, Person::getIdentifier, Identifier::equalsDeep);
mergeElementList(fromPerson, toPerson, Address.class, Person::getAddress, Address::equalsDeep);
mergeElementList(fromPerson, toPerson, ContactPoint.class, Person::getTelecom, ContactPoint::equalsDeep);
if (!toPerson.hasBirthDate()) {
toPerson.setBirthDate(fromPerson.getBirthDate());
}
if (!toPerson.hasGender()) {
toPerson.setGender(fromPerson.getGender());
}
if (!toPerson.hasPhoto()) {
toPerson.setPhoto(fromPerson.getPhoto());
}
}
private <P, T> void mergeElementList(P fromPerson, P
toPerson, Class<T> theBase, Function<P, List<T>> theGetList, BiPredicate<T, T> theEquals) {
List<T> fromList = theGetList.apply(fromPerson);
List<T> toList = theGetList.apply(toPerson);
List<T> itemsToAdd = new ArrayList<>();
for (T fromItem : fromList) {
if (toList.stream().noneMatch(t -> theEquals.test(fromItem, t))) {
itemsToAdd.add(fromItem);
}
}
toList.addAll(itemsToAdd);
}
private void mergeDstu3PersonFields(IBaseResource theFromPerson, IBaseResource theToPerson) {
org.hl7.fhir.dstu3.model.Person fromPerson = (org.hl7.fhir.dstu3.model.Person) theFromPerson;
org.hl7.fhir.dstu3.model.Person toPerson = (org.hl7.fhir.dstu3.model.Person) theToPerson;
mergeElementList(fromPerson, toPerson, org.hl7.fhir.dstu3.model.HumanName.class, org.hl7.fhir.dstu3.model.Person::getName, org.hl7.fhir.dstu3.model.HumanName::equalsDeep);
mergeElementList(fromPerson, toPerson, org.hl7.fhir.dstu3.model.Identifier.class, org.hl7.fhir.dstu3.model.Person::getIdentifier, org.hl7.fhir.dstu3.model.Identifier::equalsDeep);
mergeElementList(fromPerson, toPerson, org.hl7.fhir.dstu3.model.Address.class, org.hl7.fhir.dstu3.model.Person::getAddress, org.hl7.fhir.dstu3.model.Address::equalsDeep);
mergeElementList(fromPerson, toPerson, org.hl7.fhir.dstu3.model.ContactPoint.class, org.hl7.fhir.dstu3.model.Person::getTelecom, org.hl7.fhir.dstu3.model.ContactPoint::equalsDeep);
if (!toPerson.hasBirthDate()) {
toPerson.setBirthDate(fromPerson.getBirthDate());
}
if (!toPerson.hasGender()) {
toPerson.setGender(fromPerson.getGender());
}
if (!toPerson.hasPhoto()) {
toPerson.setPhoto(fromPerson.getPhoto());
}
}
/**
* An incoming resource is a potential duplicate if it matches a Patient that has a Person with an official EID, but
* the incoming resource also has an EID that does not match.
*
* @param theExistingPerson
* @param theComparingPerson
* @return
* An incoming resource is a potential duplicate if it matches a target that has a golden resource with an official
* EID, but the incoming resource also has an EID that does not match.
*/
public boolean isPotentialDuplicate(IAnyResource theExistingPerson, IAnyResource theComparingPerson) {
List<CanonicalEID> externalEidsPerson = myEIDHelper.getExternalEid(theExistingPerson);
List<CanonicalEID> externalEidsResource = myEIDHelper.getExternalEid(theComparingPerson);
return !externalEidsPerson.isEmpty() && !externalEidsResource.isEmpty() && !myEIDHelper.eidMatchExists(externalEidsResource, externalEidsPerson);
}
public IBaseBackboneElement newPersonLink(IIdType theTargetId, CanonicalIdentityAssuranceLevel theAssuranceLevel) {
switch (myFhirContext.getVersion().getVersion()) {
case R4:
return newR4PersonLink(theTargetId, theAssuranceLevel);
case DSTU3:
return newDstu3PersonLink(theTargetId, theAssuranceLevel);
default:
throw new UnsupportedOperationException("Version not supported: " + myFhirContext.getVersion().getVersion());
}
}
private IBaseBackboneElement newR4PersonLink(IIdType theTargetId, CanonicalIdentityAssuranceLevel
theAssuranceLevel) {
Person.PersonLinkComponent retval = new Person.PersonLinkComponent();
retval.setTarget(new Reference(theTargetId));
retval.setAssurance(theAssuranceLevel.toR4());
return retval;
}
private IBaseBackboneElement newDstu3PersonLink(IIdType theTargetId, CanonicalIdentityAssuranceLevel
theAssuranceLevel) {
org.hl7.fhir.dstu3.model.Person.PersonLinkComponent retval = new org.hl7.fhir.dstu3.model.Person.PersonLinkComponent();
retval.setTarget(new org.hl7.fhir.dstu3.model.Reference(theTargetId));
retval.setAssurance(theAssuranceLevel.toDstu3());
return retval;
}
public void setLinks(IAnyResource thePersonResource, List<IBaseBackboneElement> theNewLinks) {
switch (myFhirContext.getVersion().getVersion()) {
case R4:
setLinksR4(thePersonResource, theNewLinks);
break;
case DSTU3:
setLinksDstu3(thePersonResource, theNewLinks);
break;
default:
throw new UnsupportedOperationException("Version not supported: " + myFhirContext.getVersion().getVersion());
}
}
private void setLinksDstu3(IAnyResource thePersonResource, List<IBaseBackboneElement> theLinks) {
org.hl7.fhir.dstu3.model.Person person = (org.hl7.fhir.dstu3.model.Person) thePersonResource;
List<org.hl7.fhir.dstu3.model.Person.PersonLinkComponent> links = (List<org.hl7.fhir.dstu3.model.Person.PersonLinkComponent>) (List<?>) theLinks;
person.setLink(links);
}
private void setLinksR4(IAnyResource thePersonResource, List<IBaseBackboneElement> theLinks) {
Person person = (Person) thePersonResource;
List<Person.PersonLinkComponent> links = (List<Person.PersonLinkComponent>) (List<?>) theLinks;
person.setLink(links);
public boolean isPotentialDuplicate(IAnyResource theExistingGoldenResource, IAnyResource theComparingGoldenResource) {
List<CanonicalEID> externalEidsGoldenResource = myEIDHelper.getExternalEid(theExistingGoldenResource);
List<CanonicalEID> externalEidsResource = myEIDHelper.getExternalEid(theComparingGoldenResource);
return !externalEidsGoldenResource.isEmpty() && !externalEidsResource.isEmpty() && !myEIDHelper.eidMatchExists(externalEidsResource, externalEidsGoldenResource);
}
private void log(MdmTransactionContext theMdmTransactionContext, String theMessage) {

View File

@ -20,7 +20,6 @@ package ca.uhn.fhir.mdm.util;
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.mdm.api.MdmConstants;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -45,7 +44,7 @@ public final class MdmUtil {
/**
* Checks for the presence of the MDM-managed tag, indicating the MDM system has ownership
* of this Person's links.
* of this golden resource's links.
*
* @param theBaseResource the resource to check.
* @return a boolean indicating whether or not MDM manages this FHIR resource.

View File

@ -44,11 +44,11 @@ public abstract class BaseR4Test {
assertEquals(theExpectedMatchEnum, theMatchResult.getMatchResultEnum());
}
protected void assertMatchResult(MdmMatchResultEnum theExpectedMatchEnum, long theExpectedVector, double theExpectedScore, boolean theExpectedNewPerson, boolean theExpectedEidMatch, MdmMatchOutcome theMatchResult) {
protected void assertMatchResult(MdmMatchResultEnum theExpectedMatchEnum, long theExpectedVector, double theExpectedScore, boolean theExpectedNewGoldenResource, boolean theExpectedEidMatch, MdmMatchOutcome theMatchResult) {
assertEquals(theExpectedScore, theMatchResult.score, 0.001);
assertEquals(theExpectedVector, theMatchResult.vector);
assertEquals(theExpectedEidMatch, theMatchResult.isEidMatch());
assertEquals(theExpectedNewPerson, theMatchResult.isCreatedNewResource());
assertEquals(theExpectedNewGoldenResource, theMatchResult.isCreatedNewResource());
assertEquals(theExpectedMatchEnum, theMatchResult.getMatchResultEnum());
}
}