Empi docs (#1989)
* Remove lines from image edges. Thanks Max! * fix case 4 docs only mark possible match as eid if it was via an eid match
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
@ -67,7 +67,7 @@ When a new Patient resource is compared with all other resources of that type in
|
|||
|
||||
* CASE 3: The MATCH Patient 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. These duplicates are manually reviewed later and either merged or marked as NO_MATCH and the system will no longer consider them as a POSSIBLE_DUPLICATE going forward. POSSIBLE_DUPLICATE is the only link type that can have a Person as both the source and target of the link.
|
||||
|
||||
* CASE 4: Only POSSIBLE_MATCH outcomes -> In this case, empi-link records are created with POSSIBLE_MATCH outcome and await manual assignment to either NO_MATCH or MATCH. Person resources are not changed.
|
||||
* CASE 4: Only POSSIBLE_MATCH outcomes -> In this case, new POSSIBLE_MATCH links are created and await manual assignment to either NO_MATCH or MATCH.
|
||||
|
||||
# HAPI EMPI Technical Details
|
||||
|
||||
|
|
|
@ -101,14 +101,20 @@ public class EmpiMatchLinkSvc {
|
|||
List<IAnyResource> persons = new ArrayList<>();
|
||||
for (MatchedPersonCandidate matchedPersonCandidate : theCandidateList.getCandidates()) {
|
||||
IAnyResource person = myEmpiPersonFindingSvc.getPersonFromMatchedPersonCandidate(matchedPersonCandidate);
|
||||
myEmpiLinkSvc.updateLink(person, theResource, EmpiMatchOutcome.EID_POSSIBLE_MATCH, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
|
||||
EmpiMatchOutcome outcome = EmpiMatchOutcome.POSSIBLE_MATCH;
|
||||
outcome.setEidMatch(theCandidateList.isEidMatch());
|
||||
myEmpiLinkSvc.updateLink(person, theResource, outcome, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
|
||||
persons.add(person);
|
||||
}
|
||||
|
||||
//Set all Persons as POSSIBLE_DUPLICATE of the last person.
|
||||
IAnyResource firstPerson = persons.get(0);
|
||||
persons.subList(1, persons.size())
|
||||
.forEach(possibleDuplicatePerson -> myEmpiLinkSvc.updateLink(firstPerson, possibleDuplicatePerson, EmpiMatchOutcome.EID_POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext));
|
||||
.forEach(possibleDuplicatePerson -> {
|
||||
EmpiMatchOutcome outcome = EmpiMatchOutcome.POSSIBLE_DUPLICATE;
|
||||
outcome.setEidMatch(theCandidateList.isEidMatch());
|
||||
myEmpiLinkSvc.updateLink(firstPerson, possibleDuplicatePerson, outcome, EmpiLinkSourceEnum.AUTO, theEmpiTransactionContext);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,15 +26,15 @@ import java.util.List;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
public class CandidateList {
|
||||
private final CandidateStrategyEnum mySource;
|
||||
private final CandidateStrategyEnum myStrategy;
|
||||
private final List<MatchedPersonCandidate> myList = new ArrayList<>();
|
||||
|
||||
public CandidateList(CandidateStrategyEnum theSource) {
|
||||
mySource = theSource;
|
||||
public CandidateList(CandidateStrategyEnum theStrategy) {
|
||||
myStrategy = theStrategy;
|
||||
}
|
||||
|
||||
public CandidateStrategyEnum getSource() {
|
||||
return mySource;
|
||||
public CandidateStrategyEnum getStrategy() {
|
||||
return myStrategy;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
|
@ -63,4 +63,8 @@ public class CandidateList {
|
|||
public MatchedPersonCandidate getFirstMatch() {
|
||||
return myList.get(0);
|
||||
}
|
||||
|
||||
public boolean isEidMatch() {
|
||||
return myStrategy.isEidMatch();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,5 +26,9 @@ public enum CandidateStrategyEnum {
|
|||
/** Find Person candidates based on a link already existing for the target resource */
|
||||
LINK,
|
||||
/** Find Person candidates based on other targets that match the incoming target using the EMPI Matching rules */
|
||||
SCORE
|
||||
SCORE;
|
||||
|
||||
public boolean isEidMatch() {
|
||||
return this == EID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ import java.util.Collections;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
@ -381,4 +382,25 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
|
|||
ourLog.info(link.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertLinksMatchResult(EmpiMatchResultEnum... theExpectedValues) {
|
||||
assertFields(EmpiLink::getMatchResult, theExpectedValues);
|
||||
}
|
||||
|
||||
protected void assertLinksNewPerson(Boolean... theExpectedValues) {
|
||||
assertFields(EmpiLink::getNewPerson, theExpectedValues);
|
||||
}
|
||||
|
||||
protected void assertLinksMatchedByEid(Boolean... theExpectedValues) {
|
||||
assertFields(EmpiLink::getEidMatch, theExpectedValues);
|
||||
}
|
||||
|
||||
private <T> void assertFields(Function<EmpiLink, T> theAccessor, T... theExpectedValues) {
|
||||
List<EmpiLink> links = myEmpiLinkDao.findAll();
|
||||
assertEquals(theExpectedValues.length, links.size());
|
||||
for (int i = 0; i < links.size(); ++i) {
|
||||
assertEquals(theExpectedValues[i], theAccessor.apply(links.get(i)), "Value at index " + i + " was not equal");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.empi.svc;
|
||||
|
||||
import ca.uhn.fhir.empi.api.EmpiConstants;
|
||||
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
|
||||
import ca.uhn.fhir.empi.model.CanonicalEID;
|
||||
import ca.uhn.fhir.empi.util.EIDHelper;
|
||||
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
|
||||
|
@ -16,7 +15,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -29,7 +27,6 @@ import static org.hamcrest.Matchers.hasSize;
|
|||
import static org.hamcrest.Matchers.in;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
|
@ -87,26 +84,6 @@ public class EmpiMatchLinkSvcMultipleEidModeTest extends BaseEmpiR4Test {
|
|||
assertThat(thirdIdentifier.getValue(), is(equalTo("67890")));
|
||||
}
|
||||
|
||||
private void assertLinksMatchResult(EmpiMatchResultEnum... theExpectedValues) {
|
||||
assertFields(EmpiLink::getMatchResult, theExpectedValues);
|
||||
}
|
||||
|
||||
private void assertLinksNewPerson(Boolean... theExpectedValues) {
|
||||
assertFields(EmpiLink::getNewPerson, theExpectedValues);
|
||||
}
|
||||
|
||||
private void assertLinksMatchedByEid(Boolean... theExpectedValues) {
|
||||
assertFields(EmpiLink::getEidMatch, theExpectedValues);
|
||||
}
|
||||
|
||||
private <T> void assertFields(Function<EmpiLink, T> theAccessor, T... theExpectedValues) {
|
||||
List<EmpiLink> links = myEmpiLinkDao.findAll();
|
||||
assertEquals(theExpectedValues.length, links.size());
|
||||
for (int i = 0; i < links.size(); ++i) {
|
||||
assertEquals(theExpectedValues[i], theAccessor.apply(links.get(i)), "Value at index " + i + " was not equal");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
// Test Case #4
|
||||
public void testHavingMultipleEIDsOnIncomingPatientMatchesCorrectly() {
|
||||
|
|
|
@ -29,6 +29,10 @@ import java.util.Optional;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.MATCH;
|
||||
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.NO_MATCH;
|
||||
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_DUPLICATE;
|
||||
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_MATCH;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.blankOrNullString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
@ -37,6 +41,7 @@ import static org.hamcrest.Matchers.hasSize;
|
|||
import static org.hamcrest.Matchers.in;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
|
||||
|
@ -57,6 +62,9 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
|
|||
public void testAddPatientLinksToNewPersonIfNoneFound() {
|
||||
createPatientAndUpdateLinks(buildJanePatient());
|
||||
assertLinkCount(1);
|
||||
assertLinksMatchResult(MATCH);
|
||||
assertLinksNewPerson(true);
|
||||
assertLinksMatchedByEid(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -66,6 +74,9 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
|
|||
|
||||
assertLinkCount(2);
|
||||
assertThat(patient1, is(not(samePersonAs(patient2))));
|
||||
assertLinksMatchResult(MATCH, MATCH);
|
||||
assertLinksNewPerson(true, true);
|
||||
assertLinksMatchedByEid(false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -77,6 +88,9 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
|
|||
assertLinkCount(2);
|
||||
|
||||
assertThat(patient1, is(samePersonAs(patient2)));
|
||||
assertLinksMatchResult(MATCH, MATCH);
|
||||
assertLinksNewPerson(true, false);
|
||||
assertLinksMatchedByEid(false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -94,6 +108,10 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
|
|||
|
||||
assertThat(unmatchedJane, is(not(samePersonAs(janePerson))));
|
||||
assertThat(unmatchedJane, is(not(linkedTo(originalJane))));
|
||||
|
||||
assertLinksMatchResult(MATCH, NO_MATCH, MATCH);
|
||||
assertLinksNewPerson(true, false, true);
|
||||
assertLinksMatchedByEid(false, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -114,6 +132,10 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
|
|||
|
||||
assertThat(unmatchedPatient, is(not(samePersonAs(janePerson))));
|
||||
assertThat(unmatchedPatient, is(not(linkedTo(originalJane))));
|
||||
|
||||
assertLinksMatchResult(MATCH, NO_MATCH, MATCH);
|
||||
assertLinksNewPerson(true, false, true);
|
||||
assertLinksMatchedByEid(false, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -332,13 +354,18 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
|
|||
//Ensure there is no successful MATCH links for incomingJanePatient
|
||||
Optional<EmpiLink> matchedLinkForTargetPid = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(incomingJanePatient));
|
||||
assertThat(matchedLinkForTargetPid.isPresent(), is(false));
|
||||
|
||||
logAllLinks();
|
||||
assertLinksMatchResult(MATCH, MATCH, POSSIBLE_MATCH, POSSIBLE_MATCH, POSSIBLE_DUPLICATE);
|
||||
assertLinksNewPerson(true, true, false, false, false);
|
||||
assertLinksMatchedByEid(false, false, false, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenAllMatchResultsArePOSSIBLE_MATCHThattheyAreLinkedAndNoPersonIsCreated() {
|
||||
/**
|
||||
* CASE 4: Only POSSIBLE_MATCH outcomes -> In this case, empi-link records are created with POSSIBLE_MATCH
|
||||
* outcome and await manual assignment to either NO_MATCH or MATCHED. Person resources are not changed.
|
||||
* outcome and await manual assignment to either NO_MATCH or MATCHED. Person link is added.
|
||||
*/
|
||||
Patient patient = buildJanePatient();
|
||||
patient.getNameFirstRep().setFamily("familyone");
|
||||
|
@ -357,6 +384,17 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
|
|||
|
||||
assertThat(patient3, is(possibleMatchWith(patient2)));
|
||||
assertThat(patient3, is(possibleMatchWith(patient)));
|
||||
|
||||
IBundleProvider bundle = myPersonDao.search(new SearchParameterMap());
|
||||
assertEquals(1, bundle.size());
|
||||
Person person = (Person) bundle.getResources(0, 0);
|
||||
assertEquals(Person.IdentityAssuranceLevel.LEVEL2, person.getLink().get(0).getAssurance());
|
||||
assertEquals(Person.IdentityAssuranceLevel.LEVEL1, person.getLink().get(1).getAssurance());
|
||||
assertEquals(Person.IdentityAssuranceLevel.LEVEL1, person.getLink().get(2).getAssurance());
|
||||
|
||||
assertLinksMatchResult(MATCH, POSSIBLE_MATCH, POSSIBLE_MATCH);
|
||||
assertLinksNewPerson(true, false, false);
|
||||
assertLinksMatchedByEid(false, false, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -28,8 +28,7 @@ public final class EmpiMatchOutcome {
|
|||
public static final EmpiMatchOutcome NO_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.NO_MATCH);
|
||||
public static final EmpiMatchOutcome NEW_PERSON_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.MATCH).setNewPerson(true);
|
||||
public static final EmpiMatchOutcome EID_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.MATCH).setEidMatch(true);
|
||||
public static final EmpiMatchOutcome EID_POSSIBLE_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.POSSIBLE_MATCH).setEidMatch(true);
|
||||
public static final EmpiMatchOutcome EID_POSSIBLE_DUPLICATE = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.POSSIBLE_DUPLICATE).setEidMatch(true);
|
||||
public static final EmpiMatchOutcome POSSIBLE_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.POSSIBLE_MATCH);
|
||||
|
||||
/**
|
||||
* A bitmap that indicates which rules matched
|
||||
|
|