Attempting to perform MDM on a new resource type

This commit is contained in:
Tadgh 2020-11-11 14:15:35 -05:00
parent 5d1612eef4
commit 331acc1b55
26 changed files with 456 additions and 215 deletions

View File

@ -156,6 +156,7 @@ public class EmpiLink {
} }
public EmpiLink setSourceResourcePid(Long theSourceResourcePid) { public EmpiLink setSourceResourcePid(Long theSourceResourcePid) {
myPersonPid = theSourceResourcePid;
mySourceResourcePid = theSourceResourcePid; mySourceResourcePid = theSourceResourcePid;
return this; return this;
} }

View File

@ -54,7 +54,7 @@ public class EmpiControllerSvcImpl implements IEmpiControllerSvc {
IEmpiLinkUpdaterSvc myIEmpiLinkUpdaterSvc; IEmpiLinkUpdaterSvc myIEmpiLinkUpdaterSvc;
@Override @Override
public IAnyResource mergePersons(String theFromPersonId, String theToPersonId, EmpiTransactionContext theEmpiTransactionContext) { public IAnyResource mergeGoldenResources(String theFromPersonId, String theToPersonId, EmpiTransactionContext theEmpiTransactionContext) {
IAnyResource fromPerson = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPersonId); IAnyResource fromPerson = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPersonId);
IAnyResource toPerson = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPersonId); IAnyResource toPerson = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPersonId);
myEmpiControllerHelper.validateMergeResources(fromPerson, toPerson); myEmpiControllerHelper.validateMergeResources(fromPerson, toPerson);
@ -75,7 +75,7 @@ public class EmpiControllerSvcImpl implements IEmpiControllerSvc {
} }
@Override @Override
public Stream<EmpiLinkJson> getDuplicatePersons(EmpiTransactionContext theEmpiContext) { public Stream<EmpiLinkJson> getDuplicateGoldenResources(EmpiTransactionContext theEmpiContext) {
return myEmpiLinkQuerySvc.getDuplicatePersons(theEmpiContext); return myEmpiLinkQuerySvc.getDuplicatePersons(theEmpiContext);
} }
@ -91,7 +91,7 @@ public class EmpiControllerSvcImpl implements IEmpiControllerSvc {
} }
@Override @Override
public void notDuplicatePerson(String thePersonId, String theTargetPersonId, EmpiTransactionContext theEmpiContext) { public void notDuplicateGoldenResource(String thePersonId, String theTargetPersonId, EmpiTransactionContext theEmpiContext) {
IAnyResource person = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, thePersonId); IAnyResource person = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, thePersonId);
IAnyResource target = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetPersonId); IAnyResource target = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetPersonId);

View File

@ -62,13 +62,13 @@ public class EmpiLinkQuerySvcImpl implements IEmpiLinkQuerySvc {
EmpiLinkJson retval = new EmpiLinkJson(); EmpiLinkJson retval = new EmpiLinkJson();
String targetId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getTargetPid()).toVersionless().getValue(); String targetId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getTargetPid()).toVersionless().getValue();
retval.setTargetId(targetId); retval.setTargetId(targetId);
String personId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourceResourcePid()).toVersionless().getValue(); String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourceResourcePid()).toVersionless().getValue();
retval.setPersonId(personId); retval.setGoldenResourceId(goldenResourceId);
retval.setCreated(theLink.getCreated()); retval.setCreated(theLink.getCreated());
retval.setEidMatch(theLink.getEidMatch()); retval.setEidMatch(theLink.getEidMatch());
retval.setLinkSource(theLink.getLinkSource()); retval.setLinkSource(theLink.getLinkSource());
retval.setMatchResult(theLink.getMatchResult()); retval.setMatchResult(theLink.getMatchResult());
retval.setNewPerson(theLink.getHadToCreateNewResource()); retval.setLinkCreatedNewResource(theLink.getHadToCreateNewResource());
retval.setScore(theLink.getScore()); retval.setScore(theLink.getScore());
retval.setUpdated(theLink.getUpdated()); retval.setUpdated(theLink.getUpdated());
retval.setVector(theLink.getVector()); retval.setVector(theLink.getVector());

View File

@ -135,19 +135,18 @@ public class EmpiLinkUpdaterSvcImpl implements IEmpiLinkUpdaterSvc {
myEmpiLinkDaoSvc.save(empiLink); myEmpiLinkDaoSvc.save(empiLink);
} }
private void validateNotDuplicatePersonRequest(IAnyResource thePerson, IAnyResource theTarget) { /**
String personType = myFhirContext.getResourceType(thePerson); * Ensure that the two resources are of the same type and both are managed by HAPI-EMPI.
*/
private void validateNotDuplicatePersonRequest(IAnyResource theGoldenResource, IAnyResource theTarget) {
String goldenResourceType = myFhirContext.getResourceType(theGoldenResource);
String targetType = myFhirContext.getResourceType(theTarget); String targetType = myFhirContext.getResourceType(theTarget);
if (!"Person".equals(personType)) { if (!goldenResourceType.equalsIgnoreCase(targetType)) {
throw new InvalidRequestException("First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be a Person. Was " + personType); throw new InvalidRequestException("First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be the same resource type as the second argument. Was " + goldenResourceType + "/" + targetType);
}
if (!"Person".equals(targetType)) {
throw new InvalidRequestException("Second argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be a Person . Was " + targetType);
} }
if (!EmpiUtil.isEmpiManaged(thePerson) || !EmpiUtil.isEmpiManaged(theTarget)) { if (!EmpiUtil.isEmpiManaged(theGoldenResource) || !EmpiUtil.isEmpiManaged(theTarget)) {
throw new InvalidRequestException("Only EMPI Managed Person resources may be updated via this operation. The Person resource provided is not tagged as managed by hapi-empi"); throw new InvalidRequestException("Only EMPI Managed Golden Resources may be updated via this operation. The resource provided is not tagged as managed by hapi-empi");
} }
} }
} }

View File

@ -67,8 +67,8 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
@Transactional @Transactional
public long submitAllTargetTypesToEmpi(@Nullable String theCriteria) { public long submitAllTargetTypesToEmpi(@Nullable String theCriteria) {
long submittedCount = 0; long submittedCount = 0;
submittedCount += submitPatientTypeToEmpi(theCriteria); submittedCount += submitPatientTypeToMdm(theCriteria);
submittedCount += submitPractitionerTypeToEmpi(theCriteria); submittedCount += submitPractitionerTypeToMdm(theCriteria);
return submittedCount; return submittedCount;
} }
@ -97,7 +97,7 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
total += loadPidsAndSubmitToEmpiChannel(theSearchBuilder, pidBatch); total += loadPidsAndSubmitToEmpiChannel(theSearchBuilder, pidBatch);
} while (query.hasNext()); } while (query.hasNext());
} catch (IOException theE) { } catch (IOException theE) {
throw new InternalErrorException("Failure while attempting to query resources for " + ProviderConstants.OPERATION_EMPI_SUBMIT, theE); throw new InternalErrorException("Failure while attempting to query resources for " + ProviderConstants.OPERATION_MDM_SUBMIT, theE);
} }
ourLog.info("EMPI Submit complete. Submitted a total of {} resources.", total); ourLog.info("EMPI Submit complete. Submitted a total of {} resources.", total);
return total; return total;
@ -123,19 +123,19 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
@Override @Override
@Transactional @Transactional
public long submitPractitionerTypeToEmpi(@Nullable String theCriteria) { public long submitPractitionerTypeToMdm(@Nullable String theCriteria) {
return submitTargetTypeToEmpi("Practitioner", theCriteria); return submitTargetTypeToEmpi("Practitioner", theCriteria);
} }
@Override @Override
@Transactional @Transactional
public long submitPatientTypeToEmpi(@Nullable String theCriteria) { public long submitPatientTypeToMdm(@Nullable String theCriteria) {
return submitTargetTypeToEmpi("Patient", theCriteria); return submitTargetTypeToEmpi("Patient", theCriteria);
} }
@Override @Override
@Transactional @Transactional
public long submitTargetToEmpi(IIdType theId) { public long submitTargetToMdm(IIdType theId) {
resolveTargetTypeOrThrowException(theId.getResourceType()); resolveTargetTypeOrThrowException(theId.getResourceType());
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType()); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
IBaseResource read = resourceDao.read(theId); IBaseResource read = resourceDao.read(theId);
@ -145,7 +145,7 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
private void resolveTargetTypeOrThrowException(String theResourceType) { private void resolveTargetTypeOrThrowException(String theResourceType) {
if (!EmpiUtil.supportedTargetType(theResourceType)) { if (!EmpiUtil.supportedTargetType(theResourceType)) {
throw new InvalidRequestException(ProviderConstants.OPERATION_EMPI_SUBMIT + " does not support resource type: " + theResourceType); throw new InvalidRequestException(ProviderConstants.OPERATION_MDM_SUBMIT + " does not support resource type: " + theResourceType);
} }
} }
} }

View File

@ -41,8 +41,9 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.ContactPoint; import org.hl7.fhir.r4.model.ContactPoint;
import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DateType;
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.Patient;
import org.hl7.fhir.r4.model.Person;
import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.Practitioner;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -85,6 +86,10 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
@Autowired @Autowired
protected IFhirResourceDao<Patient> myPatientDao; protected IFhirResourceDao<Patient> myPatientDao;
@Autowired @Autowired
protected IFhirResourceDao<Organization> myOrganizationDao;
@Autowired
protected IFhirResourceDao<Medication> myMedicationDao;
@Autowired
protected IFhirResourceDao<Practitioner> myPractitionerDao; protected IFhirResourceDao<Practitioner> myPractitionerDao;
@Autowired @Autowired
protected EmpiResourceMatcherSvc myEmpiResourceMatcherSvc; protected EmpiResourceMatcherSvc myEmpiResourceMatcherSvc;
@ -170,6 +175,16 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
Patient patient = (Patient) outcome.getResource(); Patient patient = (Patient) outcome.getResource();
patient.setId(outcome.getId()); patient.setId(outcome.getId());
return patient; return patient;
}
@Nonnull
protected Medication createMedication(Medication theMedication) {
//Note that since our empi-rules block on active=true, all patients must be active.
DaoMethodOutcome outcome = myMedicationDao.create(theMedication);
Medication medication = (Medication) outcome.getResource();
medication.setId(outcome.getId());
return medication;
} }
@Nonnull @Nonnull

