Search in sources :

Example 11 with Use

use of org.hl7.fhir.r4.model.ExplanationOfBenefit.Use in project beneficiary-fhir-data by CMSgov.

the class ExplanationOfBenefitResourceProvider method findByPatient.

/**
 * Adds support for the FHIR "search" operation for {@link ExplanationOfBenefit}s, allowing users
 * to search by {@link ExplanationOfBenefit#getPatient()}.
 *
 * <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 patient a {@link ReferenceParam} for the {@link ExplanationOfBenefit#getPatient()} to
 *     try and find matches for {@link ExplanationOfBenefit}s
 * @param type a list of {@link ClaimType} to include in the result. Defaults to all types.
 * @param startIndex an {@link OptionalParam} for the startIndex (or offset) used to determine
 *     pagination
 * @param excludeSamhsa an {@link OptionalParam} that, if <code>"true"</code>, will use {@link
 *     Stu3EobSamhsaMatcher} to filter out all SAMHSA-related claims from the results
 * @param lastUpdated an {@link OptionalParam} that specifies a date range for the lastUpdated
 *     field.
 * @param serviceDate an {@link OptionalParam} that specifies a date range for {@link
 *     ExplanationOfBenefit}s that completed
 * @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
 *     to parse out pagination values
 * @return Returns a {@link Bundle} of {@link ExplanationOfBenefit}s, which may contain multiple
 *     matching resources, or may also be empty.
 */
