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) {
myPersonPid = theSourceResourcePid;
mySourceResourcePid = theSourceResourcePid;
return this;
}

View File

@ -54,7 +54,7 @@ public class EmpiControllerSvcImpl implements IEmpiControllerSvc {
IEmpiLinkUpdaterSvc myIEmpiLinkUpdaterSvc;
@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 toPerson = myEmpiControllerHelper.getLatestPersonFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPersonId);
myEmpiControllerHelper.validateMergeResources(fromPerson, toPerson);
@ -75,7 +75,7 @@ public class EmpiControllerSvcImpl implements IEmpiControllerSvc {
}
@Override
public Stream<EmpiLinkJson> getDuplicatePersons(EmpiTransactionContext theEmpiContext) {
public Stream<EmpiLinkJson> getDuplicateGoldenResources(EmpiTransactionContext theEmpiContext) {
return myEmpiLinkQuerySvc.getDuplicatePersons(theEmpiContext);
}
@ -91,7 +91,7 @@ public class EmpiControllerSvcImpl implements IEmpiControllerSvc {
}
@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 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();
String targetId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getTargetPid()).toVersionless().getValue();
retval.setTargetId(targetId);
String personId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourceResourcePid()).toVersionless().getValue();
retval.setPersonId(personId);
String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourceResourcePid()).toVersionless().getValue();
retval.setGoldenResourceId(goldenResourceId);
retval.setCreated(theLink.getCreated());
retval.setEidMatch(theLink.getEidMatch());
retval.setLinkSource(theLink.getLinkSource());
retval.setMatchResult(theLink.getMatchResult());
retval.setNewPerson(theLink.getHadToCreateNewResource());
retval.setLinkCreatedNewResource(theLink.getHadToCreateNewResource());
retval.setScore(theLink.getScore());
retval.setUpdated(theLink.getUpdated());
retval.setVector(theLink.getVector());

View File

@ -135,19 +135,18 @@ public class EmpiLinkUpdaterSvcImpl implements IEmpiLinkUpdaterSvc {
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);
if (!"Person".equals(personType)) {
throw new InvalidRequestException("First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be a Person. Was " + personType);
}
if (!"Person".equals(targetType)) {
throw new InvalidRequestException("Second argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be a Person . Was " + targetType);
if (!goldenResourceType.equalsIgnoreCase(targetType)) {
throw new InvalidRequestException("First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be the same resource type as the second argument. Was " + goldenResourceType + "/" + targetType);
}
if (!EmpiUtil.isEmpiManaged(thePerson) || !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");
if (!EmpiUtil.isEmpiManaged(theGoldenResource) || !EmpiUtil.isEmpiManaged(theTarget)) {
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
public long submitAllTargetTypesToEmpi(@Nullable String theCriteria) {
long submittedCount = 0;
submittedCount += submitPatientTypeToEmpi(theCriteria);
submittedCount += submitPractitionerTypeToEmpi(theCriteria);
submittedCount += submitPatientTypeToMdm(theCriteria);
submittedCount += submitPractitionerTypeToMdm(theCriteria);
return submittedCount;
}
@ -97,7 +97,7 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
total += loadPidsAndSubmitToEmpiChannel(theSearchBuilder, pidBatch);
} while (query.hasNext());
} 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);
return total;
@ -123,19 +123,19 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
@Override
@Transactional
public long submitPractitionerTypeToEmpi(@Nullable String theCriteria) {
public long submitPractitionerTypeToMdm(@Nullable String theCriteria) {
return submitTargetTypeToEmpi("Practitioner", theCriteria);
}
@Override
@Transactional
public long submitPatientTypeToEmpi(@Nullable String theCriteria) {
public long submitPatientTypeToMdm(@Nullable String theCriteria) {
return submitTargetTypeToEmpi("Patient", theCriteria);
}
@Override
@Transactional
public long submitTargetToEmpi(IIdType theId) {
public long submitTargetToMdm(IIdType theId) {
resolveTargetTypeOrThrowException(theId.getResourceType());
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
IBaseResource read = resourceDao.read(theId);
@ -145,7 +145,7 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
private void resolveTargetTypeOrThrowException(String 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.r4.model.ContactPoint;
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.Person;
import org.hl7.fhir.r4.model.Practitioner;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -85,6 +86,10 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
@Autowired
protected IFhirResourceDao<Patient> myPatientDao;
@Autowired
protected IFhirResourceDao<Organization> myOrganizationDao;
@Autowired
protected IFhirResourceDao<Medication> myMedicationDao;
@Autowired
protected IFhirResourceDao<Practitioner> myPractitionerDao;
@Autowired
protected EmpiResourceMatcherSvc myEmpiResourceMatcherSvc;
@ -170,6 +175,16 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
Patient patient = (Patient) outcome.getResource();
patient.setId(outcome.getId());
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

View File

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

View File

@ -25,8 +25,8 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
protected Practitioner myPractitioner;
protected StringType myPractitionerId;
protected IAnyResource myPractitionerPerson;
protected StringType myPractitionerPersonId;
protected IAnyResource myGoldenPractitier;
protected StringType myGoldenPractitionerId;
@Autowired
IInterceptorService myInterceptorService;
@ -38,8 +38,8 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
super.before();
myPractitioner = createPractitionerAndUpdateLinks(buildPractitionerWithNameAndId("some_pract", "some_pract_id"));
myPractitionerId = new StringType(myPractitioner.getIdElement().getValue());
myPractitionerPerson = getSourceResourceFromTargetResource(myPractitioner);
myPractitionerPersonId = new StringType(myPractitionerPerson.getIdElement().getValue());
myGoldenPractitier = getSourceResourceFromTargetResource(myPractitioner);
myGoldenPractitionerId = new StringType(myGoldenPractitier.getIdElement().getValue());
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 com.google.common.collect.Ordering;
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.Medication;
import org.hl7.fhir.r4.model.Organization;
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.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -15,7 +19,9 @@ import org.slf4j.LoggerFactory;
import java.util.List;
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 {
@ -52,6 +58,63 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
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
public void testMatchOrder() throws Exception {
Patient jane0 = buildJanePatient();

View File

@ -82,7 +82,7 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test
public void testQueryPossibleDuplicates() {
Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails);
Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1));
@ -93,7 +93,7 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test
public void testNotDuplicate() {
{
Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails);
Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1));
}
@ -103,7 +103,7 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
assertEquals("success", result.getParameterFirstRep().getName());
assertTrue(((BooleanType) (result.getParameterFirstRep().getValue())).booleanValue());
}
Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails);
Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter();
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) {
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(1).getName(), is("targetId"));
assertThat(thePart.get(1).getName(), is("targetResourceId"));
assertThat(thePart.get(1).getValue().toString(), is(removeVersion(theTargetId)));
if (theExpectedSize > 2) {
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).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(6).getName(), is("score"));