View File

@ -37,6 +37,7 @@ public abstract class BaseProviderR4Test extends BaseEmpiR4Test {
Resource resource = resourceLoader.getResource(theString); Resource resource = resourceLoader.getResource(theString);
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
myEmpiSettings.setScriptText(json); myEmpiSettings.setScriptText(json);
} }
@BeforeEach @BeforeEach

View File

@ -25,8 +25,8 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
protected Practitioner myPractitioner; protected Practitioner myPractitioner;
protected StringType myPractitionerId; protected StringType myPractitionerId;
protected IAnyResource myPractitionerPerson; protected IAnyResource myGoldenPractitier;
protected StringType myPractitionerPersonId; protected StringType myGoldenPractitionerId;
@Autowired @Autowired
IInterceptorService myInterceptorService; IInterceptorService myInterceptorService;
@ -38,8 +38,8 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
super.before(); super.before();
myPractitioner = createPractitionerAndUpdateLinks(buildPractitionerWithNameAndId("some_pract", "some_pract_id")); myPractitioner = createPractitionerAndUpdateLinks(buildPractitionerWithNameAndId("some_pract", "some_pract_id"));
myPractitionerId = new StringType(myPractitioner.getIdElement().getValue()); myPractitionerId = new StringType(myPractitioner.getIdElement().getValue());
myPractitionerPerson = getSourceResourceFromTargetResource(myPractitioner); myGoldenPractitier = getSourceResourceFromTargetResource(myPractitioner);
myPractitionerPersonId = new StringType(myPractitionerPerson.getIdElement().getValue()); myGoldenPractitionerId = new StringType(myGoldenPractitier.getIdElement().getValue());
myInterceptorService.registerAnonymousInterceptor(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, afterEmpiLatch); myInterceptorService.registerAnonymousInterceptor(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, afterEmpiLatch);
} }

View File

