Search in sources :

Example 1 with MATCH

use of org.hl7.fhir.r4.model.Bundle.SearchEntryMode.MATCH in project beneficiary-fhir-data by CMSgov.

the class TransformerUtils method addCareTeamPractitioner.

/**
 * Ensures that the specified {@link ExplanationOfBenefit} has the specified {@link
 * CareTeamComponent}, and links the specified {@link ItemComponent} to that {@link
 * CareTeamComponent} (via {@link ItemComponent#addCareTeamLinkId(int)}).
 *
 * @param eob the {@link ExplanationOfBenefit} that the {@link CareTeamComponent} should be part
 *     of
 * @param eobItem the {@link ItemComponent} that should be linked to the {@link CareTeamComponent}
 * @param practitionerIdSystem the {@link Identifier#getSystem()} of the practitioner to reference
 *     in {@link CareTeamComponent#getProvider()}
 * @param practitionerIdValue the {@link Identifier#getValue()} of the practitioner to reference
 *     in {@link CareTeamComponent#getProvider()}
 * @param careTeamRole the {@link ClaimCareteamrole} to use for the {@link
 *     CareTeamComponent#getRole()}
 * @return the {@link CareTeamComponent} that was created/linked
 */
static CareTeamComponent addCareTeamPractitioner(ExplanationOfBenefit eob, ItemComponent eobItem, String practitionerIdSystem, String practitionerIdValue, ClaimCareteamrole careTeamRole) {
    // Try to find a matching pre-existing entry.
    CareTeamComponent careTeamEntry = eob.getCareTeam().stream().filter(ctc -> ctc.getProvider().hasIdentifier()).filter(ctc -> practitionerIdSystem.equals(ctc.getProvider().getIdentifier().getSystem()) && practitionerIdValue.equals(ctc.getProvider().getIdentifier().getValue())).filter(ctc -> ctc.hasRole()).filter(ctc -> careTeamRole.toCode().equals(ctc.getRole().getCodingFirstRep().getCode()) && careTeamRole.getSystem().equals(ctc.getRole().getCodingFirstRep().getSystem())).findAny().orElse(null);
    // If no match was found, add one to the EOB.
    if (careTeamEntry == null) {
        careTeamEntry = eob.addCareTeam();
        careTeamEntry.setSequence(eob.getCareTeam().size() + 1);
        careTeamEntry.setProvider(createIdentifierReference(practitionerIdSystem, practitionerIdValue));
        CodeableConcept careTeamRoleConcept = createCodeableConcept(ClaimCareteamrole.OTHER.getSystem(), careTeamRole.toCode());
        careTeamRoleConcept.getCodingFirstRep().setDisplay(careTeamRole.getDisplay());
        careTeamEntry.setRole(careTeamRoleConcept);
    }
    // care team entry is at eob level so no need to create item link id
    if (eobItem == null) {
        return careTeamEntry;
    }
    // Link the EOB.item to the care team entry (if it isn't already).
    final int careTeamEntrySequence = careTeamEntry.getSequence();
    if (eobItem.getCareTeamLinkId().stream().noneMatch(id -> id.getValue() == careTeamEntrySequence)) {
        eobItem.addCareTeamLinkId(careTeamEntrySequence);
    }
    return careTeamEntry;
}
Also used : Arrays(java.util.Arrays) CarrierClaimColumn(gov.cms.bfd.model.rif.CarrierClaimColumn) CcwCodebookInterface(gov.cms.bfd.model.codebook.model.CcwCodebookInterface) Identifier(org.hl7.fhir.dstu3.model.Identifier) Constants(ca.uhn.fhir.rest.api.Constants) Coding(org.hl7.fhir.dstu3.model.Coding) InpatientClaim(gov.cms.bfd.model.rif.InpatientClaim) CodeableConcept(org.hl7.fhir.dstu3.model.CodeableConcept) DomainResource(org.hl7.fhir.dstu3.model.DomainResource) IBaseExtension(org.hl7.fhir.instance.model.api.IBaseExtension) StringUtils(org.apache.commons.lang3.StringUtils) SNFClaimColumn(gov.cms.bfd.model.rif.SNFClaimColumn) BigDecimal(java.math.BigDecimal) SNFClaimLine(gov.cms.bfd.model.rif.SNFClaimLine) IBaseResource(org.hl7.fhir.instance.model.api.IBaseResource) CarrierClaimLine(gov.cms.bfd.model.rif.CarrierClaimLine) Map(java.util.Map) ProcedureComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.ProcedureComponent) CcwCodebookMissingVariable(gov.cms.bfd.model.codebook.data.CcwCodebookMissingVariable) TemporalPrecisionEnum(ca.uhn.fhir.model.api.TemporalPrecisionEnum) Value(gov.cms.bfd.model.codebook.model.Value) Diagnosis(gov.cms.bfd.server.war.commons.Diagnosis) OutpatientClaim(gov.cms.bfd.model.rif.OutpatientClaim) Coverage(org.hl7.fhir.dstu3.model.Coverage) IdDt(ca.uhn.fhir.model.primitive.IdDt) ExplanationOfBenefit(org.hl7.fhir.dstu3.model.ExplanationOfBenefit) MedicareSegment(gov.cms.bfd.server.war.commons.MedicareSegment) Reference(org.hl7.fhir.dstu3.model.Reference) DiagnosisComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.DiagnosisComponent) InpatientClaimColumn(gov.cms.bfd.model.rif.InpatientClaimColumn) Set(java.util.Set) StandardCharsets(java.nio.charset.StandardCharsets) ZoneId(java.time.ZoneId) UncheckedIOException(java.io.UncheckedIOException) ReferralRequestStatus(org.hl7.fhir.dstu3.model.ReferralRequest.ReferralRequestStatus) Stream(java.util.stream.Stream) ReferralRequestRequesterComponent(org.hl7.fhir.dstu3.model.ReferralRequest.ReferralRequestRequesterComponent) CarrierClaim(gov.cms.bfd.model.rif.CarrierClaim) HHAClaimLine(gov.cms.bfd.model.rif.HHAClaimLine) Money(org.hl7.fhir.dstu3.model.Money) ReferralRequest(org.hl7.fhir.dstu3.model.ReferralRequest) BenefitComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.BenefitComponent) IAnyResource(org.hl7.fhir.instance.model.api.IAnyResource) ArrayList(java.util.ArrayList) Strings(com.google.common.base.Strings) CCWUtils(gov.cms.bfd.server.war.commons.CCWUtils) RequestDetails(ca.uhn.fhir.rest.api.server.RequestDetails) ResourceType(org.hl7.fhir.dstu3.model.ResourceType) CcwCodebookVariable(gov.cms.bfd.model.codebook.data.CcwCodebookVariable) Period(org.hl7.fhir.dstu3.model.Period) CurrencyIdentifier(gov.cms.bfd.server.war.stu3.providers.BeneficiaryTransformer.CurrencyIdentifier) CCWProcedure(gov.cms.bfd.server.war.commons.CCWProcedure) OffsetLinkBuilder(gov.cms.bfd.server.war.commons.OffsetLinkBuilder) Practitioner(org.hl7.fhir.dstu3.model.Practitioner) MetricRegistry(com.codahale.metrics.MetricRegistry) LinkBuilder(gov.cms.bfd.server.war.commons.LinkBuilder) BadCodeMonkeyException(gov.cms.bfd.sharedutils.exceptions.BadCodeMonkeyException) SimpleQuantity(org.hl7.fhir.dstu3.model.SimpleQuantity) IOException(java.io.IOException) Observation(org.hl7.fhir.dstu3.model.Observation) InputStreamReader(java.io.InputStreamReader) FDADrugDataUtilityApp(gov.cms.bfd.server.war.FDADrugDataUtilityApp) SupportingInformationComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.SupportingInformationComponent) DMEClaimColumn(gov.cms.bfd.model.rif.DMEClaimColumn) Patient(org.hl7.fhir.dstu3.model.Patient) MDC(org.slf4j.MDC) BufferedReader(java.io.BufferedReader) IBaseHasExtensions(org.hl7.fhir.instance.model.api.IBaseHasExtensions) Bundle(org.hl7.fhir.dstu3.model.Bundle) Date(java.util.Date) BundleEntryComponent(org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent) LoggerFactory(org.slf4j.LoggerFactory) Extension(org.hl7.fhir.dstu3.model.Extension) ClaimCareteamrole(org.hl7.fhir.dstu3.model.codesystems.ClaimCareteamrole) Organization(org.hl7.fhir.dstu3.model.Organization) SNFClaim(gov.cms.bfd.model.rif.SNFClaim) HHAClaimColumn(gov.cms.bfd.model.rif.HHAClaimColumn) BenefitBalanceComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.BenefitBalanceComponent) Collection(java.util.Collection) ItemComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.ItemComponent) Instant(java.time.Instant) Collectors(java.util.stream.Collectors) DiagnosisLabel(gov.cms.bfd.server.war.commons.Diagnosis.DiagnosisLabel) Quantity(org.hl7.fhir.dstu3.model.Quantity) UnsignedIntType(org.hl7.fhir.dstu3.model.UnsignedIntType) Objects(java.util.Objects) Beneficiary(gov.cms.bfd.model.rif.Beneficiary) List(java.util.List) CareTeamComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.CareTeamComponent) TransformerConstants(gov.cms.bfd.server.war.commons.TransformerConstants) LocalDate(java.time.LocalDate) Optional(java.util.Optional) UnsupportedEncodingException(java.io.UnsupportedEncodingException) DateType(org.hl7.fhir.dstu3.model.DateType) OutpatientClaimColumn(gov.cms.bfd.model.rif.OutpatientClaimColumn) HospiceClaimLine(gov.cms.bfd.model.rif.HospiceClaimLine) DataFormatException(ca.uhn.fhir.parser.DataFormatException) HashMap(java.util.HashMap) InpatientClaimLine(gov.cms.bfd.model.rif.InpatientClaimLine) OutpatientClaimLine(gov.cms.bfd.model.rif.OutpatientClaimLine) HashSet(java.util.HashSet) ExplanationOfBenefitStatus(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.ExplanationOfBenefitStatus) HHAClaim(gov.cms.bfd.model.rif.HHAClaim) ObservationStatus(org.hl7.fhir.dstu3.model.Observation.ObservationStatus) InvalidRifValueException(gov.cms.bfd.model.rif.parse.InvalidRifValueException) DMEClaim(gov.cms.bfd.model.rif.DMEClaim) DMEClaimLine(gov.cms.bfd.model.rif.DMEClaimLine) LinkedList(java.util.LinkedList) NoSuchElementException(java.util.NoSuchElementException) AdjudicationComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.AdjudicationComponent) BenefitCategory(org.hl7.fhir.dstu3.model.codesystems.BenefitCategory) Logger(org.slf4j.Logger) Resource(org.hl7.fhir.dstu3.model.Resource) Variable(gov.cms.bfd.model.codebook.model.Variable) HospiceClaim(gov.cms.bfd.model.rif.HospiceClaim) Consumer(java.util.function.Consumer) URLEncoder(java.net.URLEncoder) IdentifierType(gov.cms.bfd.server.war.commons.IdentifierType) InputStream(java.io.InputStream) CareTeamComponent(org.hl7.fhir.dstu3.model.ExplanationOfBenefit.CareTeamComponent) CodeableConcept(org.hl7.fhir.dstu3.model.CodeableConcept)

