Skip to content

Commit

Permalink
Update BFD HHA exporter to support PPS codes and improve HCPCS to rev…
Browse files Browse the repository at this point in the history
…enue center mapping
  • Loading branch information
hadleynet committed Jul 3, 2023
1 parent 15c8434 commit 7313dd9
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 12 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ src/main/resources/export/snf_pdpm_code_map.json
src/main/resources/export/snf_pps_code_map.json
src/main/resources/export/snf_rev_cntr_code_map.json
src/main/resources/export/hha_rev_cntr_code_map.json
src/main/resources/export/hha_pps_case_mix_codes.csv
src/main/resources/export/hha_pps_pdgm_codes.csv
src/main/resources/export/hospice_rev_cntr_code_map.json
src/main/resources/export/inpatient_rev_cntr_code_map.json
src/main/resources/export/outpatient_rev_cntr_code_map.json
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public class BB2RIFExporter {
final CodeMapper inpatientRevCntrMapper;
final CodeMapper outpatientRevCntrMapper;
final Map<String, RandomCollection<String>> externalCodes;
final RandomCollection<String> hhaCaseMixCodes;
final RandomCollection<String> hhaPDGMCodes;
final CMSStateCodeMapper locationMapper;
final BeneficiaryExporter beneExp;
final InpatientExporter inpatientExp;
Expand Down Expand Up @@ -115,6 +117,8 @@ private BB2RIFExporter() {
outpatientRevCntrMapper = new CodeMapper("export/outpatient_rev_cntr_code_map.json");
locationMapper = new CMSStateCodeMapper();
externalCodes = loadExternalCodes();
hhaCaseMixCodes = loadPPSCodes("export/hha_pps_case_mix_codes.csv");
hhaPDGMCodes = loadPPSCodes("export/hha_pps_pdgm_codes.csv");
try {
staticFieldConfig = new StaticFieldConfig();
rifWriters = prepareOutputFiles();
Expand Down Expand Up @@ -166,6 +170,30 @@ private static Map<String, RandomCollection<String>> loadExternalCodes() {
return data;
}

private static RandomCollection<String> loadPPSCodes(String resourcePath) {
RandomCollection<String> codes = new RandomCollection<>();
try {
String fileData = Utilities.readResourceAndStripBOM(resourcePath);
List<LinkedHashMap<String, String>> csv = SimpleCSV.parse(fileData);
for (LinkedHashMap<String, String> row : csv) {
String code = row.get("code");
long count = Long.parseLong(row.get("count"));
codes.add(count, code);
}
} catch (Exception e) {
if (Config.getAsBoolean("exporter.bfd.require_code_maps", true)) {
throw new MissingResourceException(
"Unable to read PPS code file",
"BB2RIFExporter", resourcePath);
} else {
// For testing, the external codes are not present.
System.out.printf("BB2RIFExporter is running without '%s'\n", resourcePath);
}
return null;
}
return codes;
}

<E extends Enum<E>> void setExternalCode(Person person,
Map<E,String> fieldValues, E diagnosisCodeKey,
E externalCodeKey, E externalVersionKey,
Expand Down
3 changes: 0 additions & 3 deletions src/main/java/org/mitre/synthea/export/rif/CodeMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
Expand All @@ -20,7 +18,6 @@
import org.mitre.synthea.helpers.Config;
import org.mitre.synthea.helpers.RandomCollection;
import org.mitre.synthea.helpers.RandomNumberGenerator;
import org.mitre.synthea.helpers.SimpleCSV;
import org.mitre.synthea.helpers.Utilities;
import org.mitre.synthea.world.concepts.HealthRecord.Code;

Expand Down
43 changes: 34 additions & 9 deletions src/main/java/org/mitre/synthea/export/rif/HHAExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
*/
public class HHAExporter extends RIFExporter {

private static final long HHA_PPS_CASE_MIX_START = parseSimpleDate("20080101");
private static final long HHA_PPS_PDGM_START = parseSimpleDate("20200101");

/**
* Construct an exporter for HHA claims.
* @param exporter the exporter instance that will be used to access code mappers
Expand Down Expand Up @@ -87,23 +90,45 @@ long export(Person person, long startTime, long stopTime) throws IOException {

final String HHA_TOTAL_CHARGE_REV_CNTR = "0001"; // Total charge
final String HHA_GENERAL_REV_CNTR = "0270"; // General medical/surgical supplies
final String HHA_PPS_REV_CNTR = "0023"; // Prospective payment system
final String HHA_MEDICATION_CODE = "T1502"; // Administration of medication

// Select a PPS code for this service period (if a PPS program was in place at the time).
// Only one PPS code per service period since the code is based on patient characteristics
// and care need.
// TODO: rather than pick a weighted random PPS code, pick a code based on current patient
// characteristics.
String ppsCode = null;
if (servicePeriod.getStart() > HHA_PPS_PDGM_START) {
ppsCode = exporter.hhaPDGMCodes.next(person);
} else if (servicePeriod.getStart() > HHA_PPS_CASE_MIX_START) {
ppsCode = exporter.hhaCaseMixCodes.next(person);
}
System.out.printf("Assigned PPS code: %s\n", ppsCode == null ? "NULL" : ppsCode);

ConsolidatedClaimLines consolidatedClaimLines = new ConsolidatedClaimLines();
for (HealthRecord.Encounter encounter : servicePeriod.getEncounters()) {
for (Claim.ClaimEntry lineItem : encounter.claim.items) {
String hcpcsCode = null;
if (lineItem.entry instanceof HealthRecord.Procedure) {
for (HealthRecord.Code code : lineItem.entry.codes) {
if (exporter.hcpcsCodeMapper.canMap(code)) {
hcpcsCode = exporter.hcpcsCodeMapper.map(code, person, true);
if (exporter.hhaRevCntrMapper.canMap(code)) {
revCenter = exporter.hhaRevCntrMapper.map(code, person);
// 10% of line items use a PPS code, use higher number here to account for
// every claim having a total charge line
if (ppsCode != null && person.rand() < 0.15) {
hcpcsCode = ppsCode;
revCenter = HHA_PPS_REV_CNTR;
} else {
for (HealthRecord.Code code : lineItem.entry.codes) {
if (exporter.hcpcsCodeMapper.canMap(code)) {
hcpcsCode = exporter.hcpcsCodeMapper.map(code, person, true);
if (exporter.hhaRevCntrMapper.canMap(hcpcsCode)) {
revCenter = exporter.hhaRevCntrMapper.map(hcpcsCode, person);
}
break; // take the first mappable code for each procedure
}
break; // take the first mappable code for each procedure
}
}
if (hcpcsCode == null) {
revCenter = HHA_GENERAL_REV_CNTR;
if (hcpcsCode == null) {
revCenter = HHA_GENERAL_REV_CNTR;
}
}
consolidatedClaimLines.addClaimLine(hcpcsCode, revCenter, lineItem, encounter);
} else if (lineItem.entry instanceof HealthRecord.Medication) {
Expand Down

0 comments on commit 7313dd9

Please sign in to comment.