@ -3,9 +3,13 @@ package ca.uhn.fhir.jpa.empi.provider;
import ca.uhn.fhir.empi.api.EmpiConstants; import ca.uhn.fhir.empi.api.EmpiConstants;
import com.google.common.collect.Ordering; import com.google.common.collect.Ordering;
import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.Extension;
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.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.codesystems.MatchGrade; import org.hl7.fhir.r4.model.codesystems.MatchGrade;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -15,7 +19,9 @@ import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class EmpiProviderMatchR4Test extends BaseProviderR4Test { public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
@ -52,6 +58,63 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString()); assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
} }
@Test
public void testMedicationMatch() throws Exception {
Organization org = new Organization();
org.setId("Organization/mfr");
myOrganizationDao.update(org);
Medication medication = buildMedication();
Medication createdMedication = createMedication(medication);
Medication newMedication = buildMedication();
Bundle result = myEmpiProviderR4.serverMatch(newMedication, new StringType("Medication"));
assertEquals(1, result.getEntry().size());
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
assertEquals(createdMedication.getId(), entry0.getResource().getId());
Bundle.BundleEntrySearchComponent searchComponent = entry0.getSearch();
assertEquals(Bundle.SearchEntryMode.MATCH, searchComponent.getMode());
assertEquals(2.0 / 3.0, searchComponent.getScore().doubleValue(), 0.01);
Extension matchGradeExtension = searchComponent.getExtensionByUrl(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
assertNotNull(matchGradeExtension);
assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
}
private Medication buildMedication() {
Medication medication = new Medication();
medication.setManufacturer(new Reference("Organization/mfr"));
CodeableConcept codeableConcept = new CodeableConcept();
codeableConcept.addCoding().setSystem("zoop").setCode("boop");
medication.setCode(codeableConcept);
return medication;
}
@Test
public void testServerLevelMatch() throws Exception {
Patient jane = buildJanePatient();
jane.setActive(true);
Patient createdJane = createPatient(jane);
Patient newJane = buildJanePatient();
Bundle result = myEmpiProviderR4.serverMatch(newJane, new StringType("Patient"));
assertEquals(1, result.getEntry().size());
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
assertEquals(createdJane.getId(), entry0.getResource().getId());
Bundle.BundleEntrySearchComponent searchComponent = entry0.getSearch();
assertEquals(Bundle.SearchEntryMode.MATCH, searchComponent.getMode());
assertEquals(2.0 / 3.0, searchComponent.getScore().doubleValue(), 0.01);
Extension matchGradeExtension = searchComponent.getExtensionByUrl(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
assertNotNull(matchGradeExtension);
assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
}
@Test @Test
public void testMatchOrder() throws Exception { public void testMatchOrder() throws Exception {
Patient jane0 = buildJanePatient(); Patient jane0 = buildJanePatient();

View File

@ -82,7 +82,7 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test @Test
public void testQueryPossibleDuplicates() { public void testQueryPossibleDuplicates() {
Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails); Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result)); ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter(); List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1)); assertThat(list, hasSize(1));
@ -93,7 +93,7 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test @Test
public void testNotDuplicate() { public void testNotDuplicate() {
{ {
Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails); Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter(); List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1)); assertThat(list, hasSize(1));
} }
@ -103,7 +103,7 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
assertEquals("success", result.getParameterFirstRep().getName()); assertEquals("success", result.getParameterFirstRep().getName());
assertTrue(((BooleanType) (result.getParameterFirstRep().getValue())).booleanValue()); assertTrue(((BooleanType) (result.getParameterFirstRep().getValue())).booleanValue());
} }
Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails); Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter(); List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(0)); assertThat(list, hasSize(0));
} }
@ -120,9 +120,9 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
private void assertEmpiLink(int theExpectedSize, List<Parameters.ParametersParameterComponent> thePart, String thePersonId, String theTargetId, EmpiMatchResultEnum theMatchResult, String theEidMatch, String theNewPerson, String theScore) { private void assertEmpiLink(int theExpectedSize, List<Parameters.ParametersParameterComponent> thePart, String thePersonId, String theTargetId, EmpiMatchResultEnum theMatchResult, String theEidMatch, String theNewPerson, String theScore) {
assertThat(thePart, hasSize(theExpectedSize)); assertThat(thePart, hasSize(theExpectedSize));
assertThat(thePart.get(0).getName(), is("personId")); assertThat(thePart.get(0).getName(), is("goldenResourceId"));
assertThat(thePart.get(0).getValue().toString(), is(removeVersion(thePersonId))); assertThat(thePart.get(0).getValue().toString(), is(removeVersion(thePersonId)));
assertThat(thePart.get(1).getName(), is("targetId")); assertThat(thePart.get(1).getName(), is("targetResourceId"));
assertThat(thePart.get(1).getValue().toString(), is(removeVersion(theTargetId))); assertThat(thePart.get(1).getValue().toString(), is(removeVersion(theTargetId)));
if (theExpectedSize > 2) { if (theExpectedSize > 2) {
assertThat(thePart.get(2).getName(), is("matchResult")); assertThat(thePart.get(2).getName(), is("matchResult"));
@ -133,7 +133,7 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
assertThat(thePart.get(4).getName(), is("eidMatch")); assertThat(thePart.get(4).getName(), is("eidMatch"));
assertThat(thePart.get(4).getValue().primitiveValue(), is(theEidMatch)); assertThat(thePart.get(4).getValue().primitiveValue(), is(theEidMatch));
assertThat(thePart.get(5).getName(), is("newPerson")); assertThat(thePart.get(5).getName(), is("hadToCreateNewResource"));
assertThat(thePart.get(5).getValue().primitiveValue(), is(theNewPerson)); assertThat(thePart.get(5).getValue().primitiveValue(), is(theNewPerson));
assertThat(thePart.get(6).getName(), is("score")); assertThat(thePart.get(6).getName(), is("score"));

View File

@ -1,5 +1,6 @@
{ {
"version": "1", "version": "1",
"mdmTypes": ["Patient", "Practitioner", "Medication"],
"candidateSearchParams": [ "candidateSearchParams": [
{ {
"resourceType": "Patient", "resourceType": "Patient",
@ -13,6 +14,12 @@
"identifier" "identifier"
] ]
}, },
{
"resourceType": "Medication",
"searchParams": [
"manufacturer"
]
},
{ {
"resourceType": "Patient", "resourceType": "Patient",
"searchParams": [ "searchParams": [
@ -22,15 +29,30 @@
], ],
"candidateFilterSearchParams": [ "candidateFilterSearchParams": [
{ {
"resourceType": "*", "resourceType": "Practitioner",
"searchParam": "active",
"fixedValue": "true"
},
{
"resourceType": "Patient",
"searchParam": "active", "searchParam": "active",
"fixedValue": "true" "fixedValue": "true"
} }
], ],
"matchFields": [ "matchFields": [
{
"name": "cosine-code",
"resourceType": "Medication",
"resourcePath": "code",
"similarity": {
"algorithm": "COSINE",
"matchThreshold": 0.8,
"exact": true
}
},
{ {
"name": "cosine-given-name", "name": "cosine-given-name",
"resourceType": "*", "resourceType": "Patient",
"resourcePath": "name.given", "resourcePath": "name.given",
"similarity": { "similarity": {
"algorithm": "COSINE", "algorithm": "COSINE",
@ -40,7 +62,7 @@
}, },
{ {
"name": "jaro-last-name", "name": "jaro-last-name",
"resourceType": "*", "resourceType": "Patient",
"resourcePath": "name.family", "resourcePath": "name.family",
"similarity": { "similarity": {
"algorithm": "JARO_WINKLER", "algorithm": "JARO_WINKLER",
@ -50,7 +72,36 @@
}, },
{ {
"name": "medicare-id", "name": "medicare-id",
"resourceType": "*", "resourceType": "Patient",
"resourcePath": "identifier",
"matcher": {
"algorithm": "IDENTIFIER",
"identifierSystem": "http://hl7.org/fhir/sid/us-medicare"
}
},
{
"name": "cosine-given-name-pract",
"resourceType": "Practitioner",
"resourcePath": "name.given",
"similarity": {
"algorithm": "COSINE",
"matchThreshold": 0.8,
"exact": true
}
},
{
"name": "jaro-last-name-pract",
"resourceType": "Practitioner",
"resourcePath": "name.family",
"similarity": {
"algorithm": "JARO_WINKLER",
"matchThreshold": 0.8,
"exact": true
}
},
{
"name": "medicare-id-pract",
"resourceType": "Practitioner",
"resourcePath": "identifier", "resourcePath": "identifier",
"matcher": { "matcher": {
"algorithm": "IDENTIFIER", "algorithm": "IDENTIFIER",
@ -60,7 +111,10 @@
], ],
"matchResultMap": { "matchResultMap": {
"cosine-given-name": "POSSIBLE_MATCH", "cosine-given-name": "POSSIBLE_MATCH",
"cosine-given-name,jaro-last-name": "MATCH" "cosine-given-name,jaro-last-name": "MATCH",
"cosine-given-name-pract": "POSSIBLE_MATCH",
"cosine-given-name-pract,jaro-last-name-pract": "MATCH",
"cosine-code": "MATCH"
}, },
"eidSystem": "http://company.io/fhir/NamingSystem/custom-eid-system" "eidSystem": "http://company.io/fhir/NamingSystem/custom-eid-system"
} }

View File

@ -26,8 +26,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date; import java.util.Date;
public class EmpiLinkJson implements IModelJson { public class EmpiLinkJson implements IModelJson {
@JsonProperty("personId") @JsonProperty("goldenResourceId")
private String myPersonId; private String myGoldenResourceId;
@JsonProperty("targetId") @JsonProperty("targetId")
private String myTargetId; private String myTargetId;
@ -52,8 +52,8 @@ public class EmpiLinkJson implements IModelJson {
private Boolean myEidMatch; private Boolean myEidMatch;
/** This link created a new person **/ /** This link created a new person **/
@JsonProperty("newPerson") @JsonProperty("linkCreatedNewGoldenResource")
private Boolean myNewPerson; private Boolean myLinkCreatedNewResource;
@JsonProperty("vector") @JsonProperty("vector")
private Long myVector; private Long myVector;
@ -61,12 +61,12 @@ public class EmpiLinkJson implements IModelJson {
@JsonProperty("score") @JsonProperty("score")
private Double myScore; private Double myScore;
public String getPersonId() { public String getGoldenResourceId() {
return myPersonId; return myGoldenResourceId;
} }
public EmpiLinkJson setPersonId(String thePersonId) { public EmpiLinkJson setGoldenResourceId(String theGoldenResourceId) {
myPersonId = thePersonId; myGoldenResourceId = theGoldenResourceId;
return this; return this;
} }
@ -133,12 +133,12 @@ public class EmpiLinkJson implements IModelJson {
return this; return this;
} }
public Boolean getNewPerson() { public Boolean getLinkCreatedNewResource() {
return myNewPerson; return myLinkCreatedNewResource;
} }
public EmpiLinkJson setNewPerson(Boolean theNewPerson) { public EmpiLinkJson setLinkCreatedNewResource(Boolean theLinkCreatedNewResource) {
myNewPerson = theNewPerson; myLinkCreatedNewResource = theLinkCreatedNewResource;
return this; return this;
} }

View File

@ -28,8 +28,8 @@ import java.util.stream.Stream;
public interface IEmpiControllerSvc { public interface IEmpiControllerSvc {
Stream<EmpiLinkJson> queryLinks(@Nullable String thePersonId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, EmpiTransactionContext theEmpiContext); Stream<EmpiLinkJson> queryLinks(@Nullable String thePersonId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, EmpiTransactionContext theEmpiContext);
Stream<EmpiLinkJson> getDuplicatePersons(EmpiTransactionContext theEmpiContext); Stream<EmpiLinkJson> getDuplicateGoldenResources(EmpiTransactionContext theEmpiContext);
void notDuplicatePerson(String thePersonId, String theTargetPersonId, EmpiTransactionContext theEmpiContext); void notDuplicateGoldenResource(String thePersonId, String theTargetPersonId, EmpiTransactionContext theEmpiContext);
IAnyResource mergePersons(String theFromPersonId, String theToPersonId, EmpiTransactionContext theEmpiTransactionContext); IAnyResource mergeGoldenResources(String theFromPersonId, String theToPersonId, EmpiTransactionContext theEmpiTransactionContext);
IAnyResource updateLink(String thePersonId, String theTargetId, String theMatchResult, EmpiTransactionContext theEmpiContext); IAnyResource updateLink(String thePersonId, String theTargetId, String theMatchResult, EmpiTransactionContext theEmpiContext);
} }

View File

@ -53,7 +53,7 @@ public interface IEmpiSubmitSvc {
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for EMPI processing. * @param theCriteria The FHIR search critieria for filtering the resources to be submitted for EMPI processing.
* @return the number of resources submitted for EMPI processing. * @return the number of resources submitted for EMPI processing.
*/ */
long submitPractitionerTypeToEmpi(String theCriteria); long submitPractitionerTypeToMdm(String theCriteria);
/** /**
* Convenience method that calls {@link #submitTargetTypeToEmpi(String, String)} with the type pre-populated. * Convenience method that calls {@link #submitTargetTypeToEmpi(String, String)} with the type pre-populated.
@ -61,13 +61,13 @@ public interface IEmpiSubmitSvc {
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for EMPI processing. * @param theCriteria The FHIR search critieria for filtering the resources to be submitted for EMPI processing.
* @return the number of resources submitted for EMPI processing. * @return the number of resources submitted for EMPI processing.
*/ */
long submitPatientTypeToEmpi(String theCriteria); long submitPatientTypeToMdm(String theCriteria);
/** /**
* Given an ID and a target type valid for EMPI, manually submit the given ID for EMPI processing. * Given an ID and a target type valid for EMPI, manually submit the given ID for EMPI processing.
* @param theId the ID of the resource to process for EMPI. * @param theId the ID of the resource to process for EMPI.
* @return the constant `1`, as if this function returns successfully, it will have processed one resource for EMPI. * @return the constant `1`, as if this function returns successfully, it will have processed one resource for EMPI.
*/ */
long submitTargetToEmpi(IIdType theId); long submitTargetToMdm(IIdType theId);
} }

View File

@ -30,7 +30,7 @@ public class EmpiTransactionContext {
SUBMIT_RESOURCE_TO_EMPI, SUBMIT_RESOURCE_TO_EMPI,
QUERY_LINKS, QUERY_LINKS,
UPDATE_LINK, UPDATE_LINK,
DUPLICATE_PERSONS, DUPLICATE_GOLDEN_RESOURCES,
NOT_DUPLICATE, NOT_DUPLICATE,
MERGE_PERSONS MERGE_PERSONS
} }

View File

@ -43,10 +43,10 @@ public abstract class BaseEmpiProvider {
myFhirContext = theFhirContext; myFhirContext = theFhirContext;
} }
protected void validateMergeParameters(IPrimitiveType<String> theFromPersonId, IPrimitiveType<String> theToPersonId) { protected void validateMergeParameters(IPrimitiveType<String> theFromGoldenResourceId, IPrimitiveType<String> theToGoldenResourceId) {
validateNotNull(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPersonId); validateNotNull(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId);
validateNotNull(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPersonId); validateNotNull(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToGoldenResourceId);
if (theFromPersonId.getValue().equals(theToPersonId.getValue())) { if (theFromGoldenResourceId.getValue().equals(theToGoldenResourceId.getValue())) {
throw new InvalidRequestException("fromPersonId must be different from toPersonId"); throw new InvalidRequestException("fromPersonId must be different from toPersonId");
} }
} }
@ -57,9 +57,9 @@ public abstract class BaseEmpiProvider {
} }
} }
protected void validateUpdateLinkParameters(IPrimitiveType<String> thePersonId, IPrimitiveType<String> theTargetId, IPrimitiveType<String> theMatchResult) { protected void validateUpdateLinkParameters(IPrimitiveType<String> theGoldenResourceId, IPrimitiveType<String> theResourceId, IPrimitiveType<String> theMatchResult) {
validateNotNull(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, thePersonId); validateNotNull(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
validateNotNull(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetId); validateNotNull(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theResourceId);
validateNotNull(ProviderConstants.MDM_UPDATE_LINK_MATCH_RESULT, theMatchResult); validateNotNull(ProviderConstants.MDM_UPDATE_LINK_MATCH_RESULT, theMatchResult);
EmpiMatchResultEnum matchResult = EmpiMatchResultEnum.valueOf(theMatchResult.getValue()); EmpiMatchResultEnum matchResult = EmpiMatchResultEnum.valueOf(theMatchResult.getValue());
switch (matchResult) { switch (matchResult) {
@ -72,12 +72,12 @@ public abstract class BaseEmpiProvider {
} }
} }
protected void validateNotDuplicateParameters(IPrimitiveType<String> thePersonId, IPrimitiveType<String> theTargetId) { protected void validateNotDuplicateParameters(IPrimitiveType<String> theGoldenResourceId, IPrimitiveType<String> theResourceId) {
validateNotNull(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, thePersonId); validateNotNull(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
validateNotNull(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetId); validateNotNull(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theResourceId);
} }
protected EmpiTransactionContext createEmpiContext(RequestDetails theRequestDetails, EmpiTransactionContext.OperationType theOperationType) { protected EmpiTransactionContext createMdmContext(RequestDetails theRequestDetails, EmpiTransactionContext.OperationType theOperationType) {
TransactionLogMessages transactionLogMessages = TransactionLogMessages.createFromTransactionGuid(theRequestDetails.getTransactionGuid()); TransactionLogMessages transactionLogMessages = TransactionLogMessages.createFromTransactionGuid(theRequestDetails.getTransactionGuid());
return new EmpiTransactionContext(transactionLogMessages, theOperationType); return new EmpiTransactionContext(transactionLogMessages, theOperationType);
} }
@ -94,17 +94,18 @@ public abstract class BaseEmpiProvider {
theEmpiLinkStream.forEach(empiLink -> { theEmpiLinkStream.forEach(empiLink -> {
IBase resultPart = ParametersUtil.addParameterToParameters(myFhirContext, retval, "link"); IBase resultPart = ParametersUtil.addParameterToParameters(myFhirContext, retval, "link");
ParametersUtil.addPartString(myFhirContext, resultPart, "personId", empiLink.getPersonId()); ParametersUtil.addPartString(myFhirContext, resultPart, "goldenResourceId", empiLink.getGoldenResourceId());
ParametersUtil.addPartString(myFhirContext, resultPart, "targetId", empiLink.getTargetId()); ParametersUtil.addPartString(myFhirContext, resultPart, "targetResourceId", empiLink.getTargetId());
if (includeResultAndSource) { if (includeResultAndSource) {
ParametersUtil.addPartString(myFhirContext, resultPart, "matchResult", empiLink.getMatchResult().name()); ParametersUtil.addPartString(myFhirContext, resultPart, "matchResult", empiLink.getMatchResult().name());
ParametersUtil.addPartString(myFhirContext, resultPart, "linkSource", empiLink.getLinkSource().name()); ParametersUtil.addPartString(myFhirContext, resultPart, "linkSource", empiLink.getLinkSource().name());
ParametersUtil.addPartBoolean(myFhirContext, resultPart, "eidMatch", empiLink.getEidMatch()); ParametersUtil.addPartBoolean(myFhirContext, resultPart, "eidMatch", empiLink.getEidMatch());
ParametersUtil.addPartBoolean(myFhirContext, resultPart, "newPerson", empiLink.getNewPerson()); ParametersUtil.addPartBoolean(myFhirContext, resultPart, "hadToCreateNewResource", empiLink.getLinkCreatedNewResource());
ParametersUtil.addPartDecimal(myFhirContext, resultPart, "score", empiLink.getScore()); ParametersUtil.addPartDecimal(myFhirContext, resultPart, "score", empiLink.getScore());
} }
}); });
return retval; return retval;
} }
} }

View File

@ -57,7 +57,7 @@ public class EmpiControllerHelper {
} }
public IAnyResource getLatestPersonFromIdOrThrowException(String theParamName, String theId) { public IAnyResource getLatestPersonFromIdOrThrowException(String theParamName, String theId) {
IdDt personId = EmpiControllerUtil.getPersonIdDtOrThrowException(theParamName, theId); IdDt personId = EmpiControllerUtil.getGoldenIdDtOrThrowException(theParamName, theId);
return loadResource(personId.toUnqualifiedVersionless()); return loadResource(personId.toUnqualifiedVersionless());
} }

View File

@ -46,7 +46,7 @@ public class EmpiControllerUtil {
if (thePersonId == null) { if (thePersonId == null) {
return null; return null;
} }
return getPersonIdDtOrThrowException(theName, thePersonId); return getGoldenIdDtOrThrowException(theName, thePersonId);
} }
public static IIdType extractTargetIdDtOrNull(String theName, String theTargetId) { public static IIdType extractTargetIdDtOrNull(String theName, String theTargetId) {
@ -56,13 +56,13 @@ public class EmpiControllerUtil {
return getTargetIdDtOrThrowException(theName, theTargetId); return getTargetIdDtOrThrowException(theName, theTargetId);
} }
static IdDt getPersonIdDtOrThrowException(String theParamName, String theId) { static IdDt getGoldenIdDtOrThrowException(String theParamName, String theId) {
IdDt personId = new IdDt(theId); IdDt goldenResourceId = new IdDt(theId);
if (!"Person".equals(personId.getResourceType()) || //TODO GGG MDM: maybe add a gate here to only consider resources that can possibly be EMPI'ed?
personId.getIdPart() == null) { if (goldenResourceId.getIdPart() == null) {
throw new InvalidRequestException(theParamName + " is '" + theId + "'. must have form Person/<id> where <id> is the id of the person"); throw new InvalidRequestException(theParamName + " is '" + theId + "'. must have form <resourceType>/<id> where <id> is the id of the resource");
} }
return personId; return goldenResourceId;
} }
public static IIdType getTargetIdDtOrThrowException(String theParamName, String theId) { public static IIdType getTargetIdDtOrThrowException(String theParamName, String theId) {

View File

@ -49,8 +49,10 @@ import org.hl7.fhir.dstu3.model.Practitioner;
import org.hl7.fhir.dstu3.model.Resource; import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.codesystems.MatchGrade; import org.hl7.fhir.dstu3.model.codesystems.MatchGrade;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import javax.annotation.Nonnull;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -77,33 +79,23 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
} }
@Operation(name = ProviderConstants.EMPI_MATCH, type = Patient.class) @Operation(name = ProviderConstants.EMPI_MATCH, type = Patient.class)
public Bundle match(@OperationParam(name = ProviderConstants.EMPI_MATCH_RESOURCE, min = 1, max = 1) Patient thePatient) { public Bundle match(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) Patient thePatient) {
if (thePatient == null) { if (thePatient == null) {
throw new InvalidRequestException("resource may not be null"); throw new InvalidRequestException("resource may not be null");
} }
List<MatchedTarget> matches = myEmpiMatchFinderSvc.getMatchedTargets("Patient", thePatient); return getMatchesAndPossibleMatchesForResource(thePatient, "Patient");
matches.sort(Comparator.comparing((MatchedTarget m) -> m.getMatchResult().getNormalizedScore()).reversed()); }
Bundle retVal = new Bundle(); @Operation(name = ProviderConstants.MDM_MATCH)
retVal.setType(Bundle.BundleType.SEARCHSET); public Bundle serverMatch(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) IAnyResource theResource,
retVal.setId(UUID.randomUUID().toString()); @OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 1, max = 1) StringType theResourceType
retVal.getMeta().setLastUpdatedElement(InstantType.now()); ) {
if (theResource == null) {
for (MatchedTarget next : matches) { throw new InvalidRequestException("resource may not be null");
boolean shouldKeepThisEntry = next.isMatch() || next.isPossibleMatch();
if (!shouldKeepThisEntry) {
continue;
}
Bundle.BundleEntryComponent entry = new Bundle.BundleEntryComponent();
entry.setResource((Resource) next.getTarget());
entry.setSearch(toBundleEntrySearchComponent(next));
retVal.addEntry(entry);
} }
return getMatchesAndPossibleMatchesForResource(theResource, theResourceType.getValueNotNull());
return retVal;
} }
private Bundle.BundleEntrySearchComponent toBundleEntrySearchComponent(MatchedTarget theMatchedTarget) { private Bundle.BundleEntrySearchComponent toBundleEntrySearchComponent(MatchedTarget theMatchedTarget) {
@ -111,12 +103,7 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
searchComponent.setMode(Bundle.SearchEntryMode.MATCH); searchComponent.setMode(Bundle.SearchEntryMode.MATCH);
searchComponent.setScore(theMatchedTarget.getMatchResult().getNormalizedScore()); searchComponent.setScore(theMatchedTarget.getMatchResult().getNormalizedScore());
MatchGrade matchGrade = MatchGrade.PROBABLE; MatchGrade matchGrade = getMatchGrade(theMatchedTarget);
if (theMatchedTarget.isMatch()) {
matchGrade = MatchGrade.CERTAIN;
} else if (theMatchedTarget.isPossibleMatch()) {
matchGrade = MatchGrade.POSSIBLE;
}
searchComponent.addExtension(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE, new CodeType(matchGrade.toCode())); searchComponent.addExtension(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE, new CodeType(matchGrade.toCode()));
return searchComponent; return searchComponent;
@ -128,7 +115,7 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
RequestDetails theRequestDetails) { RequestDetails theRequestDetails) {
validateMergeParameters(theFromPersonId, theToPersonId); validateMergeParameters(theFromPersonId, theToPersonId);
return (Person) myEmpiControllerSvc.mergePersons(theFromPersonId.getValue(), theToPersonId.getValue(), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.MERGE_PERSONS)); return (Person) myEmpiControllerSvc.mergeGoldenResources(theFromPersonId.getValue(), theToPersonId.getValue(), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.MERGE_PERSONS));
} }
@Operation(name = ProviderConstants.MDM_UPDATE_LINK, type = Person.class) @Operation(name = ProviderConstants.MDM_UPDATE_LINK, type = Person.class)
@ -139,7 +126,7 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
validateUpdateLinkParameters(thePersonId, theTargetId, theMatchResult); validateUpdateLinkParameters(thePersonId, theTargetId, theMatchResult);
return (Person) myEmpiControllerSvc.updateLink(thePersonId.getValue(), theTargetId.getValue(), theMatchResult.getValue(), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.UPDATE_LINK)); return (Person) myEmpiControllerSvc.updateLink(thePersonId.getValue(), theTargetId.getValue(), theMatchResult.getValue(), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.UPDATE_LINK));
} }
@Operation(name = ProviderConstants.MDM_QUERY_LINKS) @Operation(name = ProviderConstants.MDM_QUERY_LINKS)
@ -149,35 +136,35 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_MATCH_RESULT, min = 0, max = 1) StringType theLinkSource, @OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_MATCH_RESULT, min = 0, max = 1) StringType theLinkSource,
ServletRequestDetails theRequestDetails) { ServletRequestDetails theRequestDetails) {
Stream<EmpiLinkJson> empiLinkJson = myEmpiControllerSvc.queryLinks(extractStringOrNull(thePersonId), extractStringOrNull(theTargetId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.QUERY_LINKS)); Stream<EmpiLinkJson> empiLinkJson = myEmpiControllerSvc.queryLinks(extractStringOrNull(thePersonId), extractStringOrNull(theTargetId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.QUERY_LINKS));
return (Parameters) parametersFromEmpiLinks(empiLinkJson, true); return (Parameters) parametersFromEmpiLinks(empiLinkJson, true);
} }
@Operation(name = ProviderConstants.EMPI_DUPLICATE_PERSONS) @Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES)
public Parameters getDuplicatePersons(ServletRequestDetails theRequestDetails) { public Parameters getDuplicatePersons(ServletRequestDetails theRequestDetails) {
Stream<EmpiLinkJson> possibleDuplicates = myEmpiControllerSvc.getDuplicatePersons(createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.QUERY_LINKS)); Stream<EmpiLinkJson> possibleDuplicates = myEmpiControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.QUERY_LINKS));
return (Parameters) parametersFromEmpiLinks(possibleDuplicates, false); return (Parameters) parametersFromEmpiLinks(possibleDuplicates, false);
} }
@Operation(name = ProviderConstants.EMPI_NOT_DUPLICATE) @Operation(name = ProviderConstants.MDM_NOT_DUPLICATE)
// TODO KHS can this return void? // TODO KHS can this return void?
public Parameters notDuplicate(@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType thePersonId, public Parameters notDuplicate(@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType thePersonId,
@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 1, max = 1) StringType theTargetId, @OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 1, max = 1) StringType theTargetId,
ServletRequestDetails theRequestDetails) { ServletRequestDetails theRequestDetails) {
validateNotDuplicateParameters(thePersonId, theTargetId); validateNotDuplicateParameters(thePersonId, theTargetId);
myEmpiControllerSvc.notDuplicatePerson(thePersonId.getValue(), theTargetId.getValue(), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.NOT_DUPLICATE)); myEmpiControllerSvc.notDuplicateGoldenResource(thePersonId.getValue(), theTargetId.getValue(), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.NOT_DUPLICATE));
Parameters retval = (Parameters) ParametersUtil.newInstance(myFhirContext); Parameters retval = (Parameters) ParametersUtil.newInstance(myFhirContext);
ParametersUtil.addParameterToParametersBoolean(myFhirContext, retval, "success", true); ParametersUtil.addParameterToParametersBoolean(myFhirContext, retval, "success", true);
return retval; return retval;
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= DecimalType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= DecimalType.class)
}) })
public Parameters empiBatchOnAllTargets( public Parameters empiBatchOnAllTargets(
@OperationParam(name= ProviderConstants.EMPI_BATCH_RUN_CRITERIA,min = 0 , max = 1) StringType theCriteria, @OperationParam(name= ProviderConstants.MDM_BATCH_RUN_CRITERIA,min = 0 , max = 1) StringType theCriteria,
ServletRequestDetails theRequestDetails) { ServletRequestDetails theRequestDetails) {
String criteria = convertCriteriaToString(theCriteria); String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiBatchSvc.submitAllTargetTypesToEmpi(criteria); long submittedCount = myEmpiBatchSvc.submitAllTargetTypesToEmpi(criteria);
@ -205,45 +192,45 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
return parameters; return parameters;
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, type = Patient.class, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
}) })
public Parameters empiBatchPatientInstance( public Parameters empiBatchPatientInstance(
@IdParam IIdType theIdParam, @IdParam IIdType theIdParam,
RequestDetails theRequest) { RequestDetails theRequest) {
long submittedCount = myEmpiBatchSvc.submitTargetToEmpi(theIdParam); long submittedCount = myEmpiBatchSvc.submitTargetToMdm(theIdParam);
return buildEmpiOutParametersWithCount(submittedCount); return buildEmpiOutParametersWithCount(submittedCount);
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, type = Patient.class, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
}) })
public Parameters empiBatchPatientType( public Parameters empiBatchPatientType(
@OperationParam(name = ProviderConstants.EMPI_BATCH_RUN_CRITERIA) StringType theCriteria, @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
RequestDetails theRequest) { RequestDetails theRequest) {
String criteria = convertCriteriaToString(theCriteria); String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiBatchSvc.submitPatientTypeToEmpi(criteria); long submittedCount = myEmpiBatchSvc.submitPatientTypeToMdm(criteria);
return buildEmpiOutParametersWithCount(submittedCount); return buildEmpiOutParametersWithCount(submittedCount);
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
}) })
public Parameters empiBatchPractitionerInstance( public Parameters empiBatchPractitionerInstance(
@IdParam IIdType theIdParam, @IdParam IIdType theIdParam,
RequestDetails theRequest) { RequestDetails theRequest) {
long submittedCount = myEmpiBatchSvc.submitTargetToEmpi(theIdParam); long submittedCount = myEmpiBatchSvc.submitTargetToMdm(theIdParam);
return buildEmpiOutParametersWithCount(submittedCount); return buildEmpiOutParametersWithCount(submittedCount);
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
}) })
public Parameters empiBatchPractitionerType( public Parameters empiBatchPractitionerType(
@OperationParam(name = ProviderConstants.EMPI_BATCH_RUN_CRITERIA) StringType theCriteria, @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
RequestDetails theRequest) { RequestDetails theRequest) {
String criteria = convertCriteriaToString(theCriteria); String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiBatchSvc.submitPractitionerTypeToEmpi(criteria); long submittedCount = myEmpiBatchSvc.submitPractitionerTypeToMdm(criteria);
return buildEmpiOutParametersWithCount(submittedCount); return buildEmpiOutParametersWithCount(submittedCount);
} }
@ -257,4 +244,39 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
.setValue(new DecimalType(theCount)); .setValue(new DecimalType(theCount));
return parameters; return parameters;
} }
private Bundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theResourceType) {
List<MatchedTarget> matches = myEmpiMatchFinderSvc.getMatchedTargets(theResourceType, theResource);
matches.sort(Comparator.comparing((MatchedTarget m) -> m.getMatchResult().getNormalizedScore()).reversed());
Bundle retVal = new Bundle();
retVal.setType(Bundle.BundleType.SEARCHSET);
retVal.setId(UUID.randomUUID().toString());
retVal.getMeta().setLastUpdatedElement(InstantType.now());
for (MatchedTarget next : matches) {
boolean shouldKeepThisEntry = next.isMatch() || next.isPossibleMatch();
if (!shouldKeepThisEntry) {
continue;
}
Bundle.BundleEntryComponent entry = new Bundle.BundleEntryComponent();
entry.setResource((Resource) next.getTarget());
entry.setSearch(toBundleEntrySearchComponent(next));
retVal.addEntry(entry);
}
return retVal;
}
@Nonnull
protected MatchGrade getMatchGrade(MatchedTarget theTheMatchedTarget) {
MatchGrade matchGrade = MatchGrade.PROBABLE;
if (theTheMatchedTarget.isMatch()) {
matchGrade = MatchGrade.CERTAIN;
} else if (theTheMatchedTarget.isPossibleMatch()) {
matchGrade = MatchGrade.POSSIBLE;
}
return matchGrade;
}
} }