Example 2 with MATCH

use of org.hl7.fhir.r4.model.Bundle.SearchEntryMode.MATCH in project beneficiary-fhir-data by CMSgov.

the class TransformerUtilsV2 method mapEobType.

/**
 * maps a blue button claim type to a FHIR claim type
 *
 * @param eob the {@link CodeableConcept} that will get remapped
 * @param blueButtonClaimType the blue button {@link ClaimTypeV2} we are mapping from
 * @param ccwNearLineRecordIdCode if present, the blue button near line id code {@link
 *     Optional}<{@link Character}> gets remapped to a ccw record id code
 * @param ccwClaimTypeCode if present, the blue button claim type code {@link Optional}<{@link
 *     String}> gets remapped to a nch claim type code
 */
static void mapEobType(ExplanationOfBenefit eob, ClaimTypeV2 blueButtonClaimType, Optional<Character> ccwNearLineRecordIdCode, Optional<String> ccwClaimTypeCode) {
    // NCH_CLM_TYPE_CD => ExplanationOfBenefit.type.coding
    if (ccwClaimTypeCode.isPresent()) {
        eob.getType().addCoding(createCoding(eob, CcwCodebookVariable.NCH_CLM_TYPE_CD, ccwClaimTypeCode));
    }
    // This Coding MUST always be present as it's the only one we can definitely map
    // for all 8 of our claim types.
    // EOB Type => ExplanationOfBenefit.type.coding
    eob.getType().addCoding().setSystem(TransformerConstants.CODING_SYSTEM_BBAPI_EOB_TYPE).setCode(blueButtonClaimType.name());
    // Map a Coding for FHIR's ClaimType coding system, if we can.
    org.hl7.fhir.r4.model.codesystems.ClaimType fhirClaimType;
    switch(blueButtonClaimType) {
        case PDE:
            fhirClaimType = org.hl7.fhir.r4.model.codesystems.ClaimType.PHARMACY;
            break;
        case INPATIENT:
        case OUTPATIENT:
        case HOSPICE:
        case SNF:
        case DME:
            fhirClaimType = org.hl7.fhir.r4.model.codesystems.ClaimType.INSTITUTIONAL;
            break;
        case CARRIER:
        case HHA:
            fhirClaimType = org.hl7.fhir.r4.model.codesystems.ClaimType.PROFESSIONAL;
            break;
        default:
            // All options on ClaimTypeV2 are covered above, but this is there to appease linter
            throw new BadCodeMonkeyException("No match found for ClaimTypeV2");
    }
    // Claim Type => ExplanationOfBenefit.type.coding
    if (fhirClaimType != null) {
        eob.getType().addCoding(new Coding(fhirClaimType.getSystem(), fhirClaimType.toCode(), fhirClaimType.getDisplay()));
    }
    // NCH_NEAR_LINE_REC_IDENT_CD => ExplanationOfBenefit.extension
    if (ccwNearLineRecordIdCode.isPresent()) {
        eob.addExtension(createExtensionCoding(eob, CcwCodebookVariable.NCH_NEAR_LINE_REC_IDENT_CD, ccwNearLineRecordIdCode));
    }
}
Also used : BadCodeMonkeyException(gov.cms.bfd.sharedutils.exceptions.BadCodeMonkeyException) Coding(org.hl7.fhir.r4.model.Coding)

