#2184 Initial implementation
This commit is contained in:
parent
53c23b8d9b
commit
170cf245cc
|
@ -35,6 +35,7 @@ import java.util.stream.Collectors;
|
|||
|
||||
@Service
|
||||
public class EmpiMatchFinderSvcImpl implements IEmpiMatchFinderSvc {
|
||||
|
||||
@Autowired
|
||||
private EmpiCandidateSearchSvc myEmpiCandidateSearchSvc;
|
||||
@Autowired
|
||||
|
@ -50,13 +51,4 @@ public class EmpiMatchFinderSvcImpl implements IEmpiMatchFinderSvc {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public List<IAnyResource> findMatches(String theResourceType, IAnyResource theResource) {
|
||||
List<MatchedTarget> targetCandidates = getMatchedTargets(theResourceType, theResource);
|
||||
return targetCandidates.stream()
|
||||
.filter(candidate -> candidate.isMatch())
|
||||
.map(MatchedTarget::getTarget)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
package ca.uhn.fhir.jpa.empi.provider;
|
||||
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.codesystems.MatchGrade;
|
||||
import org.junit.Assert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderMatchR4Test.class);
|
||||
|
||||
@Override
|
||||
@BeforeEach
|
||||
|
@ -26,7 +34,30 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
|
|||
|
||||
Bundle result = myEmpiProviderR4.match(newJane);
|
||||
assertEquals(1, result.getEntry().size());
|
||||
assertEquals(createdJane.getId(), result.getEntryFirstRep().getResource().getId());
|
||||
|
||||
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("http://hl7.org/fhir/StructureDefinition/match-grade");
|
||||
assertNotNull(matchGradeExtension);
|
||||
assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMismatch() throws Exception {
|
||||
Patient jane = buildJanePatient();
|
||||
jane.setActive(true);
|
||||
Patient createdJane = createPatient(jane);
|
||||
|
||||
Patient paul = buildPaulPatient();
|
||||
paul.setActive(true);
|
||||
|
||||
Bundle result = myEmpiProviderR4.match(paul);
|
||||
assertEquals(0, result.getEntry().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -105,6 +105,21 @@ public final class EmpiMatchOutcome {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets normalized score that is in the range from zero to one
|
||||
*
|
||||
* @return
|
||||
* Returns the normalized score
|
||||
*/
|
||||
public Double getNormalizedScore() {
|
||||
if (vector == 0) {
|
||||
return 0.0;
|
||||
} else if (score > vector) {
|
||||
return 1.0;
|
||||
}
|
||||
return score / vector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this)
|
||||
|
|
|
@ -37,16 +37,4 @@ public interface IEmpiMatchFinderSvc {
|
|||
*/
|
||||
@Nonnull
|
||||
List<MatchedTarget> getMatchedTargets(String theResourceType, IAnyResource theResource);
|
||||
|
||||
/**
|
||||
* Used by the $match operation.
|
||||
* Retrieve a list of Patient/Practitioner matches, based on the given {@link IAnyResource}
|
||||
* Internally, performs all EMPI matching rules on the type of the resource then returns only those
|
||||
* with a match result of MATCHED.
|
||||
*
|
||||
* @param theResourceType the type of the resource.
|
||||
* @param theResource the resource that we are attempting to find matches for.
|
||||
* @return a List of {@link IAnyResource} representing all people who had a MATCH outcome.
|
||||
*/
|
||||
List<IAnyResource> findMatches(String theResourceType, IAnyResource theResource);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import ca.uhn.fhir.empi.api.IEmpiControllerSvc;
|
|||
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
|
||||
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
||||
import ca.uhn.fhir.empi.api.IEmpiSubmitSvc;
|
||||
import ca.uhn.fhir.empi.api.MatchedTarget;
|
||||
import ca.uhn.fhir.empi.model.EmpiTransactionContext;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
|
@ -37,6 +38,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.CodeType;
|
||||
import org.hl7.fhir.dstu3.model.DecimalType;
|
||||
import org.hl7.fhir.dstu3.model.InstantType;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
|
@ -45,10 +47,12 @@ import org.hl7.fhir.dstu3.model.Person;
|
|||
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 java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -77,20 +81,45 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider {
|
|||
if (thePatient == null) {
|
||||
throw new InvalidRequestException("resource may not be null");
|
||||
}
|
||||
Collection<IAnyResource> matches = myEmpiMatchFinderSvc.findMatches("Patient", thePatient);
|
||||
List<MatchedTarget> matches = myEmpiMatchFinderSvc.getMatchedTargets("Patient", thePatient);
|
||||
|
||||
Bundle retVal = new Bundle();
|
||||
retVal.setType(Bundle.BundleType.SEARCHSET);
|
||||
retVal.setId(UUID.randomUUID().toString());
|
||||
retVal.getMeta().setLastUpdatedElement(InstantType.now());
|
||||
|
||||
for (IAnyResource next : matches) {
|
||||
retVal.addEntry().setResource((Resource) next);
|
||||
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;
|
||||
}
|
||||
|
||||
private Bundle.BundleEntrySearchComponent toBundleEntrySearchComponent(MatchedTarget next) {
|
||||
Bundle.BundleEntrySearchComponent searchComponent = new Bundle.BundleEntrySearchComponent();
|
||||
searchComponent.setMode(Bundle.SearchEntryMode.MATCH);
|
||||
searchComponent.setScore(next.getMatchResult().getNormalizedScore());
|
||||
|
||||
MatchGrade matchGrade = MatchGrade.PROBABLE;
|
||||
if (next.isMatch()) {
|
||||
matchGrade = MatchGrade.CERTAIN;
|
||||
} else if (next.isPossibleMatch()) {
|
||||
matchGrade = MatchGrade.POSSIBLE;
|
||||
}
|
||||
searchComponent.addExtension("http://hl7.org/fhir/StructureDefinition/match-grade",
|
||||
new CodeType(matchGrade.toCode()));
|
||||
return searchComponent;
|
||||
}
|
||||
|
||||
@Operation(name = ProviderConstants.EMPI_MERGE_PERSONS, type = Person.class)
|
||||
public Person mergePerson(@OperationParam(name=ProviderConstants.EMPI_MERGE_PERSONS_FROM_PERSON_ID, min = 1, max = 1) StringType theFromPersonId,
|
||||
@OperationParam(name=ProviderConstants.EMPI_MERGE_PERSONS_TO_PERSON_ID, min = 1, max = 1) StringType theToPersonId,
|
||||
|
|
|
@ -26,6 +26,7 @@ import ca.uhn.fhir.empi.api.IEmpiControllerSvc;
|
|||
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
|
||||
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
|
||||
import ca.uhn.fhir.empi.api.IEmpiSubmitSvc;
|
||||
import ca.uhn.fhir.empi.api.MatchedTarget;
|
||||
import ca.uhn.fhir.empi.model.EmpiTransactionContext;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
|
@ -39,7 +40,9 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.Extension;
|
||||
import org.hl7.fhir.r4.model.InstantType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
|
@ -48,8 +51,10 @@ import org.hl7.fhir.r4.model.Person;
|
|||
import org.hl7.fhir.r4.model.Practitioner;
|
||||
import org.hl7.fhir.r4.model.Resource;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.codesystems.MatchGrade;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
@ -74,25 +79,50 @@ public class EmpiProviderR4 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.EMPI_MATCH_RESOURCE, min = 1, max = 1) Patient thePatient) {
|
||||
if (thePatient == null) {
|
||||
throw new InvalidRequestException("resource may not be null");
|
||||
}
|
||||
|
||||
Collection<IAnyResource> matches = myEmpiMatchFinderSvc.findMatches("Patient", thePatient);
|
||||
List<MatchedTarget> matches = myEmpiMatchFinderSvc.getMatchedTargets("Patient", thePatient);
|
||||
|
||||
Bundle retVal = new Bundle();
|
||||
retVal.setType(Bundle.BundleType.SEARCHSET);
|
||||
retVal.setId(UUID.randomUUID().toString());
|
||||
retVal.getMeta().setLastUpdatedElement(InstantType.now());
|
||||
|
||||
for (IAnyResource next : matches) {
|
||||
retVal.addEntry().setResource((Resource) next);
|
||||
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;
|
||||
}
|
||||
|
||||
private Bundle.BundleEntrySearchComponent toBundleEntrySearchComponent(MatchedTarget next) {
|
||||
Bundle.BundleEntrySearchComponent searchComponent = new Bundle.BundleEntrySearchComponent();
|
||||
searchComponent.setMode(Bundle.SearchEntryMode.MATCH);
|
||||
searchComponent.setScore(next.getMatchResult().getNormalizedScore());
|
||||
|
||||
MatchGrade matchGrade = MatchGrade.PROBABLE;
|
||||
if (next.isMatch()) {
|
||||
matchGrade = MatchGrade.CERTAIN;
|
||||
} else if (next.isPossibleMatch()) {
|
||||
matchGrade = MatchGrade.POSSIBLE;
|
||||
}
|
||||
searchComponent.addExtension("http://hl7.org/fhir/StructureDefinition/match-grade",
|
||||
new CodeType(matchGrade.toCode()));
|
||||
return searchComponent;
|
||||
}
|
||||
|
||||
@Operation(name = ProviderConstants.EMPI_MERGE_PERSONS, type = Person.class)
|
||||
public Person mergePersons(@OperationParam(name=ProviderConstants.EMPI_MERGE_PERSONS_FROM_PERSON_ID, min = 1, max = 1) StringType theFromPersonId,
|
||||
@OperationParam(name=ProviderConstants.EMPI_MERGE_PERSONS_TO_PERSON_ID, min = 1, max = 1) StringType theToPersonId,
|
||||
|
|
Loading…
Reference in New Issue