Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dental Modules #1357

Merged
merged 33 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8f8d8bf
Fix transient error with empty instructions.
jawalonoski Aug 25, 2023
1d155fc
Broaden the search for applicable reasons.
jawalonoski Aug 25, 2023
9093743
Initial dental module.
jawalonoski Aug 25, 2023
78d7d8d
Make CodeMapper public to allow use in other exporters. Git ignore cu…
hadleynet Aug 29, 2023
1fabe0a
Make a few RIF things public so the X12 exporter can re-use them
hadleynet Aug 29, 2023
4aa8e2b
Don't run custom exporters during unit testing. Fix checkstyle warnings.
hadleynet Aug 29, 2023
d8b3fd5
More updates to support re-use of RIF exporter features by X12 exporter
hadleynet Aug 30, 2023
f829bb8
Add dental cleaning prior to kidney transplant.
jawalonoski Aug 28, 2023
0c83e01
Add dental referral to cardiac valve replacement.
jawalonoski Sep 5, 2023
781b47f
Ignore dental mapping file.
jawalonoski Sep 5, 2023
5e97763
Add testing module to ensure HHA visits for RIF unit tests.
jawalonoski Sep 5, 2023
4dd3bf3
Fix bug and add dental services prior to valvuloplasty.
jawalonoski Sep 11, 2023
4535cf4
Fix operation side effects when no operation.
jawalonoski Sep 11, 2023
124b929
Drive dental care related to bone transplants.
jawalonoski Sep 13, 2023
4d984cc
Fix comparison method violation.
jawalonoski Sep 15, 2023
e6dd367
Add broken and dislocated jaws to injuries, with dental referrals.
jawalonoski Sep 15, 2023
a8fc14a
Add dentures module with surgical comorbidities.
jawalonoski Sep 18, 2023
525f68a
Update MBI class to allow non-fake MBIs.
hadleynet Sep 28, 2023
2cf4ccc
Allow valid HICN in addition to synthetic. Change visibility of HICN …
hadleynet Oct 10, 2023
7b71a53
Change final status of NPI for X12 exporter.
jawalonoski Nov 16, 2023
e251230
Add new dependency needed for X12 exporter
hadleynet Feb 6, 2024
de32971
Read modules from multiple paths, plus dental costs.
jawalonoski Mar 15, 2024
2c97526
Graphviz and module override fixes.
jawalonoski Mar 20, 2024
df8ed77
Add dependency needed by X12 exporter
hadleynet Apr 3, 2024
370eb9b
Fix merge conflicts.
jawalonoski Apr 9, 2024
6787a03
Reattached migrated method.
jawalonoski Apr 9, 2024
d10d07e
Fix a bug where setting exporter.years_of_history via the command lin…
hadleynet May 17, 2024
bd4c558
Fix checkstyle warnings
hadleynet May 17, 2024
025e0f0
Fix merge conflict.
jawalonoski May 17, 2024
e2cee7a
Fix unit test loading modules from jar.
jawalonoski May 20, 2024
950416f
Changes associated with IDR analysis and updates.
jawalonoski Jul 23, 2024
6668935
Change comment to match property name.
jawalonoski Jul 26, 2024
af33688
Fix merge conflicts.
jawalonoski Aug 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/nbproject/**
/build/**
bin/**
lib/custom/*.jar

/.gradle/
/bin/
Expand All @@ -20,6 +21,7 @@ bin/**
src/main/resources/version.txt

# don't commit the code maps
src/main/resources/export/cdt_code_map.json
src/main/resources/export/condition_code_map.json
src/main/resources/export/medication_code_map.json
src/main/resources/export/drg_code_map.json
Expand Down
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ dependencies {
implementation 'info.cqframework:elm-jackson:2.1.0'

implementation 'com.squareup.okhttp3:okhttp:4.10.0'
implementation 'org.reflections:reflections:0.10.2'
implementation 'net.lingala.zip4j:zip4j:2.11.5'

// Java 9 no longer includes these APIs by default
implementation 'javax.xml.bind:jaxb-api:2.4.0-b180830.0359'
Expand Down
32 changes: 24 additions & 8 deletions src/main/java/Graphviz.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
Expand All @@ -8,6 +9,7 @@
import guru.nidi.graphviz.attribute.Label;
import guru.nidi.graphviz.attribute.Records;
import guru.nidi.graphviz.attribute.Style;
import guru.nidi.graphviz.engine.Engine;
import guru.nidi.graphviz.engine.Format;
import guru.nidi.graphviz.engine.GraphvizException;
import guru.nidi.graphviz.model.Factory;
Expand All @@ -24,12 +26,10 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;

import org.mitre.synthea.engine.Module;
import org.mitre.synthea.export.Exporter;
Expand All @@ -47,18 +47,20 @@ public class Graphviz {
public static void main(String[] args) throws URISyntaxException, IOException {
File folder = Exporter.getOutputFolder("graphviz", null);

Path inputPath = null;
List<Path> inputPaths = new ArrayList<Path>();
if (args != null && args.length > 0) {
File file = new File(args[0]);
inputPath = file.toPath();
inputPaths.add(file.toPath());
} else {
inputPath = Module.getModulesPath();
inputPaths = Module.getModulePaths();
}

System.out.println("Rendering graphs to `" + folder.getAbsolutePath() + "`...");

long start = System.currentTimeMillis();
generateJsonModuleGraphs(inputPath, folder);
for (Path path : inputPaths) {
generateJsonModuleGraphs(path, folder);
}

System.out.println("Completed in " + (System.currentTimeMillis() - start) + " ms.");
}
Expand Down Expand Up @@ -276,8 +278,16 @@ private static void generateJsonModuleGraph(JsonObject module, File outputFolder

File outputFile = outputFolder.toPath().resolve(relativePath + ".png").toFile();
outputFile.mkdirs();
guru.nidi.graphviz.engine.Graphviz.fromGraph(g)

try {
guru.nidi.graphviz.engine.Graphviz.fromGraph(g).engine(Engine.DOT)
.render(Format.PNG).toFile(outputFile);
} catch (guru.nidi.graphviz.engine.GraphvizException gve) {
if (gve.getMessage().contains("Command took too long to execute")) {
guru.nidi.graphviz.engine.Graphviz.fromGraph(g).engine(Engine.FDP)
.render(Format.PNG).toFile(outputFile);
}
}
}

private static String getStateDescription(JsonObject state) {
Expand Down Expand Up @@ -319,7 +329,13 @@ private static String getStateDescription(JsonObject state) {
}
break;
case "SetAttribute":
String v = state.has("value") ? state.get("value").getAsString() : null;
String v = "null";
if (state.has("value")) {
JsonElement e = state.get("value");
if (e != null && e != JsonNull.INSTANCE) {
v = e.getAsString();
}
}
details.append("Set ").append(state.get("attribute").getAsString()).append(" = ").append(v);
break;
case "Symptom":
Expand Down
25 changes: 22 additions & 3 deletions src/main/java/org/mitre/synthea/engine/Distribution.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
*/
public class Distribution implements Serializable {
public enum Kind {
EXACT, GAUSSIAN, UNIFORM, EXPONENTIAL
EXACT, GAUSSIAN, UNIFORM, EXPONENTIAL, TRIANGULAR
}

public Kind kind;
Expand Down Expand Up @@ -50,8 +50,23 @@ public double generate(Person person) {
break;
case EXPONENTIAL:
double average = this.parameters.get("mean");
double lambda = (1.0d / average);
value = 1.0d + Math.log(1.0d - person.rand()) / (-1.0d * lambda);
double lambda = (-1.0d / average);
value = 1.0d + Math.log(1.0d - person.rand()) / lambda;
break;
case TRIANGULAR:
/* Pick a single value based on a triangular distribution. See:
* https://en.wikipedia.org/wiki/Triangular_distribution
*/
double min = this.parameters.get("min");
double mode = this.parameters.get("mode");
double max = this.parameters.get("max");
double f = (mode - min) / (max - min);
double rand = person.rand();
if (rand < f) {
value = min + Math.sqrt(rand * (max - min) * (mode - min));
} else {
value = max - Math.sqrt((1 - rand) * (max - min) * (max - mode));
}
break;
default:
value = -1;
Expand Down Expand Up @@ -81,6 +96,10 @@ public boolean validate() {
&& this.parameters.containsKey("standardDeviation");
case EXPONENTIAL:
return this.parameters.containsKey("mean");
case TRIANGULAR:
return this.parameters.containsKey("min")
&& this.parameters.containsKey("mode")
&& this.parameters.containsKey("max");
default:
return false;
}
Expand Down
23 changes: 16 additions & 7 deletions src/main/java/org/mitre/synthea/engine/Module.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -79,8 +81,10 @@ private static Map<String, ModuleSupplier> loadModules() {
Properties moduleOverrides = getModuleOverrides();

try {
Path modulesPath = getModulesPath();
submoduleCount = walkModuleTree(modulesPath, retVal, moduleOverrides, false);
List<Path> modulePaths = getModulePaths();
for (Path path : modulePaths) {
submoduleCount += walkModuleTree(path, retVal, moduleOverrides, false);
}
} catch (Exception e) {
e.printStackTrace();
}
Expand All @@ -93,16 +97,21 @@ private static Map<String, ModuleSupplier> loadModules() {
}

/**
* The path to the modules directory, ensuring the right file system support is loaded if
* Get the paths to the modules directories, ensuring the right file system support is loaded if
* we are running from a jar file.
* @return the path
* @throws URISyntaxException if something goes wrong
* @throws IOException if something goes wrong
*/
public static Path getModulesPath() throws URISyntaxException, IOException {
URI modulesURI = Module.class.getClassLoader().getResource("modules").toURI();
Utilities.enableReadingURIFromJar(modulesURI);
return Paths.get(modulesURI);
public static List<Path> getModulePaths() throws URISyntaxException, IOException {
List<Path> paths = new ArrayList<Path>();
Enumeration<URL> moduleURLs = Module.class.getClassLoader().getResources("modules");
while (moduleURLs.hasMoreElements()) {
URI uri = moduleURLs.nextElement().toURI();
Utilities.enableReadingURIFromJar(uri);
paths.add(Paths.get(uri));
}
return paths;
}

private static Properties getModuleOverrides() {
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/org/mitre/synthea/engine/State.java
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,12 @@ public boolean process(Person person, long time) {
break;
}
}
} else if (module.getStateNames().contains(reason)) {
State state = module.getState(reason);
if (state != null && state instanceof ConditionOnset) {
ConditionOnset condition = (ConditionOnset) state;
encounter.reason = condition.codes.get(0);
}
}
}

Expand Down Expand Up @@ -1419,6 +1425,12 @@ private void applyFeatures(Person person, Medication medication) {
medication.reasons.addAll(entry.codes);
}
}
} else if (module.getStateNames().contains(reason)) {
State state = module.getState(reason);
if (state != null && state instanceof ConditionOnset) {
ConditionOnset condition = (ConditionOnset) state;
medication.reasons.addAll(condition.codes);
}
}
}
medication.prescriptionDetails = prescription;
Expand Down Expand Up @@ -1513,6 +1525,12 @@ public boolean process(Person person, long time) {
careplan.mergeReasonList(entry.codes);
}
}
} else if (module.getStateNames().contains(reason)) {
State state = module.getState(reason);
if (state != null && state instanceof ConditionOnset) {
ConditionOnset condition = (ConditionOnset) state;
careplan.reasons.addAll(condition.codes);
}
}
}
if (shouldAssignAttribute()) {
Expand Down Expand Up @@ -1621,6 +1639,12 @@ public void processOnce(Person person, long time) {
procedure.reasons.addAll(entry.codes);
}
}
} else if (module.getStateNames().contains(reason)) {
State state = module.getState(reason);
if (state != null && state instanceof ConditionOnset) {
ConditionOnset condition = (ConditionOnset) state;
procedure.reasons.addAll(condition.codes);
}
}
}
if ((duration != null || distribution != null) && this.stop == null) {
Expand Down Expand Up @@ -2286,6 +2310,12 @@ public boolean process(Person person, long time) {
reason = entry.codes.get(0);
}
}
} else if (module.getStateNames().contains(conditionOnset)) {
State state = module.getState(conditionOnset);
if (state != null && state instanceof ConditionOnset) {
ConditionOnset condition = (ConditionOnset) state;
reason = condition.codes.get(0);
}
}
} else if (referencedByAttribute != null) {
Entry entry = (Entry) person.attributes.get(referencedByAttribute);
Expand Down
10 changes: 6 additions & 4 deletions src/main/java/org/mitre/synthea/export/CSVExporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -308,14 +308,14 @@ private void writeCSVHeaders() throws IOException {
"START,STOP,PATIENT,PAYER,ENCOUNTER,CODE,DESCRIPTION,BASE_COST,PAYER_COVERAGE,DISPENSES,"
+ "TOTALCOST,REASONCODE,REASONDESCRIPTION");
medications.write(NEWLINE);
conditions.write("START,STOP,PATIENT,ENCOUNTER,CODE,DESCRIPTION");
conditions.write("START,STOP,PATIENT,ENCOUNTER,SYSTEM,CODE,DESCRIPTION");
conditions.write(NEWLINE);
careplans.write(
"Id,START,STOP,PATIENT,ENCOUNTER,CODE,DESCRIPTION,REASONCODE,REASONDESCRIPTION");
careplans.write(NEWLINE);
observations.write("DATE,PATIENT,ENCOUNTER,CATEGORY,CODE,DESCRIPTION,VALUE,UNITS,TYPE");
observations.write(NEWLINE);
procedures.write("START,STOP,PATIENT,ENCOUNTER,CODE,DESCRIPTION,BASE_COST,"
procedures.write("START,STOP,PATIENT,ENCOUNTER,SYSTEM,CODE,DESCRIPTION,BASE_COST,"
+ "REASONCODE,REASONDESCRIPTION");
procedures.write(NEWLINE);
immunizations.write("DATE,PATIENT,ENCOUNTER,CODE,DESCRIPTION,BASE_COST");
Expand Down Expand Up @@ -752,7 +752,7 @@ private String encounter(String personID,
* @throws IOException if any IO error occurs
*/
private void condition(String personID, String encounterID, Entry condition) throws IOException {
// START,STOP,PATIENT,ENCOUNTER,CODE,DESCRIPTION
// START,STOP,PATIENT,ENCOUNTER,SYSTEM,CODE,DESCRIPTION
StringBuilder s = new StringBuilder();

s.append(dateFromTimestamp(condition.start)).append(',');
Expand All @@ -765,6 +765,7 @@ private void condition(String personID, String encounterID, Entry condition) thr

Code coding = condition.codes.get(0);

s.append(coding.system).append(',');
s.append(coding.code).append(',');
s.append(clean(coding.display));

Expand Down Expand Up @@ -903,7 +904,7 @@ private void observation(String personID,
*/
private void procedure(String personID, String encounterID,
Procedure procedure) throws IOException {
// START,STOP,PATIENT,ENCOUNTER,CODE,DESCRIPTION,COST,REASONCODE,REASONDESCRIPTION
// START,STOP,PATIENT,ENCOUNTER,SYSTEM,CODE,DESCRIPTION,COST,REASONCODE,REASONDESCRIPTION
StringBuilder s = new StringBuilder();

s.append(iso8601Timestamp(procedure.start)).append(',');
Expand All @@ -915,6 +916,7 @@ private void procedure(String personID, String encounterID,
s.append(encounterID).append(',');
// CODE
Code coding = procedure.codes.get(0);
s.append(coding.system).append(',');
s.append(coding.code).append(',');
// DESCRIPTION
s.append(clean(coding.display)).append(',');
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/org/mitre/synthea/export/ExportHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ public static long nextFriday(long time) {
private static final String RXNORM_URI = "http://www.nlm.nih.gov/research/umls/rxnorm";
private static final String CVX_URI = "http://hl7.org/fhir/sid/cvx";
private static final String DICOM_DCM_URI = "http://dicom.nema.org/medical/dicom/current/output/chtml/part16/sect_CID_29.html";
private static final String CDT_URI = "http://www.ada.org/cdt";
private static final String ICD9_URI = "http://hl7.org/fhir/sid/icd-9-cm";
private static final String ICD10_URI = "http://hl7.org/fhir/sid/icd-10";
private static final String ICD10_CM_URI = "http://hl7.org/fhir/sid/icd-10-cm";

/**
* Translate the system name (e.g. SNOMED-CT) into the official
Expand All @@ -231,6 +235,14 @@ public static String getSystemURI(String system) {
system = CVX_URI;
} else if (system.equals("DICOM-DCM")) {
system = DICOM_DCM_URI;
} else if (system.equals("CDT")) {
system = CDT_URI;
} else if (system.equals("ICD9")) {
system = ICD9_URI;
} else if (system.equals("ICD10")) {
system = ICD10_URI;
} else if (system.equals("ICD10-CM")) {
system = ICD10_CM_URI;
}
return system;
}
Expand All @@ -253,6 +265,14 @@ public static String getSystemFromURI(String uri) {
return "CVX";
case DICOM_DCM_URI:
return "DICOM-DCM";
case CDT_URI:
return "CDT";
case ICD9_URI:
return "ICD9";
case ICD10_URI:
return "ICD10";
case ICD10_CM_URI:
return "ICD10-CM";
default:
return "Unknown";
}
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/org/mitre/synthea/export/Exporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,8 @@ private static boolean exportRecord(Person person, String fileTag, long stopTime
writeNewFile(outFilePath, consolidatedNotes);
}

if (patientExporters != null && !patientExporters.isEmpty()) {
if (Config.getAsBoolean("exporter.custom.export", true)
&& patientExporters != null && !patientExporters.isEmpty()) {
for (PatientExporter patientExporter : patientExporters) {
patientExporter.export(person, stopTime, options);
}
Expand Down Expand Up @@ -618,7 +619,8 @@ public static void runPostCompletionExports(Generator generator, ExporterRuntime
parser.encodeResourceToString(parameters));
}

if (postCompletionExporters != null && !postCompletionExporters.isEmpty()) {
if (Config.getAsBoolean("exporter.custom.export", true)
&& postCompletionExporters != null && !postCompletionExporters.isEmpty()) {
for (PostCompletionExporter postCompletionExporter : postCompletionExporters) {
postCompletionExporter.export(generator, options);
}
Expand Down
Loading
Loading