Example 3 with MATCH

use of org.hl7.fhir.r4.model.Bundle.SearchEntryMode.MATCH in project beneficiary-fhir-data by CMSgov.

the class PatientResourceProvider method queryDatabaseByHash.

/**
 * @param hash the {@link Beneficiary} hash value to match
 * @param hashType a string to represent the hash type (used for logging purposes)
 * @param requestHeader the {@link #RequestHeaders} where resource request headers are
 *     encapsulated
 * @param beneficiaryHashField the JPA location of the beneficiary hash field
 * @param beneficiaryHistoryHashField the JPA location of the beneficiary history hash field
 * @return a FHIR {@link Patient} for the CCW {@link Beneficiary} that matches the specified
 *     {@link Beneficiary} hash value
 * @throws NoResultException A {@link NoResultException} will be thrown if no matching {@link
 *     Beneficiary} can be found
 */
@Trace
private Patient queryDatabaseByHash(String hash, String hashType, SingularAttribute<Beneficiary, String> beneficiaryHashField, SingularAttribute<BeneficiaryHistory, String> beneficiaryHistoryHashField, RequestHeaders requestHeader) {
    if (hash == null || hash.trim().isEmpty())
        throw new IllegalArgumentException();
    /*
     * Beneficiaries' HICN/MBIs can change over time and those past HICN/MBIs may land in
     * BeneficiaryHistory records. Accordingly, we need to search for matching HICN/MBIs in both the
     * Beneficiary and the BeneficiaryHistory records.
     *
     * There's no sane way to do this in a single query with JPA 2.1, it appears: JPA doesn't
     * support UNIONs and it doesn't support subqueries in FROM clauses. That said, the ideal query
     * would look like this:
     *
     * SELECT * FROM ( SELECT DISTINCT "beneficiaryId" FROM "Beneficiaries" WHERE "hicn" =
     * :'hicn_hash' UNION SELECT DISTINCT "beneficiaryId" FROM "BeneficiariesHistory" WHERE "hicn" =
     * :'hicn_hash') AS matching_benes INNER JOIN "Beneficiaries" ON matching_benes."beneficiaryId"
     * = "Beneficiaries"."beneficiaryId" LEFT JOIN "BeneficiariesHistory" ON
     * "Beneficiaries"."beneficiaryId" = "BeneficiariesHistory"."beneficiaryId" LEFT JOIN
     * "MedicareBeneficiaryIdHistory" ON "Beneficiaries"."beneficiaryId" =
     * "MedicareBeneficiaryIdHistory"."beneficiaryId";
     *
     * ... with the returned columns and JOINs being dynamic, depending on IncludeIdentifiers.
     *
     * In lieu of that, we run two queries: one to find HICN/MBI matches in BeneficiariesHistory,
     * and a second to find BENE_ID or HICN/MBI matches in Beneficiaries (with all of their data, so
     * we're ready to return the result). This is bad and dumb but I can't find a better working
     * alternative.
     *
     * (I'll just note that I did also try JPA/Hibernate native SQL queries but couldn't get the
     * joins or fetch groups to work with them.)
     *
     * If we want to fix this, we need to move identifiers out entirely to separate tables:
     * BeneficiaryHicns and BeneficiaryMbis. We could then safely query these tables and join them
     * back to Beneficiaries (and hopefully the optimizer will play nice, too).
     */
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    // First, find all matching hashes from BeneficiariesHistory.
    CriteriaQuery<String> beneHistoryMatches = builder.createQuery(String.class);
    Root<BeneficiaryHistory> beneHistoryMatchesRoot = beneHistoryMatches.from(BeneficiaryHistory.class);
    beneHistoryMatches.select(beneHistoryMatchesRoot.get(BeneficiaryHistory_.beneficiaryId));
    beneHistoryMatches.where(builder.equal(beneHistoryMatchesRoot.get(beneficiaryHistoryHashField), hash));
    List<String> matchingIdsFromBeneHistory = null;
    Long hicnsFromHistoryQueryNanoSeconds = null;
    Timer.Context beneHistoryMatchesTimer = metricRegistry.timer(MetricRegistry.name(getClass().getSimpleName(), "query", "bene_by_" + hashType, hashType + "s_from_beneficiarieshistory")).time();
    try {
        matchingIdsFromBeneHistory = entityManager.createQuery(beneHistoryMatches).getResultList();
    } finally {
        hicnsFromHistoryQueryNanoSeconds = beneHistoryMatchesTimer.stop();
        TransformerUtils.recordQueryInMdc("bene_by_" + hashType + "." + hashType + "s_from_beneficiarieshistory", hicnsFromHistoryQueryNanoSeconds, matchingIdsFromBeneHistory == null ? 0 : matchingIdsFromBeneHistory.size());
    }
    // Then, find all Beneficiary records that match the hash or those BENE_IDs.
    CriteriaQuery<Beneficiary> beneMatches = builder.createQuery(Beneficiary.class);
    Root<Beneficiary> beneMatchesRoot = beneMatches.from(Beneficiary.class);
    beneMatchesRoot.fetch(Beneficiary_.skippedRifRecords, JoinType.LEFT);
    if (requestHeader.isHICNinIncludeIdentifiers())
        beneMatchesRoot.fetch(Beneficiary_.beneficiaryHistories, JoinType.LEFT);
    if (requestHeader.isMBIinIncludeIdentifiers())
        beneMatchesRoot.fetch(Beneficiary_.medicareBeneficiaryIdHistories, JoinType.LEFT);
    beneMatches.select(beneMatchesRoot);
    Predicate beneHashMatches = builder.equal(beneMatchesRoot.get(beneficiaryHashField), hash);
    if (matchingIdsFromBeneHistory != null && !matchingIdsFromBeneHistory.isEmpty()) {
        Predicate beneHistoryHashMatches = beneMatchesRoot.get(Beneficiary_.beneficiaryId).in(matchingIdsFromBeneHistory);
        beneMatches.where(builder.or(beneHashMatches, beneHistoryHashMatches));
    } else {
        beneMatches.where(beneHashMatches);
    }
    List<Beneficiary> matchingBenes = Collections.emptyList();
    Long benesByHashOrIdQueryNanoSeconds = null;
    Timer.Context timerHicnQuery = metricRegistry.timer(MetricRegistry.name(getClass().getSimpleName(), "query", "bene_by_" + hashType, "bene_by_" + hashType + "_or_id")).time();
    try {
        matchingBenes = entityManager.createQuery(beneMatches).getResultList();
    } finally {
        benesByHashOrIdQueryNanoSeconds = timerHicnQuery.stop();
        TransformerUtils.recordQueryInMdc(String.format("bene_by_" + hashType + ".bene_by_" + hashType + "_or_id.include_%s", String.join("_", (List<String>) requestHeader.getValue(HEADER_NAME_INCLUDE_IDENTIFIERS))), benesByHashOrIdQueryNanoSeconds, matchingBenes.size());
    }
    // Then, if we found more than one distinct BENE_ID, or none, throw an error.
    long distinctBeneIds = matchingBenes.stream().map(Beneficiary::getBeneficiaryId).filter(Objects::nonNull).distinct().count();
    Beneficiary beneficiary = null;
    if (distinctBeneIds <= 0) {
        throw new NoResultException();
    } else if (distinctBeneIds > 1) {
        MDC.put("database_query.by_hash.collision.distinct_bene_ids", Long.toString(distinctBeneIds));
        throw new ResourceNotFoundException("By hash query found more than one distinct BENE_ID: " + Long.toString(distinctBeneIds));
    } else if (distinctBeneIds == 1) {
        beneficiary = matchingBenes.get(0);
    }
    // Null out the unhashed HICNs if we're not supposed to be returning them
    if (!requestHeader.isHICNinIncludeIdentifiers()) {
        beneficiary.setHicnUnhashed(Optional.empty());
    }
    // Null out the unhashed MBIs if we're not supposed to be returning
    if (!requestHeader.isMBIinIncludeIdentifiers()) {
        beneficiary.setMedicareBeneficiaryId(Optional.empty());
    }
    Patient patient = BeneficiaryTransformer.transform(metricRegistry, beneficiary, requestHeader);
    return patient;
}
Also used : CriteriaBuilder(javax.persistence.criteria.CriteriaBuilder) BeneficiaryHistory(gov.cms.bfd.model.rif.BeneficiaryHistory) Patient(org.hl7.fhir.dstu3.model.Patient) NoResultException(javax.persistence.NoResultException) Predicate(javax.persistence.criteria.Predicate) Timer(com.codahale.metrics.Timer) Objects(java.util.Objects) ResourceNotFoundException(ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException) Beneficiary(gov.cms.bfd.model.rif.Beneficiary) Trace(com.newrelic.api.agent.Trace)

