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);

Check warning on line 177 in src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java#L176-L177

Added lines #L176 - L177 were not covered by tests
for (LinkedHashMap<String, String> row : csv) {
String code = row.get("code");
long count = Long.parseLong(row.get("count"));
codes.add(count, code);
}

Check warning on line 182 in src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java#L179-L182

Added lines #L179 - L182 were not covered by tests
} catch (Exception e) {
if (Config.getAsBoolean("exporter.bfd.require_code_maps", true)) {
throw new MissingResourceException(

Check warning on line 185 in src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java#L185

Added line #L185 was not covered by tests
"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;

Check warning on line 194 in src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/BB2RIFExporter.java#L193-L194

Added lines #L193 - L194 were not covered by tests
}

<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

Check warning on line 93 in src/main/java/org/mitre/synthea/export/rif/HHAExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/HHAExporter.java#L93

Added line #L93 was not covered by tests
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;

Check warning on line 101 in src/main/java/org/mitre/synthea/export/rif/HHAExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/HHAExporter.java#L101

Added line #L101 was not covered by tests
if (servicePeriod.getStart() > HHA_PPS_PDGM_START) {
ppsCode = exporter.hhaPDGMCodes.next(person);

Check warning on line 103 in src/main/java/org/mitre/synthea/export/rif/HHAExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/HHAExporter.java#L103

Added line #L103 was not covered by tests
} else if (servicePeriod.getStart() > HHA_PPS_CASE_MIX_START) {
ppsCode = exporter.hhaCaseMixCodes.next(person);

Check warning on line 105 in src/main/java/org/mitre/synthea/export/rif/HHAExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/HHAExporter.java#L105

Added line #L105 was not covered by tests
}
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;

Check warning on line 118 in src/main/java/org/mitre/synthea/export/rif/HHAExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/HHAExporter.java#L117-L118

Added lines #L117 - L118 were not covered by tests
} else {
for (HealthRecord.Code code : lineItem.entry.codes) {
if (exporter.hcpcsCodeMapper.canMap(code)) {
hcpcsCode = exporter.hcpcsCodeMapper.map(code, person, true);

Check warning on line 122 in src/main/java/org/mitre/synthea/export/rif/HHAExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/HHAExporter.java#L122

Added line #L122 was not covered by tests
if (exporter.hhaRevCntrMapper.canMap(hcpcsCode)) {
revCenter = exporter.hhaRevCntrMapper.map(hcpcsCode, person);

Check warning on line 124 in src/main/java/org/mitre/synthea/export/rif/HHAExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/HHAExporter.java#L124

Added line #L124 was not covered by tests
}
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;

Check warning on line 130 in src/main/java/org/mitre/synthea/export/rif/HHAExporter.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/mitre/synthea/export/rif/HHAExporter.java#L130

Added line #L130 was not covered by tests
}
}
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.