View File

@ -38,6 +38,7 @@ import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.ParametersUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle;
@ -52,6 +53,7 @@ import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.codesystems.MatchGrade; import org.hl7.fhir.r4.model.codesystems.MatchGrade;
import javax.annotation.Nonnull;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -61,7 +63,7 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
private final IEmpiControllerSvc myEmpiControllerSvc; private final IEmpiControllerSvc myEmpiControllerSvc;
private final IEmpiMatchFinderSvc myEmpiMatchFinderSvc; private final IEmpiMatchFinderSvc myEmpiMatchFinderSvc;
private final IEmpiExpungeSvc myMdmExpungeSvc; private final IEmpiExpungeSvc myMdmExpungeSvc;
private final IEmpiSubmitSvc myEmpiSubmitSvc; private final IEmpiSubmitSvc myMdmSubmitSvc;
/** /**
* Constructor * Constructor
@ -69,21 +71,45 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
* Note that this is not a spring bean. Any necessary injections should * Note that this is not a spring bean. Any necessary injections should
* happen in the constructor * happen in the constructor
*/ */
public EmpiProviderR4(FhirContext theFhirContext, IEmpiControllerSvc theEmpiControllerSvc, IEmpiMatchFinderSvc theEmpiMatchFinderSvc, IEmpiExpungeSvc theMdmExpungeSvc, IEmpiSubmitSvc theEmpiSubmitSvc) { public EmpiProviderR4(FhirContext theFhirContext, IEmpiControllerSvc theEmpiControllerSvc, IEmpiMatchFinderSvc theEmpiMatchFinderSvc, IEmpiExpungeSvc theMdmExpungeSvc, IEmpiSubmitSvc theMdmSubmitSvc) {
super(theFhirContext); super(theFhirContext);
myEmpiControllerSvc = theEmpiControllerSvc; myEmpiControllerSvc = theEmpiControllerSvc;
myEmpiMatchFinderSvc = theEmpiMatchFinderSvc; myEmpiMatchFinderSvc = theEmpiMatchFinderSvc;
myMdmExpungeSvc = theMdmExpungeSvc; myMdmExpungeSvc = theMdmExpungeSvc;
myEmpiSubmitSvc = theEmpiSubmitSvc; myMdmSubmitSvc = theMdmSubmitSvc;
} }
@Operation(name = ProviderConstants.EMPI_MATCH, type = Patient.class) @Operation(name = ProviderConstants.EMPI_MATCH, type = Patient.class)
public Bundle match(@OperationParam(name = ProviderConstants.EMPI_MATCH_RESOURCE, min = 1, max = 1) Patient thePatient) { public Bundle match(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) Patient thePatient) {
if (thePatient == null) { if (thePatient == null) {
throw new InvalidRequestException("resource may not be null"); throw new InvalidRequestException("resource may not be null");
} }
Bundle retVal = getMatchesAndPossibleMatchesForResource(thePatient, "Patient");
return retVal;
}
@Operation(name = ProviderConstants.MDM_MATCH)
public Bundle serverMatch(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) IAnyResource theResource,
@OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 1, max = 1) StringType theResourceType
) {
if (theResource == null) {
throw new InvalidRequestException("resource may not be null");
}
Bundle retVal = getMatchesAndPossibleMatchesForResource(theResource, theResourceType.getValueNotNull());
return retVal;
}
/**
* Helper method which will return a bundle of all Matches and Possible Matches.
* @param theResource
* @param theTheValueNotNull
* @return
*/
private Bundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theTheValueNotNull) {
List<MatchedTarget> matches = myEmpiMatchFinderSvc.getMatchedTargets(theTheValueNotNull, theResource);
List<MatchedTarget> matches = myEmpiMatchFinderSvc.getMatchedTargets("Patient", thePatient);
matches.sort(Comparator.comparing((MatchedTarget m) -> m.getMatchResult().getNormalizedScore()).reversed()); matches.sort(Comparator.comparing((MatchedTarget m) -> m.getMatchResult().getNormalizedScore()).reversed());
Bundle retVal = new Bundle(); Bundle retVal = new Bundle();
@ -103,45 +129,39 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
retVal.addEntry(entry); retVal.addEntry(entry);
} }
return retVal; return retVal;
} }
private Bundle.BundleEntrySearchComponent toBundleEntrySearchComponent(MatchedTarget theMatchedTarget) { private Bundle.BundleEntrySearchComponent toBundleEntrySearchComponent(MatchedTarget theMatchedTarget) {
Bundle.BundleEntrySearchComponent searchComponent = new Bundle.BundleEntrySearchComponent(); Bundle.BundleEntrySearchComponent searchComponent = new Bundle.BundleEntrySearchComponent();
searchComponent.setMode(Bundle.SearchEntryMode.MATCH); searchComponent.setMode(Bundle.SearchEntryMode.MATCH);
searchComponent.setScore(theMatchedTarget.getMatchResult().getNormalizedScore()); searchComponent.setScore(theMatchedTarget.getMatchResult().getNormalizedScore());
MatchGrade matchGrade = MatchGrade.PROBABLE; MatchGrade matchGrade = getMatchGrade(theMatchedTarget);
if (theMatchedTarget.isMatch()) {
matchGrade = MatchGrade.CERTAIN;
} else if (theMatchedTarget.isPossibleMatch()) {
matchGrade = MatchGrade.POSSIBLE;
}
searchComponent.addExtension(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE, new CodeType(matchGrade.toCode())); searchComponent.addExtension(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE, new CodeType(matchGrade.toCode()));
return searchComponent; return searchComponent;
} }
//TODO GGG ask ken, what is the best way to genericize this? Return
@Operation(name = ProviderConstants.MDM_MERGE_GOLDEN_RESOURCES) @Operation(name = ProviderConstants.MDM_MERGE_GOLDEN_RESOURCES)
public IBaseResource mergeGoldenResources(@OperationParam(name=ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType theFromGoldenResourceId, public IBaseResource mergeGoldenResources(@OperationParam(name=ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType theFromGoldenResourceId,
@OperationParam(name=ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType theToGoldenResourceId, @OperationParam(name=ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType theToGoldenResourceId,
RequestDetails theRequestDetails) { RequestDetails theRequestDetails) {
validateMergeParameters(theFromGoldenResourceId, theToGoldenResourceId); validateMergeParameters(theFromGoldenResourceId, theToGoldenResourceId);
return myEmpiControllerSvc.mergePersons(theFromGoldenResourceId.getValue(), theToGoldenResourceId.getValue(), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.MERGE_PERSONS)); return myEmpiControllerSvc.mergeGoldenResources(theFromGoldenResourceId.getValue(), theToGoldenResourceId.getValue(), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.MERGE_PERSONS));
} }
@Operation(name = ProviderConstants.MDM_UPDATE_LINK) @Operation(name = ProviderConstants.MDM_UPDATE_LINK)
public IBaseResource updateLink(@OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType thePersonId, public IBaseResource updateLink(@OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType theGoldenResourceId,
@OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, min = 1, max = 1) StringType theTargetId, @OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, min = 1, max = 1) StringType theResourceId,
@OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_MATCH_RESULT, min = 1, max = 1) StringType theMatchResult, @OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_MATCH_RESULT, min = 1, max = 1) StringType theMatchResult,
ServletRequestDetails theRequestDetails) { ServletRequestDetails theRequestDetails) {
validateUpdateLinkParameters(thePersonId, theTargetId, theMatchResult); validateUpdateLinkParameters(theGoldenResourceId, theResourceId, theMatchResult);
return myEmpiControllerSvc.updateLink(thePersonId.getValueNotNull(), theTargetId.getValue(), theMatchResult.getValue(), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.UPDATE_LINK)); return myEmpiControllerSvc.updateLink(theGoldenResourceId.getValueNotNull(), theResourceId.getValue(), theMatchResult.getValue(), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.UPDATE_LINK));
} }
@Operation(name = ProviderConstants.MDM_CLEAR, returnParameters = { @Operation(name = ProviderConstants.MDM_CLEAR, returnParameters = {
@ -163,100 +183,112 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
@Operation(name = ProviderConstants.MDM_QUERY_LINKS, idempotent = true) @Operation(name = ProviderConstants.MDM_QUERY_LINKS, idempotent = true)
public Parameters queryLinks(@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, min = 0, max = 1) StringType thePersonId, public Parameters queryLinks(@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, min = 0, max = 1) StringType theGoldenResourceId,
@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 0, max = 1) StringType theTargetId, @OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 0, max = 1) StringType theResourceId,
@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_MATCH_RESULT, min = 0, max = 1) StringType theMatchResult, @OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_MATCH_RESULT, min = 0, max = 1) StringType theMatchResult,
@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1) StringType theLinkSource, @OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1) StringType theLinkSource,
ServletRequestDetails theRequestDetails) { ServletRequestDetails theRequestDetails) {
Stream<EmpiLinkJson> empiLinkJson = myEmpiControllerSvc.queryLinks(extractStringOrNull(thePersonId), extractStringOrNull(theTargetId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.QUERY_LINKS)); Stream<EmpiLinkJson> empiLinkJson = myEmpiControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId), extractStringOrNull(theResourceId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.QUERY_LINKS));
return (Parameters) parametersFromEmpiLinks(empiLinkJson, true); return (Parameters) parametersFromEmpiLinks(empiLinkJson, true);
} }
@Operation(name = ProviderConstants.EMPI_DUPLICATE_PERSONS, idempotent = true) @Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES, idempotent = true)
public Parameters getDuplicatePersons(ServletRequestDetails theRequestDetails) { public Parameters getDuplicateGoldenResources(ServletRequestDetails theRequestDetails) {
Stream<EmpiLinkJson> possibleDuplicates = myEmpiControllerSvc.getDuplicatePersons(createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.DUPLICATE_PERSONS)); Stream<EmpiLinkJson> possibleDuplicates = myEmpiControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES));
return (Parameters) parametersFromEmpiLinks(possibleDuplicates, false); return (Parameters) parametersFromEmpiLinks(possibleDuplicates, false);
} }
@Operation(name = ProviderConstants.EMPI_NOT_DUPLICATE) @Operation(name = ProviderConstants.MDM_NOT_DUPLICATE)
public Parameters notDuplicate(@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType thePersonId, public Parameters notDuplicate(@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType theGoldenResourceId,
@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 1, max = 1) StringType theTargetId, @OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 1, max = 1) StringType theResourceId,
ServletRequestDetails theRequestDetails) { ServletRequestDetails theRequestDetails) {
validateNotDuplicateParameters(thePersonId, theTargetId); validateNotDuplicateParameters(theGoldenResourceId, theResourceId);
myEmpiControllerSvc.notDuplicatePerson(thePersonId.getValue(), theTargetId.getValue(), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.NOT_DUPLICATE)); myEmpiControllerSvc.notDuplicateGoldenResource(theGoldenResourceId.getValue(), theResourceId.getValue(), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.NOT_DUPLICATE));
Parameters retval = (Parameters) ParametersUtil.newInstance(myFhirContext); Parameters retval = (Parameters) ParametersUtil.newInstance(myFhirContext);
ParametersUtil.addParameterToParametersBoolean(myFhirContext, retval, "success", true); ParametersUtil.addParameterToParametersBoolean(myFhirContext, retval, "success", true);
return retval; return retval;
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= IntegerType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= IntegerType.class)
}) })
public Parameters empiBatchOnAllTargets( public Parameters empiBatchOnAllTargets(
@OperationParam(name= ProviderConstants.EMPI_BATCH_RUN_CRITERIA,min = 0 , max = 1) StringType theCriteria, //TODO GGG MDM: also have to take it an optional resourceType here, to clarify which resources should have MDM run on them.
@OperationParam(name= ProviderConstants.MDM_BATCH_RUN_CRITERIA,min = 0 , max = 1) StringType theCriteria,
ServletRequestDetails theRequestDetails) { ServletRequestDetails theRequestDetails) {
String criteria = convertCriteriaToString(theCriteria); String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiSubmitSvc.submitAllTargetTypesToEmpi(criteria); long submittedCount = myMdmSubmitSvc.submitAllTargetTypesToEmpi(criteria);
return buildEmpiOutParametersWithCount(submittedCount); return buildMdmOutParametersWithCount(submittedCount);
} }
private String convertCriteriaToString(StringType theCriteria) { private String convertCriteriaToString(StringType theCriteria) {
return theCriteria == null ? null : theCriteria.getValueAsString(); return theCriteria == null ? null : theCriteria.getValueAsString();
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, type = Patient.class, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
}) })
public Parameters empiBatchPatientInstance( public Parameters empiBatchPatientInstance(
@IdParam IIdType theIdParam, @IdParam IIdType theIdParam,
RequestDetails theRequest) { RequestDetails theRequest) {
long submittedCount = myEmpiSubmitSvc.submitTargetToEmpi(theIdParam); long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
return buildEmpiOutParametersWithCount(submittedCount); return buildMdmOutParametersWithCount(submittedCount);
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, type = Patient.class, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
}) })
public Parameters empiBatchPatientType( public Parameters empiBatchPatientType(
@OperationParam(name = ProviderConstants.EMPI_BATCH_RUN_CRITERIA) StringType theCriteria, @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
RequestDetails theRequest) { RequestDetails theRequest) {
String criteria = convertCriteriaToString(theCriteria); String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiSubmitSvc.submitPatientTypeToEmpi(criteria); long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria);
return buildEmpiOutParametersWithCount(submittedCount); return buildMdmOutParametersWithCount(submittedCount);
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
}) })
public Parameters empiBatchPractitionerInstance( public Parameters empiBatchPractitionerInstance(
@IdParam IIdType theIdParam, @IdParam IIdType theIdParam,
RequestDetails theRequest) { RequestDetails theRequest) {
long submittedCount = myEmpiSubmitSvc.submitTargetToEmpi(theIdParam); long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
return buildEmpiOutParametersWithCount(submittedCount); return buildMdmOutParametersWithCount(submittedCount);
} }
@Operation(name = ProviderConstants.OPERATION_EMPI_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = { @Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class) @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
}) })
public Parameters empiBatchPractitionerType( public Parameters empiBatchPractitionerType(
@OperationParam(name = ProviderConstants.EMPI_BATCH_RUN_CRITERIA) StringType theCriteria, @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
RequestDetails theRequest) { RequestDetails theRequest) {
String criteria = convertCriteriaToString(theCriteria); String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiSubmitSvc.submitPractitionerTypeToEmpi(criteria); long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria);
return buildEmpiOutParametersWithCount(submittedCount); return buildMdmOutParametersWithCount(submittedCount);
} }
/** /**
* Helper function to build the out-parameters for all batch EMPI operations. * Helper function to build the out-parameters for all batch EMPI operations.
*/ */
private Parameters buildEmpiOutParametersWithCount(long theCount) { private Parameters buildMdmOutParametersWithCount(long theCount) {
Parameters parameters = new Parameters(); Parameters parameters = new Parameters();
parameters.addParameter() parameters.addParameter()
.setName(ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT) .setName(ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT)
.setValue(new DecimalType(theCount)); .setValue(new DecimalType(theCount));
return parameters; return parameters;
} }
@Nonnull
protected MatchGrade getMatchGrade(MatchedTarget theTheMatchedTarget) {
MatchGrade matchGrade = MatchGrade.PROBABLE;
if (theTheMatchedTarget.isMatch()) {
matchGrade = MatchGrade.CERTAIN;
} else if (theTheMatchedTarget.isPossibleMatch()) {
matchGrade = MatchGrade.POSSIBLE;
}
return matchGrade;
}
} }

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.empi.rules.config;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.empi.api.EmpiConstants; import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.IEmpiRuleValidator; import ca.uhn.fhir.empi.api.IEmpiRuleValidator;
import ca.uhn.fhir.empi.rules.json.EmpiFieldMatchJson; import ca.uhn.fhir.empi.rules.json.EmpiFieldMatchJson;
@ -41,6 +42,7 @@ import org.springframework.stereotype.Service;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
@Service @Service
@ -63,11 +65,24 @@ public class EmpiRuleValidator implements IEmpiRuleValidator {
} }
public void validate(EmpiRulesJson theEmpiRulesJson) { public void validate(EmpiRulesJson theEmpiRulesJson) {
validateMdmTypes(theEmpiRulesJson);
validateSearchParams(theEmpiRulesJson); validateSearchParams(theEmpiRulesJson);
validateMatchFields(theEmpiRulesJson); validateMatchFields(theEmpiRulesJson);
validateSystemIsUri(theEmpiRulesJson); validateSystemIsUri(theEmpiRulesJson);
} }
public void validateMdmTypes(EmpiRulesJson theEmpiRulesJson) {
for (String resourceType: theEmpiRulesJson.getMdmTypes()) {
validateTypeHasIdentifier(resourceType);
}
}
public void validateTypeHasIdentifier(String theResourceType) {
if (mySearchParamRetriever.getActiveSearchParam(theResourceType, "identifier") == null) {
throw new ConfigurationException("Resource Type " + theResourceType + " is not supported, as it does not have an 'identifier' field, which is necessary for MDM workflow.");
}
}
private void validateSearchParams(EmpiRulesJson theEmpiRulesJson) { private void validateSearchParams(EmpiRulesJson theEmpiRulesJson) {
for (EmpiResourceSearchParamJson searchParams : theEmpiRulesJson.getCandidateSearchParams()) { for (EmpiResourceSearchParamJson searchParams : theEmpiRulesJson.getCandidateSearchParams()) {
searchParams.iterator().forEachRemaining( searchParams.iterator().forEachRemaining(
@ -105,7 +120,7 @@ public class EmpiRuleValidator implements IEmpiRuleValidator {
} else if (fieldMatch.getMatcher() == null) { } else if (fieldMatch.getMatcher() == null) {
throw new ConfigurationException("MatchField " + fieldMatch.getName() + " has neither a similarity nor a matcher. At least one must be present."); throw new ConfigurationException("MatchField " + fieldMatch.getName() + " has neither a similarity nor a matcher. At least one must be present.");
} }
validatePath(fieldMatch); validatePath(theEmpiRulesJson.getMdmTypes(), fieldMatch);
} }
} }
@ -116,23 +131,29 @@ public class EmpiRuleValidator implements IEmpiRuleValidator {
} }
} }
private void validatePath(EmpiFieldMatchJson theFieldMatch) { private void validatePath(List<String> theMdmTypes, EmpiFieldMatchJson theFieldMatch) {
String resourceType = theFieldMatch.getResourceType(); String resourceType = theFieldMatch.getResourceType();
if (EmpiConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE.equals(resourceType)) { if (EmpiConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE.equals(resourceType)) {
validatePatientPath(theFieldMatch); validateFieldPathForAllTypes(theMdmTypes, theFieldMatch);
validatePractitionerPath(theFieldMatch);
} else if ("Patient".equals(resourceType)) {
validatePatientPath(theFieldMatch);
} else if ("Practitioner".equals(resourceType)) {
validatePractitionerPath(theFieldMatch);
} else { } else {
throw new ConfigurationException("MatchField " + theFieldMatch.getName() + " has unknown resourceType " + resourceType); validateFieldPath(theFieldMatch);
} }
} }
private void validatePatientPath(EmpiFieldMatchJson theFieldMatch) { private void validateFieldPathForAllTypes(List<String> theMdmResourceTypes, EmpiFieldMatchJson theFieldMatch) {
for (String resourceType: theMdmResourceTypes) {
validateFieldPathForType(resourceType, theFieldMatch);
}
}
private void validateFieldPathForType(String theResourceType, EmpiFieldMatchJson theFieldMatch) {
try { try {
myTerser.getDefinition(myPatientClass, "Patient." + theFieldMatch.getResourcePath()); RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theResourceType);
Class<? extends IBaseResource> implementingClass = resourceDefinition.getImplementingClass();
myTerser.getDefinition(implementingClass, theResourceType + "." + theFieldMatch.getResourcePath());
} catch (DataFormatException | ConfigurationException e) { } catch (DataFormatException | ConfigurationException e) {
throw new ConfigurationException("MatchField " + throw new ConfigurationException("MatchField " +
theFieldMatch.getName() + theFieldMatch.getName() +
@ -140,20 +161,12 @@ public class EmpiRuleValidator implements IEmpiRuleValidator {
theFieldMatch.getResourceType() + theFieldMatch.getResourceType() +
" has invalid path '" + theFieldMatch.getResourcePath() + "'. " + " has invalid path '" + theFieldMatch.getResourcePath() + "'. " +
e.getMessage()); e.getMessage());
} }
} }
private void validatePractitionerPath(EmpiFieldMatchJson theFieldMatch) { private void validateFieldPath(EmpiFieldMatchJson theFieldMatch) {
try { validateFieldPathForType(theFieldMatch.getResourceType(), theFieldMatch);
myTerser.getDefinition(myPractitionerClass, "Practitioner." + theFieldMatch.getResourcePath());
} catch (DataFormatException e) {
throw new ConfigurationException("MatchField " +
theFieldMatch.getName() +
" resourceType " +
theFieldMatch.getResourceType() +
" has invalid path '" + theFieldMatch.getResourcePath() + "'. " +
e.getMessage());
}
} }
private void validateSystemIsUri(EmpiRulesJson theEmpiRulesJson) { private void validateSystemIsUri(EmpiRulesJson theEmpiRulesJson) {

View File

@ -49,6 +49,10 @@ public class EmpiRulesJson implements IModelJson {
@JsonProperty(value = "eidSystem") @JsonProperty(value = "eidSystem")
String myEnterpriseEIDSystem; String myEnterpriseEIDSystem;
@JsonProperty(value = "mdmTypes")
List<String> myMdmTypes;
transient VectorMatchResultMap myVectorMatchResultMap; transient VectorMatchResultMap myVectorMatchResultMap;
public void addMatchField(EmpiFieldMatchJson theMatchRuleName) { public void addMatchField(EmpiFieldMatchJson theMatchRuleName) {
@ -174,4 +178,13 @@ public class EmpiRulesJson implements IModelJson {
return theEmpiRulesJson; return theEmpiRulesJson;
} }
} }
public List<String> getMdmTypes() {
return myMdmTypes;
}
public void setMdmTypes(List<String> theMdmTypes) {
myMdmTypes = theMdmTypes;
}
} }