Example 4 with MATCH

use of org.hl7.fhir.r4.model.Bundle.SearchEntryMode.MATCH in project beneficiary-fhir-data by CMSgov.

the class PatientResourceProvider method searchByIdentifier.

/**
 * Adds support for the FHIR "search" operation for {@link Patient}s, allowing users to search by
 * {@link Patient#getIdentifier()}. Specifically, the following criteria are supported:
 *
 * <ul>
 *   <li>Matching a {@link Beneficiary#getHicn()} hash value: when {@link TokenParam#getSystem()}
 *       matches one of the {@link #SUPPORTED_HASH_IDENTIFIER_SYSTEMS} entries.
 * </ul>
 *
 * <p>Searches that don't match one of the above forms are not supported.
 *
 * <p>The {@link Search} annotation indicates that this method supports the search operation.
 * There may be many different methods annotated with this {@link Search} annotation, to support
 * many different search criteria.
 *
 * @param identifier an {@link Identifier} {@link TokenParam} for the {@link
 *     Patient#getIdentifier()} to try and find a matching {@link Patient} for
 * @param startIndex an {@link OptionalParam} for the startIndex (or offset) used to determine
 *     pagination
 * @param lastUpdated an {@link OptionalParam} to filter the results based on the passed date
 *     range
 * @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
 *     to parse out pagination values
 * @return Returns a {@link List} of {@link Patient}s, which may contain multiple matching
 *     resources, or may also be empty.
 */
