use of org.hl7.fhir.r5.model.Range in project beneficiary-fhir-data by CMSgov.
the class PatientResourceProvider method searchByLogicalId.
/**
* Adds support for the FHIR "search" operation for {@link Patient}s, allowing users to search by
* {@link Patient#getId()}.
*
* <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 logicalId a {@link TokenParam} (with no system, per the spec) for the {@link
* Patient#getId()} 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 searchByLogicalId(@RequiredParam(name = Patient.SP_RES_ID) @Description(shortDefinition = "The patient identifier to search for") TokenParam logicalId, @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 (logicalId.getQueryParameterQualifier() != null)
throw new InvalidRequestException("Unsupported query parameter qualifier: " + logicalId.getQueryParameterQualifier());
if (logicalId.getSystem() != null && !logicalId.getSystem().isEmpty())
throw new InvalidRequestException("Unsupported query parameter system: " + logicalId.getSystem());
if (logicalId.getValueNotNull().isEmpty())
throw new InvalidRequestException("Unsupported query parameter value: " + logicalId.getValue());
List<IBaseResource> patients;
if (loadedFilterManager.isResultSetEmpty(logicalId.getValue(), lastUpdated)) {
patients = Collections.emptyList();
} else {
try {
patients = Optional.of(read(new IdType(logicalId.getValue()), requestDetails)).filter(p -> QueryUtils.isInRange(p.getMeta().getLastUpdated().toInstant(), lastUpdated)).map(p -> Collections.singletonList((IBaseResource) p)).orElse(Collections.emptyList());
} catch (ResourceNotFoundException e) {
patients = Collections.emptyList();
}
}
/*
* Publish the operation name. Note: This is a bit later than we'd normally do this, as we need
* to override the operation name that was published by the possible call to read(...), above.
*/
RequestHeaders requestHeader = RequestHeaders.getHeaderWrapper(requestDetails);
Operation operation = new Operation(Operation.Endpoint.V1_PATIENT);
operation.setOption("by", "id");
// track all api hdrs
requestHeader.getNVPairs().forEach((n, v) -> operation.setOption(n, v.toString()));
operation.setOption("_lastUpdated", Boolean.toString(lastUpdated != null && !lastUpdated.isEmpty()));
operation.publishOperationName();
OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/Patient?");
Bundle bundle = TransformerUtils.createBundle(paging, patients, loadedFilterManager.getTransactionTime());
return bundle;
}
use of org.hl7.fhir.r5.model.Range in project beneficiary-fhir-data by CMSgov.
the class CoverageResourceProviderIT method searchWithLastUpdated.
/**
* Verifies that {@link
* gov.cms.bfd.server.war.stu3.providers.CoverageResourceProvider#searchByBeneficiary} works as
* expected for a search with a lastUpdated value.
*/
@Test
public void searchWithLastUpdated() {
List<Object> loadedRecords = ServerTestUtils.get().loadData(Arrays.asList(StaticRifResourceGroup.SAMPLE_A.getResources()));
IGenericClient fhirClient = ServerTestUtils.get().createFhirClient();
Beneficiary beneficiary = loadedRecords.stream().filter(r -> r instanceof Beneficiary).map(r -> (Beneficiary) r).findFirst().get();
Bundle searchResults = fhirClient.search().forResource(Coverage.class).where(Coverage.BENEFICIARY.hasId(TransformerUtils.buildPatientId(beneficiary))).returnBundle(Bundle.class).execute();
LOGGER.info("Bundle information: database {}, first {}", searchResults.getMeta().getLastUpdated(), searchResults.getEntry().get(0).getResource().getMeta().getLastUpdated());
Date nowDate = new Date();
Date secondsAgoDate = Date.from(Instant.now().minusSeconds(100));
DateRangeParam inBoundsRange = new DateRangeParam().setLowerBoundInclusive(secondsAgoDate).setUpperBoundExclusive(nowDate);
LOGGER.info("Query Date Range {}", inBoundsRange);
Bundle searchInBoundsResults = fhirClient.search().forResource(Coverage.class).where(Coverage.BENEFICIARY.hasId(TransformerUtils.buildPatientId(beneficiary))).lastUpdated(inBoundsRange).returnBundle(Bundle.class).execute();
assertNotNull(searchInBoundsResults);
assertEquals(MedicareSegment.values().length, searchInBoundsResults.getTotal());
Date hourAgoDate = Date.from(Instant.now().minusSeconds(3600));
DateRangeParam outOfBoundsRange = new DateRangeParam(hourAgoDate, secondsAgoDate);
Bundle searchOutOfBoundsResult = fhirClient.search().forResource(Coverage.class).where(Coverage.BENEFICIARY.hasId(TransformerUtils.buildPatientId(beneficiary))).lastUpdated(outOfBoundsRange).returnBundle(Bundle.class).execute();
assertNotNull(searchOutOfBoundsResult);
assertEquals(0, searchOutOfBoundsResult.getTotal());
}
use of org.hl7.fhir.r5.model.Range in project beneficiary-fhir-data by CMSgov.
the class AbstractR4ResourceProvider method findByPatient.
@Search
@Trace
public Bundle findByPatient(@RequiredParam(name = "mbi") @Description(shortDefinition = "The patient identifier to search for") ReferenceParam mbi, @OptionalParam(name = "type") @Description(shortDefinition = "A list of claim types to include") TokenAndListParam types, @OptionalParam(name = "isHashed") @Description(shortDefinition = "A boolean indicating whether or not the MBI is hashed") String hashed, @OptionalParam(name = "excludeSAMHSA") @Description(shortDefinition = "If true, exclude all SAMHSA-related resources") String samhsa, @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) {
if (mbi != null && !StringUtils.isBlank(mbi.getIdPart())) {
String mbiString = mbi.getIdPart();
Bundle bundleResource;
boolean isHashed = !Boolean.FALSE.toString().equalsIgnoreCase(hashed);
boolean excludeSamhsa = Boolean.TRUE.toString().equalsIgnoreCase(samhsa);
if (isHashed) {
TransformerUtilsV2.logMbiHashToMdc(mbiString);
}
if (types != null) {
bundleResource = createBundleFor(parseClaimTypes(types), mbiString, isHashed, excludeSamhsa, lastUpdated, serviceDate);
} else {
bundleResource = createBundleFor(getResourceTypes(), mbiString, isHashed, excludeSamhsa, lastUpdated, serviceDate);
}
return bundleResource;
} else {
throw new IllegalArgumentException("mbi can't be null/blank");
}
}
use of org.hl7.fhir.r5.model.Range in project beneficiary-fhir-data by CMSgov.
the class TransformerUtilsV2 method createInformationAdmPeriodSlice.
/**
* Optionally creates an `admissionperiod` {@link SupportingInformationComponent} slice.
*
* @param periodStart Period start
* @param periodEnd Period end
* @return The created {@link SupportingInformationComponent}
*/
static Optional<SupportingInformationComponent> createInformationAdmPeriodSlice(ExplanationOfBenefit eob, Optional<LocalDate> periodStart, Optional<LocalDate> periodEnd) {
// Create a range if we can
if (periodStart.isPresent() || periodEnd.isPresent()) {
validatePeriodDates(periodStart, periodEnd);
// Create the period
Period period = new Period();
periodStart.ifPresent(start -> period.setStart(convertToDate(start), TemporalPrecisionEnum.DAY));
periodEnd.ifPresent(end -> period.setEnd(convertToDate(end), TemporalPrecisionEnum.DAY));
int maxSequence = eob.getSupportingInfo().stream().mapToInt(i -> i.getSequence()).max().orElse(0);
// Create the SupportingInfo element
return Optional.of(new SupportingInformationComponent().setSequence(maxSequence + 1).setCategory(new CodeableConcept().addCoding(createC4BBSupportingInfoCoding(C4BBSupportingInfoType.ADMISSION_PERIOD))).setTiming(period));
} else {
return Optional.empty();
}
}
use of org.hl7.fhir.r5.model.Range in project beneficiary-fhir-data by CMSgov.
the class R4ExplanationOfBenefitResourceProvider 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 ClaimTypeV2} 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
* R4EobSamhsaMatcher} 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<ClaimTypeV2> claimTypes = parseTypeParam(type);
OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/ExplanationOfBenefit?");
boolean includeTaxNumbers = returnIncludeTaxNumbers(requestDetails);
Operation operation = new Operation(Operation.Endpoint.V2_EOB);
operation.setOption("by", "patient");
operation.setOption("IncludeTaxNumbers", "" + includeTaxNumbers);
operation.setOption("types", (claimTypes.size() == ClaimTypeV2.values().length) ? "*" : claimTypes.stream().sorted(Comparator.comparing(ClaimTypeV2::name)).collect(Collectors.toList()).toString());
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 TransformerUtilsV2.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(ClaimTypeV2.CARRIER)) {
eobs.addAll(transformToEobs(ClaimTypeV2.CARRIER, findClaimTypeByPatient(ClaimTypeV2.CARRIER, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
}
if (claimTypes.contains(ClaimTypeV2.DME)) {
eobs.addAll(transformToEobs(ClaimTypeV2.DME, findClaimTypeByPatient(ClaimTypeV2.DME, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
}
if (claimTypes.contains(ClaimTypeV2.HHA)) {
eobs.addAll(transformToEobs(ClaimTypeV2.HHA, findClaimTypeByPatient(ClaimTypeV2.HHA, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
}
if (claimTypes.contains(ClaimTypeV2.HOSPICE)) {
eobs.addAll(transformToEobs(ClaimTypeV2.HOSPICE, findClaimTypeByPatient(ClaimTypeV2.HOSPICE, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
}
if (claimTypes.contains(ClaimTypeV2.INPATIENT)) {
eobs.addAll(transformToEobs(ClaimTypeV2.INPATIENT, findClaimTypeByPatient(ClaimTypeV2.INPATIENT, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
}
if (claimTypes.contains(ClaimTypeV2.OUTPATIENT)) {
eobs.addAll(transformToEobs(ClaimTypeV2.OUTPATIENT, findClaimTypeByPatient(ClaimTypeV2.OUTPATIENT, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
}
if (claimTypes.contains(ClaimTypeV2.PDE)) {
eobs.addAll(transformToEobs(ClaimTypeV2.PDE, findClaimTypeByPatient(ClaimTypeV2.PDE, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
}
if (claimTypes.contains(ClaimTypeV2.SNF)) {
eobs.addAll(transformToEobs(ClaimTypeV2.SNF, findClaimTypeByPatient(ClaimTypeV2.SNF, beneficiaryId, lastUpdated, serviceDate), Optional.of(includeTaxNumbers)));
}
if (Boolean.parseBoolean(excludeSamhsa)) {
filterSamhsa(eobs);
}
eobs.sort(R4ExplanationOfBenefitResourceProvider::compareByClaimIdThenClaimType);
// Add bene_id to MDC logs
TransformerUtilsV2.logBeneIdToMdc(Arrays.asList(beneficiaryId));
return TransformerUtilsV2.createBundle(paging, eobs, loadedFilterManager.getTransactionTime());
}
Aggregations