use of gov.cms.bfd.server.war.Operation 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());
}
use of gov.cms.bfd.server.war.Operation in project beneficiary-fhir-data by CMSgov.
the class PatientResourceProvider method read.
/**
* Adds support for the FHIR "read" operation, for {@link Patient}s. The {@link Read} annotation
* indicates that this method supports the read operation.
*
* <p>Read operations take a single parameter annotated with {@link IdParam}, and should return a
* single resource instance.
*
* @param patientId The read operation takes one parameter, which must be of type {@link IdType}
* and must be annotated with the {@link IdParam} annotation.
* @param requestDetails a {@link RequestDetails} containing the details of the request URL, used
* to parse out pagination values
* @return Returns a resource matching the specified {@link IdDt}, or <code>null</code> if none
* exists.
*/
@Read(version = false)
@Trace
public Patient read(@IdParam IdType patientId, RequestDetails requestDetails) {
if (patientId == null)
throw new IllegalArgumentException();
if (patientId.getVersionIdPartAsLong() != null)
throw new IllegalArgumentException();
String beneIdText = patientId.getIdPart();
if (beneIdText == null || beneIdText.trim().isEmpty())
throw new IllegalArgumentException();
RequestHeaders requestHeader = RequestHeaders.getHeaderWrapper(requestDetails);
Operation operation = new Operation(Operation.Endpoint.V1_PATIENT);
operation.setOption("by", "id");
// there is another method with exclude list: requestHeader.getNVPairs(<excludeHeaders>)
requestHeader.getNVPairs().forEach((n, v) -> operation.setOption(n, v.toString()));
operation.publishOperationName();
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Beneficiary> criteria = builder.createQuery(Beneficiary.class);
Root<Beneficiary> root = criteria.from(Beneficiary.class);
root.fetch(Beneficiary_.skippedRifRecords, JoinType.LEFT);
if (requestHeader.isHICNinIncludeIdentifiers())
root.fetch(Beneficiary_.beneficiaryHistories, JoinType.LEFT);
if (requestHeader.isMBIinIncludeIdentifiers())
root.fetch(Beneficiary_.medicareBeneficiaryIdHistories, JoinType.LEFT);
criteria.select(root);
criteria.where(builder.equal(root.get(Beneficiary_.beneficiaryId), beneIdText));
Beneficiary beneficiary = null;
Long beneByIdQueryNanoSeconds = null;
Timer.Context timerBeneQuery = metricRegistry.timer(MetricRegistry.name(getClass().getSimpleName(), "query", "bene_by_id")).time();
try {
beneficiary = entityManager.createQuery(criteria).getSingleResult();
} catch (NoResultException e) {
throw new ResourceNotFoundException(patientId);
} finally {
beneByIdQueryNanoSeconds = timerBeneQuery.stop();
TransformerUtils.recordQueryInMdc(String.format("bene_by_id.include_%s", String.join("_", (List<String>) requestHeader.getValue(HEADER_NAME_INCLUDE_IDENTIFIERS))), beneByIdQueryNanoSeconds, beneficiary == null ? 0 : 1);
}
// 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());
}
// Add bene_id to MDC logs
TransformerUtils.logBeneIdToMdc(Arrays.asList(beneIdText));
Patient patient = BeneficiaryTransformer.transform(metricRegistry, beneficiary, requestHeader);
return patient;
}
use of gov.cms.bfd.server.war.Operation 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;
}
use of gov.cms.bfd.server.war.Operation in project beneficiary-fhir-data by CMSgov.
the class CoverageResourceProvider method searchByBeneficiary.
/**
* Adds support for the FHIR "search" operation for {@link Coverage}s, allowing users to search by
* {@link Coverage#getBeneficiary()}.
*
* <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 beneficiary a {@link ReferenceParam} for the {@link Coverage#getBeneficiary()} to try
* and find matches 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 Coverage}s, which may contain multiple matching
* resources, or may also be empty.
*/
@Search
@Trace
public Bundle searchByBeneficiary(@RequiredParam(name = Coverage.SP_BENEFICIARY) @Description(shortDefinition = "The patient identifier to search for") ReferenceParam beneficiary, @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) {
List<IBaseResource> coverages;
try {
Beneficiary beneficiaryEntity = findBeneficiaryById(beneficiary.getIdPart(), lastUpdated);
if (!beneficiaryEntity.getBeneEnrollmentReferenceYear().isPresent()) {
throw new ResourceNotFoundException("Cannot find coverage for non present enrollment year");
}
coverages = CoverageTransformer.transform(metricRegistry, beneficiaryEntity);
} catch (NoResultException e) {
coverages = new LinkedList<IBaseResource>();
}
OffsetLinkBuilder paging = new OffsetLinkBuilder(requestDetails, "/Coverage?");
Operation operation = new Operation(Operation.Endpoint.V1_COVERAGE);
operation.setOption("by", "beneficiary");
operation.setOption("pageSize", paging.isPagingRequested() ? "" + paging.getPageSize() : "*");
operation.setOption("_lastUpdated", Boolean.toString(lastUpdated != null && !lastUpdated.isEmpty()));
operation.publishOperationName();
// Add bene_id to MDC logs
TransformerUtils.logBeneIdToMdc(Arrays.asList(beneficiary.getIdPart()));
return TransformerUtils.createBundle(paging, coverages, loadedFilterManager.getTransactionTime());
}
use of gov.cms.bfd.server.war.Operation in project beneficiary-fhir-data by CMSgov.
the class R4CoverageResourceProvider method read.
/**
* Adds support for the FHIR "read" operation, for {@link Coverage}s. The {@link Read} annotation
* indicates that this method supports the read operation.
*
* <p>Read operations take a single parameter annotated with {@link IdParam}, and should return a
* single resource instance.
*
* @param coverageId The read operation takes one parameter, which must be of type {@link IdType}
* and must be annotated with the {@link IdParam} annotation.
* @return Returns a resource matching the specified {@link IdDt}, or <code>null</code> if none
* exists.
*/
@Read(version = false)
@Trace
public Coverage read(@IdParam IdType coverageId) {
if (coverageId == null)
throw new IllegalArgumentException();
if (coverageId.getVersionIdPartAsLong() != null)
throw new IllegalArgumentException();
String coverageIdText = coverageId.getIdPart();
if (coverageIdText == null || coverageIdText.trim().isEmpty())
throw new IllegalArgumentException();
Operation operation = new Operation(Operation.Endpoint.V2_COVERAGE);
operation.setOption("by", "id");
operation.publishOperationName();
Matcher coverageIdMatcher = COVERAGE_ID_PATTERN.matcher(coverageIdText);
if (!coverageIdMatcher.matches())
throw new IllegalArgumentException("Unsupported ID pattern: " + coverageIdText);
String coverageIdSegmentText = coverageIdMatcher.group(1);
Optional<MedicareSegment> coverageIdSegment = MedicareSegment.selectByUrlPrefix(coverageIdSegmentText);
if (!coverageIdSegment.isPresent())
throw new ResourceNotFoundException(coverageId);
String coverageIdBeneficiaryIdText = coverageIdMatcher.group(2);
Beneficiary beneficiaryEntity;
try {
beneficiaryEntity = findBeneficiaryById(coverageIdBeneficiaryIdText, null);
if (!beneficiaryEntity.getBeneEnrollmentReferenceYear().isPresent()) {
throw new ResourceNotFoundException("Cannot find coverage for non present enrollment year");
}
} catch (NoResultException e) {
throw new ResourceNotFoundException(new IdDt(Beneficiary.class.getSimpleName(), coverageIdBeneficiaryIdText));
}
Coverage coverage = CoverageTransformerV2.transform(metricRegistry, coverageIdSegment.get(), beneficiaryEntity);
return coverage;
}
Aggregations