@Search
@Trace
public Bundle searchByIdentifier(@RequiredParam(name = Patient.SP_IDENTIFIER) @Description(shortDefinition = "The patient identifier to search for") TokenParam identifier, @OptionalParam(name = "startIndex") @Description(shortDefinition = "The offset used for result pagination") String startIndex, @OptionalParam(name = "_lastUpdated") @Description(shortDefinition = "Include resources last updated in the given range") DateRangeParam lastUpdated, RequestDetails requestDetails) {
    if (identifier.getQueryParameterQualifier() != null)
        throw new InvalidRequestException("Unsupported query parameter qualifier: " + identifier.getQueryParameterQualifier());
    if (!SUPPORTED_HASH_IDENTIFIER_SYSTEMS.contains(identifier.getSystem()))
        throw new InvalidRequestException("Unsupported identifier system: " + identifier.getSystem());
    RequestHeaders requestHeader = RequestHeaders.getHeaderWrapper(requestDetails);
    Operation operation = new Operation(Operation.Endpoint.V1_PATIENT);
    operation.setOption("by", "identifier");
    requestHeader.getNVPairs().forEach((n, v) -> operation.setOption(n, v.toString()));
    operation.setOption("_lastUpdated", Boolean.toString(lastUpdated != null && !lastUpdated.isEmpty()));
    operation.publishOperationName();
    List<IBaseResource> patients;
    try {
        Patient patient;
        switch(identifier.getSystem()) {
            case TransformerConstants.CODING_BBAPI_BENE_HICN_HASH:
            case TransformerConstants.CODING_BBAPI_BENE_HICN_HASH_OLD:
                patient = queryDatabaseByHicnHash(identifier.getValue(), requestHeader);
                break;
            case TransformerConstants.CODING_BBAPI_BENE_MBI_HASH:
                patient = queryDatabaseByMbiHash(identifier.getValue(), requestHeader);
                break;
            default:
                throw new InvalidRequestException("Unsupported identifier system: " + identifier.getSystem());
        }
        patients = QueryUtils.isInRange(patient.getMeta().getLastUpdated().toInstant(), lastUpdated) ? Collections.singletonList(patient) : Collections.emptyList();
    } catch (NoResultException e) {
        patients = new LinkedList<>();
    }
    OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/Patient?");
    Bundle bundle = TransformerUtils.createBundle(paging, patients, loadedFilterManager.getTransactionTime());
    return bundle;
}
Also used : OffsetLinkBuilder(gov.cms.bfd.server.war.commons.OffsetLinkBuilder) Bundle(org.hl7.fhir.dstu3.model.Bundle) Patient(org.hl7.fhir.dstu3.model.Patient) InvalidRequestException(ca.uhn.fhir.rest.server.exceptions.InvalidRequestException) Operation(gov.cms.bfd.server.war.Operation) NoResultException(javax.persistence.NoResultException) RequestHeaders(gov.cms.bfd.server.war.commons.RequestHeaders) IBaseResource(org.hl7.fhir.instance.model.api.IBaseResource) LinkedList(java.util.LinkedList) Trace(com.newrelic.api.agent.Trace) Search(ca.uhn.fhir.rest.annotation.Search)

