use of gov.cms.bfd.model.rif.RecordAction in project beneficiary-fhir-data by CMSgov.
the class RifFilesProcessor method buildHospiceClaimEvent.
/**
* @param fileEvent the {@link RifFileEvent} being processed
* @param csvRecords the {@link CSVRecord}s to be mapped, which must be from a {@link
* RifFileType#HOSPICE} {@link RifFile}
* @return a {@link RifRecordEvent} built from the specified {@link CSVRecord}s
*/
private static RifRecordEvent<HospiceClaim> buildHospiceClaimEvent(RifFileEvent fileEvent, List<CSVRecord> csvRecords) {
if (LOGGER.isTraceEnabled())
LOGGER.trace(csvRecords.toString());
CSVRecord firstCsvRecord = csvRecords.get(0);
RecordAction recordAction = RecordAction.match(firstCsvRecord.get("DML_IND"));
HospiceClaim claim = HospiceClaimParser.parseRif(csvRecords);
return new RifRecordEvent<HospiceClaim>(fileEvent, csvRecords, recordAction, claim.getBeneficiaryId(), claim);
}
use of gov.cms.bfd.model.rif.RecordAction in project beneficiary-fhir-data by CMSgov.
the class RifLoader method process.
/**
* @param recordsBatch the {@link RifRecordEvent}s to process
* @param loadedFileBuilder the builder for the {@LoadedFile} associated with this batch
* @param postgresBatch the {@link PostgreSqlCopyInserter} for the current set of {@link
* RifFilesEvent}s being processed
* @return the {@link RifRecordLoadResult}s that model the results of the operation
*/
private List<RifRecordLoadResult> process(List<RifRecordEvent<?>> recordsBatch, long loadedFileId, PostgreSqlCopyInserter postgresBatch) {
RifFileEvent fileEvent = recordsBatch.get(0).getFileEvent();
MetricRegistry fileEventMetrics = fileEvent.getEventMetrics();
RifFileType rifFileType = fileEvent.getFile().getFileType();
if (rifFileType == RifFileType.BENEFICIARY_HISTORY) {
for (RifRecordEvent<?> rifRecordEvent : recordsBatch) {
hashBeneficiaryHistoryHicn(rifRecordEvent);
hashBeneficiaryHistoryMbi(rifRecordEvent);
}
}
// Only one of each failure/success Timer.Contexts will be applied.
Timer.Context timerBatchSuccess = appState.getMetrics().timer(MetricRegistry.name(getClass().getSimpleName(), "recordBatches")).time();
Timer.Context timerBatchTypeSuccess = fileEventMetrics.timer(MetricRegistry.name(getClass().getSimpleName(), "recordBatches", rifFileType.name())).time();
Timer.Context timerBundleFailure = appState.getMetrics().timer(MetricRegistry.name(getClass().getSimpleName(), "recordBatches", "failed")).time();
EntityManager entityManager = null;
EntityTransaction txn = null;
// TODO: refactor the following to be less of an indented mess
try {
entityManager = appState.getEntityManagerFactory().createEntityManager();
txn = entityManager.getTransaction();
txn.begin();
List<RifRecordLoadResult> loadResults = new ArrayList<>(recordsBatch.size());
/*
* Dev Note: All timestamps of records in the batch and the LoadedBatch must be the same for data consistency.
* The timestamp from the LoadedBatchBuilder is used.
*/
LoadedBatchBuilder loadedBatchBuilder = new LoadedBatchBuilder(loadedFileId, recordsBatch.size());
for (RifRecordEvent<?> rifRecordEvent : recordsBatch) {
RecordAction recordAction = rifRecordEvent.getRecordAction();
RifRecordBase record = rifRecordEvent.getRecord();
LOGGER.trace("Loading '{}' record.", rifFileType);
// Set lastUpdated to the same value for the whole batch
record.setLastUpdated(Optional.of(loadedBatchBuilder.getTimestamp()));
// Associate the beneficiary with this file loaded
loadedBatchBuilder.associateBeneficiary(rifRecordEvent.getBeneficiaryId());
LoadStrategy strategy = selectStrategy(recordAction);
LoadAction loadAction;
if (strategy == LoadStrategy.INSERT_IDEMPOTENT) {
// Check to see if record already exists.
Timer.Context timerIdempotencyQuery = fileEventMetrics.timer(MetricRegistry.name(getClass().getSimpleName(), "idempotencyQueries")).time();
Object recordId = appState.getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(record);
Objects.requireNonNull(recordId);
Object recordInDb = entityManager.find(record.getClass(), recordId);
timerIdempotencyQuery.close();
// Log if we have a non-2022 enrollment year INSERT
if (isBackdatedBene(rifRecordEvent)) {
Beneficiary bene = (Beneficiary) rifRecordEvent.getRecord();
LOGGER.info("Inserted beneficiary with non-2022 enrollment year (beneficiaryId={})", bene.getBeneficiaryId());
}
if (recordInDb == null) {
loadAction = LoadAction.INSERTED;
tweakIfBeneficiary(entityManager, loadedBatchBuilder, rifRecordEvent);
entityManager.persist(record);
// FIXME Object recordInDbAfterUpdate = entityManager.find(record.getClass(), recordId);
} else {
loadAction = LoadAction.DID_NOTHING;
}
} else if (strategy == LoadStrategy.INSERT_UPDATE_NON_IDEMPOTENT) {
if (rifRecordEvent.getRecordAction().equals(RecordAction.INSERT)) {
loadAction = LoadAction.INSERTED;
// Log if we have a non-2022 enrollment year INSERT
if (isBackdatedBene(rifRecordEvent)) {
Beneficiary bene = (Beneficiary) rifRecordEvent.getRecord();
LOGGER.info("Inserted beneficiary with non-2022 enrollment year (beneficiaryId={})", bene.getBeneficiaryId());
}
tweakIfBeneficiary(entityManager, loadedBatchBuilder, rifRecordEvent);
entityManager.persist(record);
} else if (rifRecordEvent.getRecordAction().equals(RecordAction.UPDATE)) {
loadAction = LoadAction.UPDATED;
// Skip this record if the year is not 2022 and its an update.
if (isBackdatedBene(rifRecordEvent)) {
/*
* Serialize the record's CSV data back to actual RIF/CSV, as that's how we'll store
* it in the DB.
*/
StringBuffer rifData = new StringBuffer();
try (CSVPrinter csvPrinter = new CSVPrinter(rifData, RifParsingUtils.CSV_FORMAT)) {
for (CSVRecord csvRow : rifRecordEvent.getRawCsvRecords()) {
csvPrinter.printRecord(csvRow);
}
}
// Save the skipped record to the DB.
SkippedRifRecord skippedRifRecord = new SkippedRifRecord(rifRecordEvent.getFileEvent().getParentFilesEvent().getTimestamp(), SkipReasonCode.DELAYED_BACKDATED_ENROLLMENT_BFD_1566, rifRecordEvent.getFileEvent().getFile().getFileType().name(), rifRecordEvent.getRecordAction(), ((Beneficiary) record).getBeneficiaryId(), rifData.toString());
entityManager.persist(skippedRifRecord);
LOGGER.info("Skipped RIF record, due to '{}'.", skippedRifRecord.getSkipReason());
} else {
tweakIfBeneficiary(entityManager, loadedBatchBuilder, rifRecordEvent);
entityManager.merge(record);
}
} else {
throw new BadCodeMonkeyException(String.format("Unhandled %s: '%s'.", RecordAction.class, rifRecordEvent.getRecordAction()));
}
} else
throw new BadCodeMonkeyException();
LOGGER.trace("Loaded '{}' record.", rifFileType);
fileEventMetrics.meter(MetricRegistry.name(getClass().getSimpleName(), "records", loadAction.name())).mark(1);
loadResults.add(new RifRecordLoadResult(rifRecordEvent, loadAction));
}
LoadedBatch loadedBatch = loadedBatchBuilder.build();
entityManager.persist(loadedBatch);
txn.commit();
// Update the metrics now that things have been pushed.
timerBatchSuccess.stop();
timerBatchTypeSuccess.stop();
return loadResults;
} catch (Throwable t) {
timerBundleFailure.stop();
fileEventMetrics.meter(MetricRegistry.name(getClass().getSimpleName(), "recordBatches", "failed")).mark(1);
LOGGER.warn("Failed to load '{}' record.", rifFileType, t);
throw new RifLoadFailure(recordsBatch, t);
} finally {
/*
* Some errors (e.g. HSQL constraint violations) seem to cause the
* rollback to fail. Extra error handling is needed here, too, to
* ensure that the failing data is captured.
*/
try {
if (txn != null && txn.isActive())
txn.rollback();
} catch (Throwable t) {
timerBundleFailure.stop();
fileEventMetrics.meter(MetricRegistry.name(getClass().getSimpleName(), "recordBatches", "failed")).mark(1);
LOGGER.warn("Failed to load '{}' record.", rifFileType, t);
throw new RifLoadFailure(recordsBatch, t);
}
if (entityManager != null)
entityManager.close();
}
}
use of gov.cms.bfd.model.rif.RecordAction in project beneficiary-fhir-data by CMSgov.
the class RifFilesProcessor method buildHHAClaimEvent.
/**
* @param fileEvent the {@link RifFileEvent} being processed
* @param csvRecords the {@link CSVRecord}s to be mapped, which must be from a {@link
* RifFileType#HHA} {@link RifFile}
* @return a {@link RifRecordEvent} built from the specified {@link CSVRecord}s
*/
private static RifRecordEvent<HHAClaim> buildHHAClaimEvent(RifFileEvent fileEvent, List<CSVRecord> csvRecords) {
if (LOGGER.isTraceEnabled())
LOGGER.trace(csvRecords.toString());
CSVRecord firstCsvRecord = csvRecords.get(0);
RecordAction recordAction = RecordAction.match(firstCsvRecord.get("DML_IND"));
HHAClaim claim = HHAClaimParser.parseRif(csvRecords);
return new RifRecordEvent<HHAClaim>(fileEvent, csvRecords, recordAction, claim.getBeneficiaryId(), claim);
}
use of gov.cms.bfd.model.rif.RecordAction in project beneficiary-fhir-data by CMSgov.
the class RifFilesProcessor method buildDMEClaimEvent.
/**
* @param fileEvent the {@link RifFileEvent} being processed
* @param csvRecords the {@link CSVRecord}s to be mapped, which must be from a {@link
* RifFileType#DME} {@link RifFile}
* @return a {@link RifRecordEvent} built from the specified {@link CSVRecord}s
*/
private static RifRecordEvent<DMEClaim> buildDMEClaimEvent(RifFileEvent fileEvent, List<CSVRecord> csvRecords) {
if (LOGGER.isTraceEnabled())
LOGGER.trace(csvRecords.toString());
CSVRecord firstCsvRecord = csvRecords.get(0);
RecordAction recordAction = RecordAction.match(firstCsvRecord.get("DML_IND"));
DMEClaim claim = DMEClaimParser.parseRif(csvRecords);
return new RifRecordEvent<DMEClaim>(fileEvent, csvRecords, recordAction, claim.getBeneficiaryId(), claim);
}
use of gov.cms.bfd.model.rif.RecordAction in project beneficiary-fhir-data by CMSgov.
the class RifFilesProcessor method buildBeneficiaryEvent.
/**
* @param fileEvent the {@link RifFileEvent} being processed
* @param csvRecords the {@link CSVRecord} to be mapped (in a single-element {@link List}), which
* must be from a {@link RifFileType#BENEFICIARY} {@link RifFile}
* @return a {@link RifRecordEvent} built from the specified {@link CSVRecord}s
*/
private static RifRecordEvent<Beneficiary> buildBeneficiaryEvent(RifFileEvent fileEvent, List<CSVRecord> csvRecords) {
if (csvRecords.size() != 1)
throw new BadCodeMonkeyException();
CSVRecord csvRecord = csvRecords.get(0);
if (LOGGER.isTraceEnabled())
LOGGER.trace(csvRecord.toString());
RecordAction recordAction = RecordAction.match(csvRecord.get("DML_IND"));
Beneficiary beneficiaryRow = BeneficiaryParser.parseRif(csvRecords);
// Swap the unhashed HICN into the correct field.
beneficiaryRow.setHicnUnhashed(Optional.ofNullable(beneficiaryRow.getHicn()));
beneficiaryRow.setHicn(null);
return new RifRecordEvent<Beneficiary>(fileEvent, csvRecords, recordAction, beneficiaryRow.getBeneficiaryId(), beneficiaryRow);
}
Aggregations