View File

@ -23,12 +23,14 @@ package ca.uhn.fhir.empi.rules.svc;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.EmpiMatchEvaluation; import ca.uhn.fhir.empi.api.EmpiMatchEvaluation;
import ca.uhn.fhir.empi.rules.json.EmpiFieldMatchJson; import ca.uhn.fhir.empi.rules.json.EmpiFieldMatchJson;
import ca.uhn.fhir.empi.rules.json.EmpiRulesJson;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import static ca.uhn.fhir.empi.api.EmpiConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE; import static ca.uhn.fhir.empi.api.EmpiConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE;
@ -40,12 +42,14 @@ public class EmpiResourceFieldMatcher {
private final EmpiFieldMatchJson myEmpiFieldMatchJson; private final EmpiFieldMatchJson myEmpiFieldMatchJson;
private final String myResourceType; private final String myResourceType;
private final String myResourcePath; private final String myResourcePath;
private final EmpiRulesJson myEmpiRulesJson;
public EmpiResourceFieldMatcher(FhirContext theFhirContext, EmpiFieldMatchJson theEmpiFieldMatchJson) { public EmpiResourceFieldMatcher(FhirContext theFhirContext, EmpiFieldMatchJson theEmpiFieldMatchJson, EmpiRulesJson theEmpiRulesJson) {
myFhirContext = theFhirContext; myFhirContext = theFhirContext;
myEmpiFieldMatchJson = theEmpiFieldMatchJson; myEmpiFieldMatchJson = theEmpiFieldMatchJson;
myResourceType = theEmpiFieldMatchJson.getResourceType(); myResourceType = theEmpiFieldMatchJson.getResourceType();
myResourcePath = theEmpiFieldMatchJson.getResourcePath(); myResourcePath = theEmpiFieldMatchJson.getResourcePath();
myEmpiRulesJson = theEmpiRulesJson;
} }
/** /**
@ -88,11 +92,20 @@ public class EmpiResourceFieldMatcher {
private void validate(IBaseResource theResource) { private void validate(IBaseResource theResource) {
String resourceType = myFhirContext.getResourceType(theResource); String resourceType = myFhirContext.getResourceType(theResource);
Validate.notNull(resourceType, "Resource type may not be null"); Validate.notNull(resourceType, "Resource type may not be null");
if (ALL_RESOURCE_SEARCH_PARAM_TYPE.equals(myResourceType)) { if (ALL_RESOURCE_SEARCH_PARAM_TYPE.equals(myResourceType)) {
Validate.isTrue("Patient".equalsIgnoreCase(resourceType) || "Practitioner".equalsIgnoreCase(resourceType), boolean isMdmType = myEmpiRulesJson.getMdmTypes().stream().anyMatch(mdmType -> mdmType.equalsIgnoreCase(resourceType));
"Expecting resource type Patient/Practitioner got resource type %s", resourceType); Validate.isTrue(isMdmType, "Expecting resource type %s, got resource type %s", myEmpiRulesJson.getMdmTypes().stream().collect(Collectors.joining(",")), resourceType);
} else { } else {
Validate.isTrue(myResourceType.equals(resourceType), "Expecting resource type %s got resource type %s", myResourceType, resourceType); Validate.isTrue(myResourceType.equals(resourceType), "Expecting resource type %s got resource type %s", myResourceType, resourceType);
} }
} }
public String getResourceType() {
return myResourceType;
}
public String getResourcePath() {
return myResourcePath;
}
} }

View File

@ -37,6 +37,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
/** /**
* The EmpiResourceComparator is in charge of performing actual comparisons between left and right records. * The EmpiResourceComparator is in charge of performing actual comparisons between left and right records.
@ -66,7 +67,7 @@ public class EmpiResourceMatcherSvc {
throw new ConfigurationException("Failed to load EMPI Rules. If EMPI is enabled, then EMPI rules must be available in context."); throw new ConfigurationException("Failed to load EMPI Rules. If EMPI is enabled, then EMPI rules must be available in context.");
} }
for (EmpiFieldMatchJson matchFieldJson : myEmpiRulesJson.getMatchFields()) { for (EmpiFieldMatchJson matchFieldJson : myEmpiRulesJson.getMatchFields()) {
myFieldMatchers.add(new EmpiResourceFieldMatcher(myFhirContext, matchFieldJson)); myFieldMatchers.add(new EmpiResourceFieldMatcher( myFhirContext, matchFieldJson, myEmpiRulesJson));
} }
} }
@ -116,9 +117,15 @@ public class EmpiResourceMatcherSvc {
private EmpiMatchOutcome getMatchOutcome(IBaseResource theLeftResource, IBaseResource theRightResource) { private EmpiMatchOutcome getMatchOutcome(IBaseResource theLeftResource, IBaseResource theRightResource) {
long vector = 0; long vector = 0;
double score = 0.0; double score = 0.0;
for (int i = 0; i < myFieldMatchers.size(); ++i) { //TODO GGG MDM: This grabs ALL comparators, not just the ones we care about (e.g. the ones for Medication)
String resourceType = myFhirContext.getResourceType(theLeftResource);
List<EmpiResourceFieldMatcher> resourceRelevantFieldMatchers = myFieldMatchers.stream()
.filter(comp -> comp.getResourceType().equalsIgnoreCase("*") || comp.getResourceType().equalsIgnoreCase(resourceType))
.collect(Collectors.toList());
for (int i = 0; i < resourceRelevantFieldMatchers.size(); ++i) {
//any that are not for the resourceType in question. //any that are not for the resourceType in question.
EmpiResourceFieldMatcher fieldComparator = myFieldMatchers.get(i); EmpiResourceFieldMatcher fieldComparator = resourceRelevantFieldMatchers.get(i);
EmpiMatchEvaluation matchEvaluation = fieldComparator.match(theLeftResource, theRightResource); EmpiMatchEvaluation matchEvaluation = fieldComparator.match(theLeftResource, theRightResource);
if (matchEvaluation.match) { if (matchEvaluation.match) {
vector |= (1 << i); vector |= (1 << i);

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.empi.rules.svc; package ca.uhn.fhir.empi.rules.svc;
import ca.uhn.fhir.empi.rules.json.EmpiFieldMatchJson; import ca.uhn.fhir.empi.rules.json.EmpiFieldMatchJson;
import ca.uhn.fhir.empi.rules.json.EmpiRulesJson;
import ca.uhn.fhir.empi.rules.json.EmpiSimilarityJson; import ca.uhn.fhir.empi.rules.json.EmpiSimilarityJson;
import ca.uhn.fhir.empi.rules.similarity.EmpiSimilarityEnum; import ca.uhn.fhir.empi.rules.similarity.EmpiSimilarityEnum;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
@ -9,6 +10,8 @@ import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringStartsWith.startsWith; import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -20,14 +23,17 @@ public class EmpiResourceFieldMatcherR4Test extends BaseEmpiRulesR4Test {
protected EmpiResourceFieldMatcher myComparator; protected EmpiResourceFieldMatcher myComparator;
private Patient myJohn; private Patient myJohn;
private Patient myJohny; private Patient myJohny;
private EmpiRulesJson myEmpiRulesJson;
@Override @Override
@BeforeEach @BeforeEach
public void before() { public void before() {
super.before(); super.before();
ArrayList<String> myLegalMdmTypes = new ArrayList<>();
myComparator = new EmpiResourceFieldMatcher(ourFhirContext, myGivenNameMatchField); myLegalMdmTypes.add("Patient");
myEmpiRulesJson.setMdmTypes(myLegalMdmTypes);
myComparator = new EmpiResourceFieldMatcher(ourFhirContext, myGivenNameMatchField, myEmpiRulesJson);
myJohn = buildJohn(); myJohn = buildJohn();
myJohny = buildJohny(); myJohny = buildJohny();
} }
@ -67,7 +73,7 @@ public class EmpiResourceFieldMatcherR4Test extends BaseEmpiRulesR4Test {
.setResourceType("Patient") .setResourceType("Patient")
.setResourcePath("foo") .setResourcePath("foo")
.setSimilarity(new EmpiSimilarityJson().setAlgorithm(EmpiSimilarityEnum.COSINE).setMatchThreshold(NAME_THRESHOLD)); .setSimilarity(new EmpiSimilarityJson().setAlgorithm(EmpiSimilarityEnum.COSINE).setMatchThreshold(NAME_THRESHOLD));
EmpiResourceFieldMatcher comparator = new EmpiResourceFieldMatcher(ourFhirContext, matchField); EmpiResourceFieldMatcher comparator = new EmpiResourceFieldMatcher(ourFhirContext, matchField, myEmpiRulesJson);
comparator.match(myJohn, myJohny); comparator.match(myJohn, myJohny);
fail(); fail();
} catch (DataFormatException e) { } catch (DataFormatException e) {

View File

@ -64,31 +64,32 @@ public class ProviderConstants {
public static final String EMPI_MATCH = "$match"; public static final String EMPI_MATCH = "$match";
//TODO GGG MDM: implement a server-level MDM match to complement the FHIR-spec $match for /Patient //TODO GGG MDM: implement a server-level MDM match to complement the FHIR-spec $match for /Patient
public static final String MDM_MATCH = "$mdm-match"; public static final String MDM_MATCH = "$mdm-match";
public static final String EMPI_MATCH_RESOURCE = "resource"; public static final String MDM_MATCH_RESOURCE = "resource";
public static final String MDM_RESOURCE_TYPE = "resourceType";
//TODO GGG MDM: rename all these vars //TODO GGG MDM: rename all these vars
public static final String MDM_MERGE_GOLDEN_RESOURCES = "$empi-merge-golden-resources"; public static final String MDM_MERGE_GOLDEN_RESOURCES = "$mdm-merge-golden-resources";
public static final String MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID = "fromGoldenResourceId"; public static final String MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID = "fromGoldenResourceId";
public static final String MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID = "toGoldenResourceId"; public static final String MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID = "toGoldenResourceId";
public static final String MDM_UPDATE_LINK = "$empi-update-link"; public static final String MDM_UPDATE_LINK = "$mdm-update-link";
public static final String MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID = "goldenResourceId"; public static final String MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID = "goldenResourceId";
public static final String MDM_UPDATE_LINK_RESOURCE_ID = "resourceId"; public static final String MDM_UPDATE_LINK_RESOURCE_ID = "resourceId";
public static final String MDM_UPDATE_LINK_MATCH_RESULT = "matchResult"; public static final String MDM_UPDATE_LINK_MATCH_RESULT = "matchResult";
public static final String MDM_QUERY_LINKS = "$empi-query-links"; public static final String MDM_QUERY_LINKS = "$mdm-query-links";
public static final String MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID = "goldenResourceId"; public static final String MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID = "goldenResourceId";
public static final String MDM_QUERY_LINKS_RESOURCE_ID = "resourceId"; public static final String MDM_QUERY_LINKS_RESOURCE_ID = "resourceId";
public static final String EMPI_QUERY_LINKS_MATCH_RESULT = "matchResult"; public static final String EMPI_QUERY_LINKS_MATCH_RESULT = "matchResult";
public static final String EMPI_QUERY_LINKS_LINK_SOURCE = "linkSource"; public static final String EMPI_QUERY_LINKS_LINK_SOURCE = "linkSource";
public static final String EMPI_DUPLICATE_PERSONS = "$empi-duplicate-golden-resources"; public static final String MDM_DUPLICATE_GOLDEN_RESOURCES = "$empi-duplicate-golden-resources";
public static final String EMPI_NOT_DUPLICATE = "$empi-not-duplicate"; public static final String MDM_NOT_DUPLICATE = "$empi-not-duplicate";
public static final String MDM_CLEAR = "$empi-clear"; public static final String MDM_CLEAR = "$mdm-clear";
public static final String MDM_CLEAR_TARGET_TYPE = "targetType"; public static final String MDM_CLEAR_TARGET_TYPE = "targetType";
public static final String OPERATION_EMPI_SUBMIT = "$empi-submit"; public static final String OPERATION_MDM_SUBMIT = "$mdm-submit";
public static final String EMPI_BATCH_RUN_CRITERIA= "criteria" ; public static final String MDM_BATCH_RUN_CRITERIA = "criteria" ;
public static final String OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT = "submitted" ; public static final String OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT = "submitted" ;
public static final String OPERATION_MDM_CLEAR_OUT_PARAM_DELETED_COUNT = "deleted"; public static final String OPERATION_MDM_CLEAR_OUT_PARAM_DELETED_COUNT = "deleted";
} }