Example 5 with MATCH

use of org.hl7.fhir.r4.model.Bundle.SearchEntryMode.MATCH in project beneficiary-fhir-data by CMSgov.

the class R4PatientResourceProvider method searchByIdentifier.

/**
 * Adds support for the FHIR "search" operation for {@link Patient}s, allowing users to search by
 * {@link Patient#getIdentifier()}. Specifically, the following criteria are supported:
 *
 * <p>Searches that don't match one of the above forms are not supported.
 *
 * <p>The {@link Search} annotation indicates that this method supports the search operation.
 * There may be many different methods annotated with this {@link Search} annotation, to support
 * many different search criteria.
 *
 * @param identifier an {@link Identifier} {@link TokenParam} for the {@link
 *     Patient#getIdentifier()} to try and find a matching {@link Patient} for
 * @param startIndex an {@link OptionalParam} for the startIndex (or offset) used to determine
 *     pagination
 * @param lastUpdated an {@link OptionalParam} to filter the results based on the passed date
 *     range
 * @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
 *     to parse out pagination values
 * @return Returns a {@link List} of {@link Patient}s, which may contain multiple matching
 *     resources, or may also be empty.
 */
@Search
@Trace
public Bundle searchByIdentifier(@RequiredParam(name = Patient.SP_IDENTIFIER) @Description(shortDefinition = "The patient identifier to search for") TokenParam identifier, @OptionalParam(name = "startIndex") @Description(shortDefinition = "The offset used for result pagination") String startIndex, @OptionalParam(name = "_lastUpdated") @Description(shortDefinition = "Include resources last updated in the given range") DateRangeParam lastUpdated, RequestDetails requestDetails) {
    if (identifier.getQueryParameterQualifier() != null) {
        throw new InvalidRequestException("Unsupported query parameter qualifier: " + identifier.getQueryParameterQualifier());
    }
    if (!SUPPORTED_HASH_IDENTIFIER_SYSTEMS.contains(identifier.getSystem()))
        throw new InvalidRequestException("Unsupported identifier system: " + identifier.getSystem());
    RequestHeaders requestHeader = RequestHeaders.getHeaderWrapper(requestDetails);
    Operation operation = new Operation(Operation.Endpoint.V2_PATIENT);
    operation.setOption("by", "identifier");
    requestHeader.getNVPairs().forEach((n, v) -> operation.setOption(n, v.toString()));
    operation.setOption("_lastUpdated", Boolean.toString(lastUpdated != null && !lastUpdated.isEmpty()));
    operation.publishOperationName();
    List<IBaseResource> patients;
    try {
        Patient patient;
        switch(identifier.getSystem()) {
            case TransformerConstants.CODING_BBAPI_BENE_MBI_HASH:
                patient = queryDatabaseByMbiHash(identifier.getValue(), requestHeader);
                break;
            case TransformerConstants.CODING_BBAPI_BENE_HICN_HASH:
            case TransformerConstants.CODING_BBAPI_BENE_HICN_HASH_OLD:
                patient = queryDatabaseByHicnHash(identifier.getValue(), requestHeader);
                break;
            default:
                throw new InvalidRequestException("Unsupported identifier system: " + identifier.getSystem());
        }
        patients = QueryUtils.isInRange(patient.getMeta().getLastUpdated().toInstant(), lastUpdated) ? Collections.singletonList(patient) : Collections.emptyList();
    } catch (NoResultException e) {
        patients = new LinkedList<>();
    }
    OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/Patient?");
    return TransformerUtilsV2.createBundle(paging, patients, loadedFilterManager.getTransactionTime());
}
Also used : OffsetLinkBuilder(gov.cms.bfd.server.war.commons.OffsetLinkBuilder) Patient(org.hl7.fhir.r4.model.Patient) InvalidRequestException(ca.uhn.fhir.rest.server.exceptions.InvalidRequestException) Operation(gov.cms.bfd.server.war.Operation) NoResultException(javax.persistence.NoResultException) RequestHeaders(gov.cms.bfd.server.war.commons.RequestHeaders) IBaseResource(org.hl7.fhir.instance.model.api.IBaseResource) LinkedList(java.util.LinkedList) Trace(com.newrelic.api.agent.Trace) Search(ca.uhn.fhir.rest.annotation.Search)

