Merge pull request #2019 from jamesagnew/minimum-data-required-for-empi
Add a Minimum data requirement for EMPI processing
This commit is contained in:
commit
605c3d8f8a
|
@ -28,6 +28,7 @@ import ca.uhn.fhir.interceptor.api.HookParams;
|
|||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.jpa.empi.svc.EmpiMatchLinkSvc;
|
||||
import ca.uhn.fhir.jpa.empi.svc.EmpiResourceFilteringSvc;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||
import ca.uhn.fhir.rest.server.TransactionLogMessages;
|
||||
|
@ -50,6 +51,8 @@ public class EmpiMessageHandler implements MessageHandler {
|
|||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
@Autowired
|
||||
private FhirContext myFhirContext;
|
||||
@Autowired
|
||||
private EmpiResourceFilteringSvc myEmpiResourceFileringSvc;
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message<?> theMessage) throws MessagingException {
|
||||
|
@ -62,13 +65,14 @@ public class EmpiMessageHandler implements MessageHandler {
|
|||
|
||||
ResourceModifiedMessage msg = ((ResourceModifiedJsonMessage) theMessage).getPayload();
|
||||
try {
|
||||
matchEmpiAndUpdateLinks(msg);
|
||||
if (myEmpiResourceFileringSvc.shouldBeProcessed(getResourceFromPayload(msg))) {
|
||||
matchEmpiAndUpdateLinks(msg);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ourLog.error("Failed to handle EMPI Matching Resource:", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public void matchEmpiAndUpdateLinks(ResourceModifiedMessage theMsg) {
|
||||
String resourceType = theMsg.getId(myFhirContext).getResourceType();
|
||||
validateResourceType(resourceType);
|
||||
|
|
|
@ -47,6 +47,7 @@ import ca.uhn.fhir.jpa.empi.svc.EmpiLinkSvcImpl;
|
|||
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkUpdaterSvcImpl;
|
||||
import ca.uhn.fhir.jpa.empi.svc.EmpiMatchFinderSvcImpl;
|
||||
import ca.uhn.fhir.jpa.empi.svc.EmpiMatchLinkSvc;
|
||||
import ca.uhn.fhir.jpa.empi.svc.EmpiResourceFilteringSvc;
|
||||
import ca.uhn.fhir.jpa.empi.svc.EmpiPersonDeletingSvc;
|
||||
import ca.uhn.fhir.jpa.empi.svc.EmpiPersonMergerSvcImpl;
|
||||
import ca.uhn.fhir.jpa.empi.svc.EmpiResetSvcImpl;
|
||||
|
@ -211,4 +212,9 @@ public class EmpiConsumerConfig {
|
|||
EmpiLinkDeleteSvc empiLinkDeleteSvc() {
|
||||
return new EmpiLinkDeleteSvc();
|
||||
}
|
||||
|
||||
@Bean
|
||||
EmpiResourceFilteringSvc empiResourceFilteringSvc() {
|
||||
return new EmpiResourceFilteringSvc();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package ca.uhn.fhir.jpa.empi.svc;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.empi.api.IEmpiSettings;
|
||||
import ca.uhn.fhir.empi.log.Logs;
|
||||
import ca.uhn.fhir.empi.rules.json.EmpiResourceSearchParamJson;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class EmpiResourceFilteringSvc {
|
||||
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
|
||||
|
||||
@Autowired
|
||||
private IEmpiSettings empiSettings;
|
||||
@Autowired
|
||||
EmpiSearchParamSvc myEmpiSearchParamSvc;
|
||||
@Autowired
|
||||
FhirContext myFhirContext;
|
||||
|
||||
/**
|
||||
* Given a resource from the EMPI Channel, determine whether or not EMPI processing should occur on it.
|
||||
*
|
||||
* EMPI processing should occur if for any {@link EmpiResourceSearchParamJson) Search Param, the resource contains a value.
|
||||
*
|
||||
* If the resource has no attributes that appear in the candidate search params, processing should be skipped, as there is not
|
||||
* sufficient information to perform meaningful EMPI processing. (For example, how can EMPI processing occur on a patient that has _no_ attributes?)
|
||||
*
|
||||
* @param theResource the resource that you wish to check against EMPI rules.
|
||||
*
|
||||
* @return whether or not EMPI processing should proceed
|
||||
*/
|
||||
public boolean shouldBeProcessed(IAnyResource theResource) {
|
||||
String resourceType = myFhirContext.getResourceType(theResource);
|
||||
List<EmpiResourceSearchParamJson> candidateSearchParams = empiSettings.getEmpiRules().getCandidateSearchParams();
|
||||
|
||||
boolean containsValueForSomeSearchParam = candidateSearchParams.stream()
|
||||
.filter(csp -> myEmpiSearchParamSvc.searchParamTypeIsValidForResourceType(csp.getResourceType(), resourceType))
|
||||
.flatMap(csp -> csp.getSearchParams().stream())
|
||||
.map(searchParam -> myEmpiSearchParamSvc.getValueFromResourceForSearchParam(theResource, searchParam))
|
||||
.anyMatch(valueList -> !valueList.isEmpty());
|
||||
|
||||
ourLog.debug("Is {} suitable for EMPI processing? : {}", theResource.getId(), containsValueForSomeSearchParam);
|
||||
return containsValueForSomeSearchParam;
|
||||
}
|
||||
}
|
|
@ -87,10 +87,20 @@ public class EmpiSearchParamSvc implements ISearchParamRetriever {
|
|||
spMap = mapFromCriteria(theTargetType, theCriteria);
|
||||
}
|
||||
return spMap;
|
||||
}
|
||||
}
|
||||
|
||||
public ISearchBuilder generateSearchBuilderForType(String theTargetType) {
|
||||
public ISearchBuilder generateSearchBuilderForType(String theTargetType) {
|
||||
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theTargetType);
|
||||
return mySearchBuilderFactory.newSearchBuilder(resourceDao, theTargetType, resourceDao.getResourceType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return true if the types match, or the search param type is '*', otherwise false.
|
||||
* @param theSearchParamType
|
||||
* @param theResourceType
|
||||
* @return
|
||||
*/
|
||||
public boolean searchParamTypeIsValidForResourceType(String theSearchParamType, String theResourceType) {
|
||||
return theSearchParamType.equalsIgnoreCase(theResourceType) || theSearchParamType.equalsIgnoreCase("*");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import org.hl7.fhir.r4.model.Enumerations;
|
|||
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.hl7.fhir.r4.model.SearchParameter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -62,27 +61,21 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
|
|||
super.loadEmpiSearchParameters();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePatient() throws InterruptedException {
|
||||
myEmpiHelper.createWithLatch(new Patient());
|
||||
assertLinkCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePractitioner() throws InterruptedException {
|
||||
myEmpiHelper.createWithLatch(new Practitioner());
|
||||
myEmpiHelper.createWithLatch(buildPractitionerWithNameAndId("somename", "some_id"));
|
||||
assertLinkCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePerson() throws InterruptedException {
|
||||
public void testCreatePerson() {
|
||||
myPersonDao.create(new Person());
|
||||
assertLinkCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeletePersonDeletesLinks() throws InterruptedException {
|
||||
myEmpiHelper.createWithLatch(new Patient());
|
||||
myEmpiHelper.createWithLatch(buildPaulPatient());
|
||||
assertLinkCount(1);
|
||||
Person person = getOnlyActivePerson();
|
||||
myPersonDao.delete(person.getIdElement());
|
||||
|
@ -102,6 +95,18 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatingPersonWithInsufficentEMPIAttributesIsNotEMPIProcessed() throws InterruptedException {
|
||||
myEmpiHelper.doCreateResource(new Patient(), true);
|
||||
assertLinkCount(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatingPatientWithOneOrMoreMatchingAttributesIsEMPIProcessed() throws InterruptedException {
|
||||
myEmpiHelper.createWithLatch(buildPaulPatient());
|
||||
assertLinkCount(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOrganizationWithEmpiTagForbidden() throws InterruptedException {
|
||||
//Creating a organization with the EMPI-MANAGED tag should fail
|
||||
|
@ -163,7 +168,7 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
|
|||
public void testEmpiManagedPersonCannotBeModifiedByPersonUpdateRequest() throws InterruptedException {
|
||||
// When EMPI is enabled, only the EMPI system is allowed to modify Person links of Persons with the EMPI-MANAGED tag.
|
||||
Patient patient = new Patient();
|
||||
IIdType patientId = myEmpiHelper.createWithLatch(new Patient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless();
|
||||
IIdType patientId = myEmpiHelper.createWithLatch(buildPaulPatient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless();
|
||||
|
||||
patient.setId(patientId);
|
||||
|
||||
|
@ -263,7 +268,7 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
|
|||
@Test
|
||||
public void testPatientsWithNoEIDCanBeUpdated() throws InterruptedException {
|
||||
setPreventEidUpdates(true);
|
||||
Patient p = new Patient();
|
||||
Patient p = buildPaulPatient();
|
||||
EmpiHelperR4.OutcomeAndLogMessageWrapper wrapper = myEmpiHelper.createWithLatch(p);
|
||||
|
||||
p.setId(wrapper.getDaoMethodOutcome().getId());
|
||||
|
@ -275,7 +280,7 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
|
|||
@Test
|
||||
public void testPatientsCanHaveEIDAddedInStrictMode() throws InterruptedException {
|
||||
setPreventEidUpdates(true);
|
||||
Patient p = new Patient();
|
||||
Patient p = buildPaulPatient();
|
||||
EmpiHelperR4.OutcomeAndLogMessageWrapper messageWrapper = myEmpiHelper.createWithLatch(p);
|
||||
p.setId(messageWrapper.getDaoMethodOutcome().getId());
|
||||
addExternalEID(p, "external eid");
|
||||
|
|
|
@ -31,7 +31,7 @@ public abstract class BaseLinkR4Test extends BaseProviderR4Test {
|
|||
public void before() {
|
||||
super.before();
|
||||
|
||||
myPatient = createPatientAndUpdateLinks(new Patient());
|
||||
myPatient = createPatientAndUpdateLinks(buildPaulPatient());
|
||||
myPatientId = new StringType(myPatient.getIdElement().getValue());
|
||||
|
||||
myPerson = getPersonFromTarget(myPatient);
|
||||
|
|
|
@ -36,7 +36,7 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
|
|||
@BeforeEach
|
||||
public void before() {
|
||||
super.before();
|
||||
myPractitioner = createPractitionerAndUpdateLinks(new Practitioner());
|
||||
myPractitioner = createPractitionerAndUpdateLinks(buildPractitionerWithNameAndId("some_pract", "some_pract_id"));
|
||||
myPractitionerId = new StringType(myPractitioner.getIdElement().getValue());
|
||||
myPractitionerPerson = getPersonFromTarget(myPractitioner);
|
||||
myPractitionerPersonId = new StringType(myPractitionerPerson.getIdElement().getValue());
|
||||
|
|
|
@ -17,7 +17,7 @@ import java.util.Date;
|
|||
class EmpiBatchSvcImplTest extends BaseEmpiR4Test {
|
||||
|
||||
@Autowired
|
||||
IEmpiBatchSvc myEmpiBatchSvc;
|
||||
IEmpiBatchSvc myEmpiBatchSvc;
|
||||
|
||||
@Autowired
|
||||
IInterceptorService myInterceptorService;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package ca.uhn.fhir.jpa.empi.svc;
|
||||
|
||||
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
class EmpiResourceFilteringSvcTest extends BaseEmpiR4Test {
|
||||
|
||||
@Autowired
|
||||
private EmpiResourceFilteringSvc myEmpiResourceFilteringSvc;
|
||||
|
||||
@Test
|
||||
public void testFilterResourcesWhichHaveNoRelevantAttributes() {
|
||||
Patient patient = new Patient();
|
||||
patient.setDeceased(new BooleanType(true)); //EMPI rules defined do not care about the deceased attribute.
|
||||
|
||||
//SUT
|
||||
boolean shouldBeProcessed = myEmpiResourceFilteringSvc.shouldBeProcessed(patient);
|
||||
|
||||
assertThat(shouldBeProcessed, is(equalTo(false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoNotFilterResourcesWithEMPIAttributes() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setValue("Hey I'm an ID! rules defined in empi-rules.json care about me!");
|
||||
|
||||
//SUT
|
||||
boolean shouldBeProcessed = myEmpiResourceFilteringSvc.shouldBeProcessed(patient);
|
||||
|
||||
assertThat(shouldBeProcessed, is(equalTo(true)));
|
||||
}
|
||||
}
|
|
@ -399,6 +399,7 @@ public class SearchParamExtractorService {
|
|||
myInterceptorBroadcaster = theJpaInterceptorBroadcaster;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<String> extractParamValuesAsStrings(RuntimeSearchParam theActiveSearchParam, IBaseResource theResource) {
|
||||
return mySearchParamExtractor.extractParamValuesAsStrings(theActiveSearchParam, theResource);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ public class EmpiResourceSearchParamJson implements IModelJson, Iterable<String>
|
|||
return this;
|
||||
}
|
||||
|
||||
private List<String> getSearchParams() {
|
||||
public List<String> getSearchParams() {
|
||||
if (mySearchParams == null) {
|
||||
mySearchParams = new ArrayList<>();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue