use of gov.cms.ab2d.coverage.model.CoveragePagingResult in project ab2d by CMSgov.
the class CoverageServiceImplTest method selectCoverageAllButOneMonth.
@DisplayName("Coverage summary for most months")
@Test
void selectCoverageAllButOneMonth() {
/*
* testing-1 is a member for three months (January to March)
* testing-2 is a member for three months (February to April)
*/
// Bootstrap coverage periods
coverageService.submitSearch(period1Jan.getId(), "testing");
CoverageSearchEvent inProgressJan = startSearchAndPullEvent();
coverageService.submitSearch(period1Feb.getId(), "testing");
CoverageSearchEvent inProgressFeb = startSearchAndPullEvent();
coverageService.submitSearch(period1March.getId(), "testing");
CoverageSearchEvent inProgressMarch = startSearchAndPullEvent();
coverageService.submitSearch(period1April.getId(), "testing");
CoverageSearchEvent inProgressApril = startSearchAndPullEvent();
Identifiers testing1 = createIdentifier(1L);
Identifiers testing2 = createIdentifier(2L);
coverageService.insertCoverage(inProgressJan.getId(), Set.of(testing1));
coverageService.insertCoverage(inProgressFeb.getId(), Set.of(testing1, testing2));
coverageService.insertCoverage(inProgressMarch.getId(), Set.of(testing1, testing2));
coverageService.insertCoverage(inProgressApril.getId(), Set.of(testing2));
CoveragePagingRequest pagingRequest = new CoveragePagingRequest(2, null, contract1, jobStartTime);
CoveragePagingResult pagingResult = coverageService.pageCoverage(pagingRequest);
List<CoverageSummary> coverageSummaries = pagingResult.getCoverageSummaries();
coverageSummaries.sort(Comparator.comparing(summary -> summary.getIdentifiers().getBeneficiaryId()));
assertEquals(2, coverageSummaries.size());
CoverageSummary summary1 = coverageSummaries.get(0);
assertEquals(1, summary1.getIdentifiers().getBeneficiaryId());
assertTrue(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_JAN)));
assertFalse(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(END_DEC)));
assertTrue(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(END_MARCH)));
assertFalse(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_APRIL)));
CoverageSummary summary2 = coverageSummaries.get(1);
assertEquals(2, summary2.getIdentifiers().getBeneficiaryId());
assertTrue(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_FEB)));
assertFalse(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(END_JAN)));
assertTrue(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(END_APRIL)));
assertFalse(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_MAY)));
}
use of gov.cms.ab2d.coverage.model.CoveragePagingResult in project ab2d by CMSgov.
the class CoverageServiceRepository method pageCoverage.
/**
* Page through coverage records in database by beneficiary and aggregate those records into a single object
* for beneficiary.
*
* The paging request will contain a contract, a page size (number of beneficiaries to pull), and a cursor with the
* last patient pulled.
*
* The coverage table contains more than one coverage record per beneficiary. Each coverage record corresponds
* to a beneficiary belonging to a contract for a specific month and year. These records must be aggregated
* for each patient that has ever been a member of the contract to calculate a list of date ranges for their
* membership.
*
* The paging is done by beneficiary, not by enrollment record. A beneficiary may have dozens of enrollment records
* in the database.
*
* For example if
*
* 1. The page size is 10, and
* 2. A contract has been active for two years
* Then each beneficiary could have a record for every month they were active in the contract. Assuming each beneficiary
* was active for every month of the contract that would be (10 beneficiaries) * (24 months per beneficiary) = 240 records
*
* Internally the pageCoverage method will pull more beneficiaries' records than it needs for a complete page and then
* truncate the results down to the expected page size.
*
* The page coverage method will also include the cursor with the result for the next page of beneficiaries.
*
* Step by step what is involved in this method:
*
* 1. Check that contract has enrollment for all necessary months before retrieving a {@link CoveragePagingRequest#getPageSize()}.
* If a contract does not have enrollment for every month except the current month, then it violates a business requirement
* {@link #getExpectedCoveragePeriods(CoveragePagingRequest)}
* 2. Calculate number of coverage records which correspond to page size patients to pull from database. There should be
* at most one coverage record per beneficiary per month the contract has been active
* (pageSize * months * beneficiaries) {@link #getCoverageLimit(int, long)}
* 3. Conduct query to receive coverage records for a {@link #getCoverageLimit(int, long)} of enrollment information
* without processing the results. Each record in the results contains all known identifiers associated
* with a patient and specifies a month and year that the beneficiaries
* are a member of the contract {@link #queryCoverageMembership(CoveragePagingRequest, long)}
* 4. Group the previous queries' results by patient {@link #aggregateEnrollmentByPatient(int, List)}
* 5. For each patient condense enrollment down to a single set of date ranges {@link #summarizeCoverageMembership(ContractForCoverageDTO, Map.Entry)}
* 6. Determine whether another page of results is necessary
* 7. If another page of results is necessary create a {@link CoveragePagingRequest}
* 8. Collect the {@link CoverageSummary} and next {@link CoveragePagingRequest }into a single {@link CoveragePagingResult}
*
* @param page request for paging coverage
* @return the result of paging with a cursor to the next request
*/
public CoveragePagingResult pageCoverage(CoveragePagingRequest page) {
ContractForCoverageDTO contract = page.getContract();
int expectedCoveragePeriods = getExpectedCoveragePeriods(page);
// Make sure all coverage periods are present so that there isn't any missing coverage data
// Do not remove this check because it is a fail safe to guarantee that there isn't something majorly
// wrong with the enrollment data.
// A missing period = one month of enrollment missing for the contract
List<CoveragePeriod> coveragePeriods = coveragePeriodRepo.findAllByContractNumber(contract.getContractNumber());
if (coveragePeriods.size() != expectedCoveragePeriods) {
throw new IllegalArgumentException("at least one coverage period missing from enrollment table for contract " + page.getContract().getContractNumber());
}
// Determine how many records to pull back
long limit = getCoverageLimit(page.getPageSize(), expectedCoveragePeriods);
// Query coverage membership from database and collect it
List<CoverageMembership> enrollment = queryCoverageMembership(page, limit);
// Guarantee ordering of results to the order that the beneficiaries were returned from SQL
Map<Long, List<CoverageMembership>> enrollmentByBeneficiary = aggregateEnrollmentByPatient(expectedCoveragePeriods, enrollment);
// Only summarize page size beneficiaries worth of information and report it
List<CoverageSummary> beneficiarySummaries = enrollmentByBeneficiary.entrySet().stream().limit(page.getPageSize()).map(membershipEntry -> summarizeCoverageMembership(contract, membershipEntry)).collect(toList());
// Get the patient to start from next time
Optional<Map.Entry<Long, List<CoverageMembership>>> nextCursor = enrollmentByBeneficiary.entrySet().stream().skip(page.getPageSize()).findAny();
// Build the next request if there is a next patient
CoveragePagingRequest request = null;
if (nextCursor.isPresent()) {
Map.Entry<Long, List<CoverageMembership>> nextCursorBeneficiary = nextCursor.get();
request = new CoveragePagingRequest(page.getPageSize(), nextCursorBeneficiary.getKey(), contract, page.getJobStartTime());
}
return new CoveragePagingResult(beneficiarySummaries, request);
}
use of gov.cms.ab2d.coverage.model.CoveragePagingResult in project ab2d by CMSgov.
the class ContractProcessorImpl method loadEobRequests.
/**
* Load beneficiaries and create an EOB request for each patient. Patients are loaded a page at a time. The page size is
* configurable. At the end of loading all requests, the number of requests loaded is compared to the expected
* number and the job immediately failed if not equal.
* <p>
* Steps:
* - load a page of beneficiaries from the database
* - create a request per patient and queue each request
* - update the progress tracker with the number of patients added
* - check if the job has been cancelled
* - process any requests to BFD that are complete
*
* @param contractData job requests record and object for storing in motion requests
* @throws InterruptedException if job is shut down during a busy wait for space in the queue
*/
private void loadEobRequests(ContractData contractData) throws InterruptedException {
String jobUuid = contractData.getJob().getJobUuid();
ContractDTO contract = contractData.getContract();
// Handle first page of beneficiaries and then enter loop
CoveragePagingResult current = coverageDriver.pageCoverage(new CoveragePagingRequest(eobJobPatientQueuePageSize, null, mapping.map(contract), contractData.getJob().getCreatedAt()));
loadRequestBatch(contractData, current, searchConfig.getNumberBenesPerBatch());
jobChannelService.sendUpdate(jobUuid, JobMeasure.PATIENT_REQUEST_QUEUED, current.size());
// noinspection WhileLoopReplaceableByForEach
while (current.getNextRequest().isPresent()) {
if (eobClaimRequestsQueue.size(jobUuid) > eobJobPatientQueueMaxSize) {
// Wait for queue to empty out some before adding more
// noinspection BusyWait
Thread.sleep(1000);
continue;
}
// Queue a batch of patients
current = coverageDriver.pageCoverage(current.getNextRequest().get());
loadRequestBatch(contractData, current, searchConfig.getNumberBenesPerBatch());
jobChannelService.sendUpdate(jobUuid, JobMeasure.PATIENT_REQUEST_QUEUED, current.size());
processFinishedRequests(contractData);
}
// Verify that the number of benes requested matches the number expected from the database and fail
// immediately if the two do not match
ProgressTracker progressTracker = jobProgressService.getStatus(jobUuid);
int totalQueued = progressTracker.getPatientRequestQueuedCount();
int totalExpected = progressTracker.getPatientsExpected();
if (totalQueued != totalExpected) {
throw new ContractProcessingException("expected " + totalExpected + " patients from database but retrieved " + totalQueued);
}
}
use of gov.cms.ab2d.coverage.model.CoveragePagingResult in project ab2d by CMSgov.
the class CoverageServiceImplTest method pageCoverageEdgeCase.
@DisplayName("Page coverage when number of beneficiaries enrollment is one more than page size")
@Test
void pageCoverageEdgeCase() {
coverageService.submitSearch(period1Jan.getId(), "testing");
CoverageSearchEvent inProgress = startSearchAndPullEvent();
// Last page will have only one id
int totalBeneficiaries = 500;
int pageSize = 500;
// Add 700 beneficiaries to
Set<Identifiers> identifiers = new LinkedHashSet<>();
for (long idx = 0; idx < totalBeneficiaries; idx++) {
identifiers.add(createIdentifier(idx));
}
CoverageSearchEvent savedTo = coverageService.insertCoverage(inProgress.getId(), identifiers);
assertEquals(inProgress, savedTo);
CoveragePagingRequest pagingRequest = new CoveragePagingRequest(pageSize, null, contract1, jobStartTime);
// Complete third request should return one record with no next cursor
CoveragePagingResult pagingResult = coverageServiceRepo.pageCoverage(pagingRequest);
List<CoverageSummary> coverageSummaries = pagingResult.getCoverageSummaries();
coverageSummaries.sort(Comparator.comparing(summary -> summary.getIdentifiers().getBeneficiaryId()));
assertEquals(pageSize, coverageSummaries.size());
assertTrue(pagingResult.getNextRequest().isEmpty());
}
use of gov.cms.ab2d.coverage.model.CoveragePagingResult in project ab2d by CMSgov.
the class CoverageServiceImplTest method selectCoverageDisjointMonths.
@DisplayName("Coverage summary where individual months are in range")
@Test
void selectCoverageDisjointMonths() {
/*
* testing-1 is a member for two months (January and March)
* testing-2 is a member for two months (February and April)
*/
// Bootstrap coverage periods
coverageService.submitSearch(period1Jan.getId(), "testing");
CoverageSearchEvent inProgressJan = startSearchAndPullEvent();
coverageService.submitSearch(period1Feb.getId(), "testing");
CoverageSearchEvent inProgressFeb = startSearchAndPullEvent();
coverageService.submitSearch(period1March.getId(), "testing");
CoverageSearchEvent inProgressMarch = startSearchAndPullEvent();
coverageService.submitSearch(period1April.getId(), "testing");
CoverageSearchEvent inProgressApril = startSearchAndPullEvent();
coverageService.insertCoverage(inProgressJan.getId(), Set.of(createIdentifier(1L)));
coverageService.insertCoverage(inProgressFeb.getId(), Set.of(createIdentifier(2L)));
coverageService.insertCoverage(inProgressMarch.getId(), Set.of(createIdentifier(1L)));
coverageService.insertCoverage(inProgressApril.getId(), Set.of(createIdentifier(2L)));
CoveragePagingRequest pagingRequest = new CoveragePagingRequest(2, null, contract1, jobStartTime);
CoveragePagingResult pagingResult = coverageService.pageCoverage(pagingRequest);
List<CoverageSummary> coverageSummaries = pagingResult.getCoverageSummaries();
coverageSummaries.sort(Comparator.comparing(summary -> summary.getIdentifiers().getBeneficiaryId()));
assertEquals(2, coverageSummaries.size());
CoverageSummary summary1 = coverageSummaries.get(0);
assertEquals(1, summary1.getIdentifiers().getBeneficiaryId());
assertTrue(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_JAN)));
assertFalse(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(END_DEC)));
assertFalse(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_FEB)));
assertTrue(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_MARCH)));
assertFalse(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(END_FEB)));
assertFalse(summary1.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_APRIL)));
CoverageSummary summary2 = coverageSummaries.get(1);
assertEquals(2, summary2.getIdentifiers().getBeneficiaryId());
assertTrue(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_FEB)));
assertFalse(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(END_JAN)));
assertFalse(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_MARCH)));
assertTrue(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_APRIL)));
assertFalse(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(END_MARCH)));
assertFalse(summary2.getDateRanges().stream().anyMatch(dr -> dr.inRange(START_MAY)));
}
Aggregations