View File

@ -1,5 +1,6 @@
{
"version": "1",
"mdmTypes": ["Patient", "Practitioner", "Medication"],
"candidateSearchParams": [
{
"resourceType": "Patient",
@ -13,6 +14,12 @@
"identifier"
]
},
{
"resourceType": "Medication",
"searchParams": [
"manufacturer"
]
},
{
"resourceType": "Patient",
"searchParams": [
@ -22,15 +29,30 @@
],
"candidateFilterSearchParams": [
{
"resourceType": "*",
"resourceType": "Practitioner",
"searchParam": "active",
"fixedValue": "true"
},
{
"resourceType": "Patient",
"searchParam": "active",
"fixedValue": "true"
}
],
"matchFields": [
{
"name": "cosine-code",
"resourceType": "Medication",
"resourcePath": "code",
"similarity": {
"algorithm": "COSINE",
"matchThreshold": 0.8,
"exact": true
}
},
{
"name": "cosine-given-name",
"resourceType": "*",
"resourceType": "Patient",
"resourcePath": "name.given",
"similarity": {
"algorithm": "COSINE",
@ -40,7 +62,7 @@
},
{
"name": "jaro-last-name",
"resourceType": "*",
"resourceType": "Patient",
"resourcePath": "name.family",
"similarity": {
"algorithm": "JARO_WINKLER",
@ -50,7 +72,36 @@
},
{
"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",
"matcher": {
"algorithm": "IDENTIFIER",
@ -60,7 +111,10 @@
],
"matchResultMap": {
"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"
}

View File

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

View File

@ -28,8 +28,8 @@ import java.util.stream.Stream;
public interface IEmpiControllerSvc {
Stream<EmpiLinkJson> queryLinks(@Nullable String thePersonId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, EmpiTransactionContext theEmpiContext);
Stream<EmpiLinkJson> getDuplicatePersons(EmpiTransactionContext theEmpiContext);
void notDuplicatePerson(String thePersonId, String theTargetPersonId, EmpiTransactionContext theEmpiContext);
IAnyResource mergePersons(String theFromPersonId, String theToPersonId, EmpiTransactionContext theEmpiTransactionContext);
Stream<EmpiLinkJson> getDuplicateGoldenResources(EmpiTransactionContext theEmpiContext);
void notDuplicateGoldenResource(String thePersonId, String theTargetPersonId, EmpiTransactionContext theEmpiContext);
IAnyResource mergeGoldenResources(String theFromPersonId, String theToPersonId, EmpiTransactionContext theEmpiTransactionContext);
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.
* @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.
@ -61,13 +61,13 @@ public interface IEmpiSubmitSvc {
* @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.
*/
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.
* @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.
*/
long submitTargetToEmpi(IIdType theId);
long submitTargetToMdm(IIdType theId);
}

View File

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

View File

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

View File

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

View File

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

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.StringType;
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 javax.annotation.Nonnull;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
@ -77,33 +79,23 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
}
@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) {
throw new InvalidRequestException("resource may not be null");
}
List<MatchedTarget> matches = myEmpiMatchFinderSvc.getMatchedTargets("Patient", thePatient);
matches.sort(Comparator.comparing((MatchedTarget m) -> m.getMatchResult().getNormalizedScore()).reversed());
return getMatchesAndPossibleMatchesForResource(thePatient, "Patient");
}
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);
@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");
}
return getMatchesAndPossibleMatchesForResource(theResource, theResourceType.getValueNotNull());
return retVal;
}
private Bundle.BundleEntrySearchComponent toBundleEntrySearchComponent(MatchedTarget theMatchedTarget) {
@ -111,12 +103,7 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
searchComponent.setMode(Bundle.SearchEntryMode.MATCH);
searchComponent.setScore(theMatchedTarget.getMatchResult().getNormalizedScore());
MatchGrade matchGrade = MatchGrade.PROBABLE;
if (theMatchedTarget.isMatch()) {
matchGrade = MatchGrade.CERTAIN;
} else if (theMatchedTarget.isPossibleMatch()) {
matchGrade = MatchGrade.POSSIBLE;
}
MatchGrade matchGrade = getMatchGrade(theMatchedTarget);
searchComponent.addExtension(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE, new CodeType(matchGrade.toCode()));
return searchComponent;
@ -128,7 +115,7 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
RequestDetails theRequestDetails) {
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)
@ -139,7 +126,7 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
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)
@ -149,35 +136,35 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_MATCH_RESULT, min = 0, max = 1) StringType theLinkSource,
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);
}
@Operation(name = ProviderConstants.EMPI_DUPLICATE_PERSONS)
@Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES)
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);
}
@Operation(name = ProviderConstants.EMPI_NOT_DUPLICATE)
@Operation(name = ProviderConstants.MDM_NOT_DUPLICATE)
// TODO KHS can this return void?
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,
ServletRequestDetails theRequestDetails) {
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);
ParametersUtil.addParameterToParametersBoolean(myFhirContext, retval, "success", true);
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)
})
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) {
String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiBatchSvc.submitAllTargetTypesToEmpi(criteria);
@ -205,45 +192,45 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
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)
})
public Parameters empiBatchPatientInstance(
@IdParam IIdType theIdParam,
RequestDetails theRequest) {
long submittedCount = myEmpiBatchSvc.submitTargetToEmpi(theIdParam);
long submittedCount = myEmpiBatchSvc.submitTargetToMdm(theIdParam);
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)
})
public Parameters empiBatchPatientType(
@OperationParam(name = ProviderConstants.EMPI_BATCH_RUN_CRITERIA) StringType theCriteria,
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
RequestDetails theRequest) {
String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiBatchSvc.submitPatientTypeToEmpi(criteria);
long submittedCount = myEmpiBatchSvc.submitPatientTypeToMdm(criteria);
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)
})
public Parameters empiBatchPractitionerInstance(
@IdParam IIdType theIdParam,
RequestDetails theRequest) {
long submittedCount = myEmpiBatchSvc.submitTargetToEmpi(theIdParam);
long submittedCount = myEmpiBatchSvc.submitTargetToMdm(theIdParam);
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)
})
public Parameters empiBatchPractitionerType(
@OperationParam(name = ProviderConstants.EMPI_BATCH_RUN_CRITERIA) StringType theCriteria,
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
RequestDetails theRequest) {
String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiBatchSvc.submitPractitionerTypeToEmpi(criteria);
long submittedCount = myEmpiBatchSvc.submitPractitionerTypeToMdm(criteria);
return buildEmpiOutParametersWithCount(submittedCount);
}
@ -257,4 +244,39 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
.setValue(new DecimalType(theCount));
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.util.ParametersUtil;
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.IIdType;
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.codesystems.MatchGrade;
import javax.annotation.Nonnull;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
@ -61,7 +63,7 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
private final IEmpiControllerSvc myEmpiControllerSvc;
private final IEmpiMatchFinderSvc myEmpiMatchFinderSvc;
private final IEmpiExpungeSvc myMdmExpungeSvc;
private final IEmpiSubmitSvc myEmpiSubmitSvc;
private final IEmpiSubmitSvc myMdmSubmitSvc;
/**
* Constructor
@ -69,21 +71,45 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
* Note that this is not a spring bean. Any necessary injections should
* 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);
myEmpiControllerSvc = theEmpiControllerSvc;
myEmpiMatchFinderSvc = theEmpiMatchFinderSvc;
myMdmExpungeSvc = theMdmExpungeSvc;
myEmpiSubmitSvc = theEmpiSubmitSvc;
myMdmSubmitSvc = theMdmSubmitSvc;
}
@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) {
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());
Bundle retVal = new Bundle();
@ -103,45 +129,39 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
retVal.addEntry(entry);
}
return retVal;
}
private Bundle.BundleEntrySearchComponent toBundleEntrySearchComponent(MatchedTarget theMatchedTarget) {
Bundle.BundleEntrySearchComponent searchComponent = new Bundle.BundleEntrySearchComponent();
searchComponent.setMode(Bundle.SearchEntryMode.MATCH);
searchComponent.setScore(theMatchedTarget.getMatchResult().getNormalizedScore());
MatchGrade matchGrade = MatchGrade.PROBABLE;
if (theMatchedTarget.isMatch()) {
matchGrade = MatchGrade.CERTAIN;
} else if (theMatchedTarget.isPossibleMatch()) {
matchGrade = MatchGrade.POSSIBLE;
}
MatchGrade matchGrade = getMatchGrade(theMatchedTarget);
searchComponent.addExtension(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE, new CodeType(matchGrade.toCode()));
return searchComponent;
}
//TODO GGG ask ken, what is the best way to genericize this? Return
@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,
@OperationParam(name=ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType theToGoldenResourceId,
RequestDetails theRequestDetails) {
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)
public IBaseResource updateLink(@OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, min = 1, max = 1) StringType thePersonId,
@OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, min = 1, max = 1) StringType theTargetId,
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 theResourceId,
@OperationParam(name=ProviderConstants.MDM_UPDATE_LINK_MATCH_RESULT, min = 1, max = 1) StringType theMatchResult,
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 = {
@ -163,100 +183,112 @@ public class EmpiProviderR4 extends BaseEmpiProvider {
@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,
@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 0, max = 1) StringType theTargetId,
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 theResourceId,
@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,
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);
}
@Operation(name = ProviderConstants.EMPI_DUPLICATE_PERSONS, idempotent = true)
public Parameters getDuplicatePersons(ServletRequestDetails theRequestDetails) {
Stream<EmpiLinkJson> possibleDuplicates = myEmpiControllerSvc.getDuplicatePersons(createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.DUPLICATE_PERSONS));
@Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES, idempotent = true)
public Parameters getDuplicateGoldenResources(ServletRequestDetails theRequestDetails) {
Stream<EmpiLinkJson> possibleDuplicates = myEmpiControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES));
return (Parameters) parametersFromEmpiLinks(possibleDuplicates, false);
}
@Operation(name = ProviderConstants.EMPI_NOT_DUPLICATE)
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,
@Operation(name = ProviderConstants.MDM_NOT_DUPLICATE)
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 theResourceId,
ServletRequestDetails theRequestDetails) {
validateNotDuplicateParameters(thePersonId, theTargetId);
myEmpiControllerSvc.notDuplicatePerson(thePersonId.getValue(), theTargetId.getValue(), createEmpiContext(theRequestDetails, EmpiTransactionContext.OperationType.NOT_DUPLICATE));
validateNotDuplicateParameters(theGoldenResourceId, theResourceId);
myEmpiControllerSvc.notDuplicateGoldenResource(theGoldenResourceId.getValue(), theResourceId.getValue(), createMdmContext(theRequestDetails, EmpiTransactionContext.OperationType.NOT_DUPLICATE));
Parameters retval = (Parameters) ParametersUtil.newInstance(myFhirContext);
ParametersUtil.addParameterToParametersBoolean(myFhirContext, retval, "success", true);
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)
})
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) {
String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiSubmitSvc.submitAllTargetTypesToEmpi(criteria);
return buildEmpiOutParametersWithCount(submittedCount);
long submittedCount = myMdmSubmitSvc.submitAllTargetTypesToEmpi(criteria);
return buildMdmOutParametersWithCount(submittedCount);
}
private String convertCriteriaToString(StringType theCriteria) {
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)
})
public Parameters empiBatchPatientInstance(
@IdParam IIdType theIdParam,
RequestDetails theRequest) {
long submittedCount = myEmpiSubmitSvc.submitTargetToEmpi(theIdParam);
return buildEmpiOutParametersWithCount(submittedCount);
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
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)
})
public Parameters empiBatchPatientType(
@OperationParam(name = ProviderConstants.EMPI_BATCH_RUN_CRITERIA) StringType theCriteria,
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
RequestDetails theRequest) {
String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiSubmitSvc.submitPatientTypeToEmpi(criteria);
return buildEmpiOutParametersWithCount(submittedCount);
long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria);
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)
})
public Parameters empiBatchPractitionerInstance(
@IdParam IIdType theIdParam,
RequestDetails theRequest) {
long submittedCount = myEmpiSubmitSvc.submitTargetToEmpi(theIdParam);
return buildEmpiOutParametersWithCount(submittedCount);
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
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)
})
public Parameters empiBatchPractitionerType(
@OperationParam(name = ProviderConstants.EMPI_BATCH_RUN_CRITERIA) StringType theCriteria,
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
RequestDetails theRequest) {
String criteria = convertCriteriaToString(theCriteria);
long submittedCount = myEmpiSubmitSvc.submitPractitionerTypeToEmpi(criteria);
return buildEmpiOutParametersWithCount(submittedCount);
long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria);
return buildMdmOutParametersWithCount(submittedCount);
}
/**
* 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.addParameter()
.setName(ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT)
.setValue(new DecimalType(theCount));
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.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.IEmpiRuleValidator;
import ca.uhn.fhir.empi.rules.json.EmpiFieldMatchJson;
@ -41,6 +42,7 @@ import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
@ -63,11 +65,24 @@ public class EmpiRuleValidator implements IEmpiRuleValidator {
}
public void validate(EmpiRulesJson theEmpiRulesJson) {
validateMdmTypes(theEmpiRulesJson);
validateSearchParams(theEmpiRulesJson);
validateMatchFields(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) {
for (EmpiResourceSearchParamJson searchParams : theEmpiRulesJson.getCandidateSearchParams()) {
searchParams.iterator().forEachRemaining(
@ -105,7 +120,7 @@ public class EmpiRuleValidator implements IEmpiRuleValidator {
} else if (fieldMatch.getMatcher() == null) {
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();
if (EmpiConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE.equals(resourceType)) {
validatePatientPath(theFieldMatch);
validatePractitionerPath(theFieldMatch);
} else if ("Patient".equals(resourceType)) {
validatePatientPath(theFieldMatch);
} else if ("Practitioner".equals(resourceType)) {
validatePractitionerPath(theFieldMatch);
validateFieldPathForAllTypes(theMdmTypes, theFieldMatch);
} 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 {
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) {
throw new ConfigurationException("MatchField " +
theFieldMatch.getName() +
@ -140,20 +161,12 @@ public class EmpiRuleValidator implements IEmpiRuleValidator {
theFieldMatch.getResourceType() +
" has invalid path '" + theFieldMatch.getResourcePath() + "'. " +
e.getMessage());
}
}
private void validatePractitionerPath(EmpiFieldMatchJson theFieldMatch) {
try {
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 validateFieldPath(EmpiFieldMatchJson theFieldMatch) {
validateFieldPathForType(theFieldMatch.getResourceType(), theFieldMatch);
}
private void validateSystemIsUri(EmpiRulesJson theEmpiRulesJson) {

View File

@ -49,6 +49,10 @@ public class EmpiRulesJson implements IModelJson {
@JsonProperty(value = "eidSystem")
String myEnterpriseEIDSystem;
@JsonProperty(value = "mdmTypes")
List<String> myMdmTypes;
transient VectorMatchResultMap myVectorMatchResultMap;
public void addMatchField(EmpiFieldMatchJson theMatchRuleName) {
@ -174,4 +178,13 @@ public class EmpiRulesJson implements IModelJson {
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.empi.api.EmpiMatchEvaluation;
import ca.uhn.fhir.empi.rules.json.EmpiFieldMatchJson;
import ca.uhn.fhir.empi.rules.json.EmpiRulesJson;
import ca.uhn.fhir.util.FhirTerser;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.List;
import java.util.stream.Collectors;
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 String myResourceType;
private final String myResourcePath;
private final EmpiRulesJson myEmpiRulesJson;
public EmpiResourceFieldMatcher(FhirContext theFhirContext, EmpiFieldMatchJson theEmpiFieldMatchJson) {
public EmpiResourceFieldMatcher(FhirContext theFhirContext, EmpiFieldMatchJson theEmpiFieldMatchJson, EmpiRulesJson theEmpiRulesJson) {
myFhirContext = theFhirContext;
myEmpiFieldMatchJson = theEmpiFieldMatchJson;
myResourceType = theEmpiFieldMatchJson.getResourceType();
myResourcePath = theEmpiFieldMatchJson.getResourcePath();
myEmpiRulesJson = theEmpiRulesJson;
}
/**
@ -88,11 +92,20 @@ public class EmpiResourceFieldMatcher {
private void validate(IBaseResource theResource) {
String resourceType = myFhirContext.getResourceType(theResource);
Validate.notNull(resourceType, "Resource type may not be null");
if (ALL_RESOURCE_SEARCH_PARAM_TYPE.equals(myResourceType)) {
Validate.isTrue("Patient".equalsIgnoreCase(resourceType) || "Practitioner".equalsIgnoreCase(resourceType),
"Expecting resource type Patient/Practitioner got resource type %s", resourceType);
boolean isMdmType = myEmpiRulesJson.getMdmTypes().stream().anyMatch(mdmType -> mdmType.equalsIgnoreCase(resourceType));
Validate.isTrue(isMdmType, "Expecting resource type %s, got resource type %s", myEmpiRulesJson.getMdmTypes().stream().collect(Collectors.joining(",")), resourceType);
} else {
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 java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 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.");
}
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) {
long vector = 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.
EmpiResourceFieldMatcher fieldComparator = myFieldMatchers.get(i);
EmpiResourceFieldMatcher fieldComparator = resourceRelevantFieldMatchers.get(i);
EmpiMatchEvaluation matchEvaluation = fieldComparator.match(theLeftResource, theRightResource);
if (matchEvaluation.match) {
vector |= (1 << i);

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.empi.rules.svc;
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.similarity.EmpiSimilarityEnum;
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.Test;
import java.util.ArrayList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -20,14 +23,17 @@ public class EmpiResourceFieldMatcherR4Test extends BaseEmpiRulesR4Test {
protected EmpiResourceFieldMatcher myComparator;
private Patient myJohn;
private Patient myJohny;
private EmpiRulesJson myEmpiRulesJson;
@Override
@BeforeEach
public void before() {
super.before();
myComparator = new EmpiResourceFieldMatcher(ourFhirContext, myGivenNameMatchField);
ArrayList<String> myLegalMdmTypes = new ArrayList<>();
myLegalMdmTypes.add("Patient");
myEmpiRulesJson.setMdmTypes(myLegalMdmTypes);
myComparator = new EmpiResourceFieldMatcher(ourFhirContext, myGivenNameMatchField, myEmpiRulesJson);
myJohn = buildJohn();
myJohny = buildJohny();
}
@ -67,7 +73,7 @@ public class EmpiResourceFieldMatcherR4Test extends BaseEmpiRulesR4Test {
.setResourceType("Patient")
.setResourcePath("foo")
.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);
fail();
} catch (DataFormatException e) {

View File

@ -64,31 +64,32 @@ public class ProviderConstants {
public static final String EMPI_MATCH = "$match";
//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 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
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_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_RESOURCE_ID = "resourceId";
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_RESOURCE_ID = "resourceId";
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_DUPLICATE_PERSONS = "$empi-duplicate-golden-resources";
public static final String EMPI_NOT_DUPLICATE = "$empi-not-duplicate";
public static final String MDM_DUPLICATE_GOLDEN_RESOURCES = "$empi-duplicate-golden-resources";
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 OPERATION_EMPI_SUBMIT = "$empi-submit";
public static final String EMPI_BATCH_RUN_CRITERIA= "criteria" ;
public static final String OPERATION_MDM_SUBMIT = "$mdm-submit";
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_CLEAR_OUT_PARAM_DELETED_COUNT = "deleted";
}