Aggregations

Test (org.junit.jupiter.api.Test)64 ArrayList (java.util.ArrayList)52 CodeableConcept (org.hl7.fhir.r4.model.CodeableConcept)34 Identifier (org.hl7.fhir.r4.model.Identifier)28 FHIRException (org.hl7.fhir.exceptions.FHIRException)27 Bundle (org.hl7.fhir.r4.model.Bundle)23 ValidationMessage (org.hl7.fhir.utilities.validation.ValidationMessage)20 IGenericClient (ca.uhn.fhir.rest.client.api.IGenericClient)19 IOException (java.io.IOException)18 TokenClientParam (ca.uhn.fhir.rest.gclient.TokenClientParam)17 Beneficiary (gov.cms.bfd.model.rif.Beneficiary)16 Coding (org.hl7.fhir.r4.model.Coding)14 List (java.util.List)13 Patient (org.hl7.fhir.dstu3.model.Patient)13 DefinitionException (org.hl7.fhir.exceptions.DefinitionException)13 Patient (org.hl7.fhir.r4.model.Patient)13 IBaseResource (org.hl7.fhir.instance.model.api.IBaseResource)11 Resource (org.hl7.fhir.r4.model.Resource)11 InvalidRequestException (ca.uhn.fhir.rest.server.exceptions.InvalidRequestException)10 HashMap (java.util.HashMap)10