@Search
@Trace
public Bundle findByPatient(@RequiredParam(name = ExplanationOfBenefit.SP_PATIENT) @Description(shortDefinition = "The patient identifier to search for") ReferenceParam patient, @OptionalParam(name = "type") @Description(shortDefinition = "A list of claim types to include") TokenAndListParam type, @OptionalParam(name = "startIndex") @Description(shortDefinition = "The offset used for result pagination") String startIndex, @OptionalParam(name = "excludeSAMHSA") @Description(shortDefinition = "If true, exclude all SAMHSA-related resources") String excludeSamhsa, @OptionalParam(name = "_lastUpdated") @Description(shortDefinition = "Include resources last updated in the given range") DateRangeParam lastUpdated, @OptionalParam(name = "service-date") @Description(shortDefinition = "Include resources that completed in the given range") DateRangeParam serviceDate, RequestDetails requestDetails) {
    /*
     * startIndex is an optional parameter here because it must be declared in the
     * event it is passed in. However, it is not being used here because it is also
     * contained within requestDetails and parsed out along with other parameters
     * later.
     */
    String beneficiaryId = patient.getIdPart();
    Set<ClaimType> claimTypes = parseTypeParam(type);
    Boolean includeTaxNumbers = returnIncludeTaxNumbers(requestDetails);
    OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/ExplanationOfBenefit?");
    Operation operation = new Operation(Operation.Endpoint.V1_EOB);
    operation.setOption("by", "patient");
    operation.setOption("types", (claimTypes.size() == ClaimType.values().length) ? "*" : claimTypes.stream().sorted(Comparator.comparing(ClaimType::name)).collect(Collectors.toList()).toString());
    operation.setOption("IncludeTaxNumbers", "" + includeTaxNumbers);
    operation.setOption("pageSize", paging.isPagingRequested() ? "" + paging.getPageSize() : "*");
    operation.setOption("_lastUpdated", Boolean.toString(lastUpdated != null && !lastUpdated.isEmpty()));
    operation.setOption("service-date", Boolean.toString(serviceDate != null && !serviceDate.isEmpty()));
    operation.publishOperationName();
    List<IBaseResource> eobs = new ArrayList<IBaseResource>();
    // Optimize when the lastUpdated parameter is specified and result set is empty
    if (loadedFilterManager.isResultSetEmpty(beneficiaryId, lastUpdated)) {
        return TransformerUtils.createBundle(paging, eobs, loadedFilterManager.getTransactionTime());
    }
    /*
     * The way our JPA/SQL schema is setup, we have to run a separate search for
     * each claim type, then combine the results. It's not super efficient, but it's
     * also not so inefficient that it's worth fixing.
     */
    if (claimTypes.contains(ClaimType.CARRIER))
        eobs.addAll(transformToEobs(ClaimType.CARRIER, findClaimTypeByPatient(ClaimType.CARRIER, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.DME))
        eobs.addAll(transformToEobs(ClaimType.DME, findClaimTypeByPatient(ClaimType.DME, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.HHA))
        eobs.addAll(transformToEobs(ClaimType.HHA, findClaimTypeByPatient(ClaimType.HHA, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.HOSPICE))
        eobs.addAll(transformToEobs(ClaimType.HOSPICE, findClaimTypeByPatient(ClaimType.HOSPICE, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.INPATIENT))
        eobs.addAll(transformToEobs(ClaimType.INPATIENT, findClaimTypeByPatient(ClaimType.INPATIENT, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.OUTPATIENT))
        eobs.addAll(transformToEobs(ClaimType.OUTPATIENT, findClaimTypeByPatient(ClaimType.OUTPATIENT, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.PDE))
        eobs.addAll(transformToEobs(ClaimType.PDE, findClaimTypeByPatient(ClaimType.PDE, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (claimTypes.contains(ClaimType.SNF))
        eobs.addAll(transformToEobs(ClaimType.SNF, findClaimTypeByPatient(ClaimType.SNF, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
    if (Boolean.parseBoolean(excludeSamhsa))
        filterSamhsa(eobs);
    eobs.sort(ExplanationOfBenefitResourceProvider::compareByClaimIdThenClaimType);
    // Add bene_id to MDC logs
    TransformerUtils.logBeneIdToMdc(Arrays.asList(beneficiaryId));
    return TransformerUtils.createBundle(paging, eobs, loadedFilterManager.getTransactionTime());
}
Also used : OffsetLinkBuilder(gov.cms.bfd.server.war.commons.OffsetLinkBuilder) ArrayList(java.util.ArrayList) Operation(gov.cms.bfd.server.war.Operation) IBaseResource(org.hl7.fhir.instance.model.api.IBaseResource) Trace(com.newrelic.api.agent.Trace) Search(ca.uhn.fhir.rest.annotation.Search)

Example 12 with Use

use of org.hl7.fhir.r4.model.ExplanationOfBenefit.Use in project beneficiary-fhir-data by CMSgov.

the class ExplanationOfBenefitResourceProvider method findClaimTypeByPatient.

/**
 * @param claimType the {@link ClaimType} to find
 * @param patientId the {@link Beneficiary#getBeneficiaryId()} to filter by
 * @param lastUpdated the update time to filter by
 * @return the matching claim/event entities
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
@Trace
private <T> List<T> findClaimTypeByPatient(ClaimType claimType, String patientId, DateRangeParam lastUpdated, DateRangeParam serviceDate) {
    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery criteria = builder.createQuery((Class) claimType.getEntityClass());
    Root root = criteria.from(claimType.getEntityClass());
    claimType.getEntityLazyAttributes().stream().forEach(a -> root.fetch(a));
    criteria.select(root).distinct(true);
    // Search for a beneficiary's records. Use lastUpdated if present
    Predicate wherePredicate = builder.equal(root.get(claimType.getEntityBeneficiaryIdAttribute()), patientId);
    if (lastUpdated != null && !lastUpdated.isEmpty()) {
        Predicate predicate = QueryUtils.createLastUpdatedPredicate(builder, root, lastUpdated);
        wherePredicate = builder.and(wherePredicate, predicate);
    }
    criteria.where(wherePredicate);
    List<T> claimEntities = null;
    Long eobsByBeneIdQueryNanoSeconds = null;
    Timer.Context timerEobQuery = metricRegistry.timer(MetricRegistry.name(metricRegistry.getClass().getSimpleName(), "query", "eobs_by_bene_id", claimType.name().toLowerCase())).time();
    try {
        claimEntities = entityManager.createQuery(criteria).getResultList();
    } finally {
        eobsByBeneIdQueryNanoSeconds = timerEobQuery.stop();
        TransformerUtils.recordQueryInMdc(String.format("eobs_by_bene_id.%s", claimType.name().toLowerCase()), eobsByBeneIdQueryNanoSeconds, claimEntities == null ? 0 : claimEntities.size());
    }
    if (claimEntities != null && serviceDate != null && !serviceDate.isEmpty()) {
        final Instant lowerBound = serviceDate.getLowerBoundAsInstant() != null ? serviceDate.getLowerBoundAsInstant().toInstant() : null;
        final Instant upperBound = serviceDate.getUpperBoundAsInstant() != null ? serviceDate.getUpperBoundAsInstant().toInstant() : null;
        final java.util.function.Predicate<LocalDate> lowerBoundCheck = lowerBound == null ? (date) -> true : (date) -> compareLocalDate(date, lowerBound.atZone(ZoneId.systemDefault()).toLocalDate(), serviceDate.getLowerBound().getPrefix());
        final java.util.function.Predicate<LocalDate> upperBoundCheck = upperBound == null ? (date) -> true : (date) -> compareLocalDate(date, upperBound.atZone(ZoneId.systemDefault()).toLocalDate(), serviceDate.getUpperBound().getPrefix());
        return claimEntities.stream().filter(entity -> lowerBoundCheck.test(claimType.getServiceEndAttributeFunction().apply(entity)) && upperBoundCheck.test(claimType.getServiceEndAttributeFunction().apply(entity))).collect(Collectors.toList());
    }
    return claimEntities;
}
Also used : CriteriaBuilder(javax.persistence.criteria.CriteriaBuilder) IdParam(ca.uhn.fhir.rest.annotation.IdParam) Arrays(java.util.Arrays) Bundle(org.hl7.fhir.dstu3.model.Bundle) ListIterator(java.util.ListIterator) Description(ca.uhn.fhir.model.api.annotation.Description) IdType(org.hl7.fhir.dstu3.model.IdType) LoggerFactory(org.slf4j.LoggerFactory) NoResultException(javax.persistence.NoResultException) Matcher(java.util.regex.Matcher) DateRangeParam(ca.uhn.fhir.rest.param.DateRangeParam) Predicate(javax.persistence.criteria.Predicate) IResourceProvider(ca.uhn.fhir.rest.server.IResourceProvider) IBaseResource(org.hl7.fhir.instance.model.api.IBaseResource) CriteriaBuilder(javax.persistence.criteria.CriteriaBuilder) LoadedFilterManager(gov.cms.bfd.server.war.commons.LoadedFilterManager) CriteriaQuery(javax.persistence.criteria.CriteriaQuery) IdDt(ca.uhn.fhir.model.primitive.IdDt) ExplanationOfBenefit(org.hl7.fhir.dstu3.model.ExplanationOfBenefit) InvalidRequestException(ca.uhn.fhir.rest.server.exceptions.InvalidRequestException) ParamPrefixEnum(ca.uhn.fhir.rest.param.ParamPrefixEnum) Set(java.util.Set) Instant(java.time.Instant) Collectors(java.util.stream.Collectors) ZoneId(java.time.ZoneId) Beneficiary(gov.cms.bfd.model.rif.Beneficiary) List(java.util.List) TransformerConstants(gov.cms.bfd.server.war.commons.TransformerConstants) LocalDate(java.time.LocalDate) TokenOrListParam(ca.uhn.fhir.rest.param.TokenOrListParam) Timer(com.codahale.metrics.Timer) Optional(java.util.Optional) OptionalParam(ca.uhn.fhir.rest.annotation.OptionalParam) Pattern(java.util.regex.Pattern) TokenAndListParam(ca.uhn.fhir.rest.param.TokenAndListParam) QueryUtils(gov.cms.bfd.server.war.commons.QueryUtils) Trace(com.newrelic.api.agent.Trace) ArrayList(java.util.ArrayList) RequiredParam(ca.uhn.fhir.rest.annotation.RequiredParam) HashSet(java.util.HashSet) Inject(javax.inject.Inject) RequestDetails(ca.uhn.fhir.rest.api.server.RequestDetails) Search(ca.uhn.fhir.rest.annotation.Search) ReferenceParam(ca.uhn.fhir.rest.param.ReferenceParam) ResourceNotFoundException(ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException) Nullable(javax.annotation.Nullable) Root(javax.persistence.criteria.Root) Read(ca.uhn.fhir.rest.annotation.Read) OffsetLinkBuilder(gov.cms.bfd.server.war.commons.OffsetLinkBuilder) MetricRegistry(com.codahale.metrics.MetricRegistry) Logger(org.slf4j.Logger) Operation(gov.cms.bfd.server.war.Operation) EntityManager(javax.persistence.EntityManager) PersistenceContext(javax.persistence.PersistenceContext) TokenParam(ca.uhn.fhir.rest.param.TokenParam) Component(org.springframework.stereotype.Component) Comparator(java.util.Comparator) Root(javax.persistence.criteria.Root) Instant(java.time.Instant) CriteriaQuery(javax.persistence.criteria.CriteriaQuery) LocalDate(java.time.LocalDate) Predicate(javax.persistence.criteria.Predicate) Timer(com.codahale.metrics.Timer) Trace(com.newrelic.api.agent.Trace)

Example 13 with Use

use of org.hl7.fhir.r4.model.ExplanationOfBenefit.Use in project beneficiary-fhir-data by CMSgov.

the class HospiceClaimTransformer method transform.

/**
 * @param metricRegistry the {@link MetricRegistry} to use
 * @param claim the CCW {@link HospiceClaim} to transform
 * @param includeTaxNumbers whether or not to include tax numbers in the result (see {@link
 *     ExplanationOfBenefitResourceProvider#HEADER_NAME_INCLUDE_TAX_NUMBERS}, defaults to <code>
 *     false</code>)
 * @return a FHIR {@link ExplanationOfBenefit} resource that represents the specified {@link
 *     HospiceClaim}
 */
@Trace
static ExplanationOfBenefit transform(MetricRegistry metricRegistry, Object claim, Optional<Boolean> includeTaxNumbers) {
    Timer.Context timer = metricRegistry.timer(MetricRegistry.name(HospiceClaimTransformer.class.getSimpleName(), "transform")).time();
    if (!(claim instanceof HospiceClaim))
        throw new BadCodeMonkeyException();
    ExplanationOfBenefit eob = transformClaim((HospiceClaim) claim);
    timer.stop();
    return eob;
}
Also used : Timer(com.codahale.metrics.Timer) BadCodeMonkeyException(gov.cms.bfd.sharedutils.exceptions.BadCodeMonkeyException) HospiceClaim(gov.cms.bfd.model.rif.HospiceClaim) ExplanationOfBenefit(org.hl7.fhir.dstu3.model.ExplanationOfBenefit) Trace(com.newrelic.api.agent.Trace)

Example 14 with Use

use of org.hl7.fhir.r4.model.ExplanationOfBenefit.Use in project beneficiary-fhir-data by CMSgov.

the class InpatientClaimTransformer method transform.

/**
 * @param metricRegistry the {@link MetricRegistry} to use
 * @param claim the CCW {@link InpatientClaim} to transform
 * @param includeTaxNumbers whether or not to include tax numbers in the result (see {@link
 *     ExplanationOfBenefitResourceProvider#HEADER_NAME_INCLUDE_TAX_NUMBERS}, defaults to <code>
 *     false</code>)
 * @return a FHIR {@link ExplanationOfBenefit} resource that represents the specified {@link
 *     InpatientClaim}
 */
@Trace
static ExplanationOfBenefit transform(MetricRegistry metricRegistry, Object claim, Optional<Boolean> includeTaxNumbers) {
    Timer.Context timer = metricRegistry.timer(MetricRegistry.name(InpatientClaimTransformer.class.getSimpleName(), "transform")).time();
    if (!(claim instanceof InpatientClaim))
        throw new BadCodeMonkeyException();
    ExplanationOfBenefit eob = transformClaim((InpatientClaim) claim);
    timer.stop();
    return eob;
}
Also used : InpatientClaim(gov.cms.bfd.model.rif.InpatientClaim) Timer(com.codahale.metrics.Timer) BadCodeMonkeyException(gov.cms.bfd.sharedutils.exceptions.BadCodeMonkeyException) ExplanationOfBenefit(org.hl7.fhir.dstu3.model.ExplanationOfBenefit) Trace(com.newrelic.api.agent.Trace)

Example 15 with Use

use of org.hl7.fhir.r4.model.ExplanationOfBenefit.Use in project beneficiary-fhir-data by CMSgov.

the class TransformerUtilsV2 method createExtensionQuantity.

/**
 * @param ccwVariable the {@link CcwCodebookInterface} being mapped
 * @param quantityValue the value to use for {@link Coding#getCode()} for the resulting {@link
 *     Coding}
 * @return the output {@link Extension}, with {@link Extension#getValue()} set to represent the
 *     specified input values
 */
static Extension createExtensionQuantity(CcwCodebookInterface ccwVariable, Optional<? extends Number> quantityValue) {
    if (!quantityValue.isPresent()) {
        throw new IllegalArgumentException();
    }
    Quantity quantity;
    if (quantityValue.get() instanceof BigDecimal) {
        quantity = new Quantity().setValue((BigDecimal) quantityValue.get());
    } else {
        throw new BadCodeMonkeyException();
    }
    String extensionUrl = CCWUtils.calculateVariableReferenceUrl(ccwVariable);
    Extension extension = new Extension(extensionUrl, quantity);
    return extension;
}
Also used : Extension(org.hl7.fhir.r4.model.Extension) BadCodeMonkeyException(gov.cms.bfd.sharedutils.exceptions.BadCodeMonkeyException) SimpleQuantity(org.hl7.fhir.r4.model.SimpleQuantity) Quantity(org.hl7.fhir.r4.model.Quantity) BigDecimal(java.math.BigDecimal)

Aggregations

ArrayList (java.util.ArrayList)82 BundleEntryComponent (org.hl7.fhir.r4.model.Bundle.BundleEntryComponent)43 List (java.util.List)41 FHIRException (org.hl7.fhir.exceptions.FHIRException)40 Date (java.util.Date)39 IOException (java.io.IOException)38 CodeableConcept (org.hl7.fhir.r4.model.CodeableConcept)35 Test (org.junit.jupiter.api.Test)35 Resource (org.hl7.fhir.r4.model.Resource)34 Collectors (java.util.stream.Collectors)29 Coding (org.hl7.fhir.r4.model.Coding)27 Reference (org.hl7.fhir.r4.model.Reference)27 Timer (com.codahale.metrics.Timer)26 HashMap (java.util.HashMap)25 Bundle (org.hl7.fhir.r4.model.Bundle)25 Reference (org.hl7.fhir.dstu3.model.Reference)24 XhtmlNode (org.hl7.fhir.utilities.xhtml.XhtmlNode)23 CodeableConcept (org.hl7.fhir.dstu3.model.CodeableConcept)21 DefinitionException (org.hl7.fhir.exceptions.DefinitionException)21 JsonObject (com.google.gson.JsonObject)20