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

Add Web Interface for Synthea Patient Generator #1499

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,8 @@ src/main/resources/export/outpatient_rev_cntr_code_map.json

# VS Code plugin files
.history
.trunk

#Python
/venv
/.venv
33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Variables
PYTHON := python3
FLASK_APP := app.py
HTML_DIR := .
FLASK_PORT := 5000
HTTP_SERVER_PORT := 8000
VENV_DIR := .venv

# Targets
.PHONY: all setup serve flask run

all: setup serve flask

# Setup environment
setup:
@echo "Setting up the environment..."
$(PYTHON) -m venv $(VENV_DIR)
. $(VENV_DIR)/bin/activate && pip install --upgrade pip && pip install flask

# Serve the HTML frontend
serve:
@echo "Starting HTTP server..."
cd $(HTML_DIR) && $(PYTHON) -m http.server $(HTTP_SERVER_PORT)

# Run Flask backend
flask:
@echo "Starting Flask backend..."
. $(VENV_DIR)/bin/activate && FLASK_APP=$(FLASK_APP) flask run --host=localhost --port=$(FLASK_PORT)

# Run both serve and flask targets in parallel
run:
@echo "Running both HTTP server and Flask backend..."
$(MAKE) -j2 serve flask
43 changes: 36 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,63 @@ These instructions are intended for those wishing to examine the Synthea source
Synthea<sup>TM</sup> requires Java JDK 11 or newer. We strongly recommend using a Long-Term Support (LTS) release of Java, 11 or 17, as issues may occur with more recent non-LTS versions.

To clone the Synthea<sup>TM</sup> repo, then build and run the test suite:
```

```bash
git clone https://github.com/synthetichealth/synthea.git
cd synthea
./gradlew build check test
```

### Changing the default properties


The default properties file values can be found at `src/main/resources/synthea.properties`.
By default, synthea does not generate CCDA, CPCDA, CSV, or Bulk FHIR (ndjson). You'll need to
adjust this file to activate these features. See the [wiki](https://github.com/synthetichealth/synthea/wiki)
for more details, or use our [guided customizer tool](https://synthetichealth.github.io/spt/#/customizer).

### Generate Synthetic Patients

## Web Interface

### Generate Synthetic Patients
Generating the population one at a time...
Requirements: Python 3.8 or higher

Run the Makefile in a terminal:

```bash
make
```

Open browser to `http://localhost:8000/` or `http://127.0.0.1:8000/`

Enter the desired inputs and hit the generate button at the bottom of the form. An alert will pop up; copy all except "Command to run:" into a terminal.

Example alert:

```text
Localhost:8000 says

Command to run: ./run_synthea "New York" "New York" -p 10 -s 42 -cs 12345 -r 20240830 -g M -a 1-99 -o 5 --exporter.baseDirectory="./output"
```

To end program Ctrl+c in the terminal.

## Command Line

Generating the population one at a time...

```bash
./run_synthea
```

Command-line arguments may be provided to specify a state, city, population size, or seed for randomization.
```

```bash
run_synthea [-s seed] [-p populationSize] [state [city]]
```

Full usage info can be printed by passing the `-h` option.
```

```bash
$ ./run_synthea -h

> Task :run
Expand Down Expand Up @@ -104,7 +132,8 @@ Generate graphical visualizations of Synthea<sup>TM</sup> rules and modules.

### Concepts and Attributes
Generate a list of concepts (used in the records) or attributes (variables on each patient).
```

```bash
./gradlew concepts
./gradlew attributes
```
Expand Down
25 changes: 25 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from flask import Flask, request, jsonify
import subprocess

app = Flask(__name__)

@app.route('/run-synthea', methods=['POST'])
def run_synthea():
data = request.json
command = data.get('command')

try:
# Run the command
result = subprocess.run(command, shell=True, capture_output=True, text=True)

# Return the output
return jsonify({
'output': result.stdout,
'error': result.stderr,
'returncode': result.returncode
})
except Exception as e:
return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
app.run(host='localhost', port=5000)
108 changes: 108 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Synthea Patient Generator</title>
</head>
<body>
<h1>Synthea Patient Generator</h1>

<form id="syntheaForm">
<label for="state">State:</label>
<input type="text" id="state" name="state" placeholder="Enter state" /><br><br>

<label for="city">City:</label>
<input type="text" id="city" name="city" placeholder="Enter city" /><br><br>

<label for="populationSize">Population Size (-p):</label>
<input type="number" id="populationSize" name="populationSize" min="1" placeholder="Enter population size" /><br><br>

<label for="seed">Random Seed (-s):</label>
<input type="number" id="seed" name="seed" placeholder="Enter seed value" /><br><br>

<label for="clinicianSeed">Clinician Seed (-cs):</label>
<input type="number" id="clinicianSeed" name="clinicianSeed" placeholder="Enter clinician seed" /><br><br>

<label for="referenceDate">Reference Date (-r) (Format: YYYYMMDD):</label>
<input type="text" id="referenceDate" name="referenceDate" placeholder="Enter reference date" /><br><br>

<label for="gender">Gender (-g):</label>
<select id="gender" name="gender">
<option value="">Any</option>
<option value="M">Male</option>
<option value="F">Female</option>
</select><br><br>

<label for="ageRange">Age Range (-a) (minAge-maxAge):</label>
<input type="text" id="ageRange" name="ageRange" placeholder="Enter age range (e.g., 30-40)" /><br><br>

<label for="overflowPopulation">Overflow Population (-o):</label>
<input type="number" id="overflowPopulation" name="overflowPopulation" placeholder="Enter overflow population" /><br><br>

<label for="configFilePath">Local Config File Path (-c):</label>
<input type="text" id="configFilePath" name="configFilePath" placeholder="Enter config file path" /><br><br>

<label for="modulesDirPath">Local Modules Directory Path (-d):</label>
<input type="text" id="modulesDirPath" name="modulesDirPath" placeholder="Enter modules directory path" /><br><br>

<label for="initialSnapshotPath">Initial Population Snapshot Path (-i):</label>
<input type="text" id="initialSnapshotPath" name="initialSnapshotPath" placeholder="Enter initial snapshot path" /><br><br>

<label for="updatedSnapshotPath">Updated Population Snapshot Path (-u):</label>
<input type="text" id="updatedSnapshotPath" name="updatedSnapshotPath" placeholder="Enter updated snapshot path" /><br><br>

<label for="updateTimePeriod">Update Time Period (-t) (in days):</label>
<input type="number" id="updateTimePeriod" name="updateTimePeriod" placeholder="Enter update time period in days" /><br><br>

<label for="fixedRecordPath">Fixed Record Path (-f):</label>
<input type="text" id="fixedRecordPath" name="fixedRecordPath" placeholder="Enter fixed record path" /><br><br>

<label for="keepMatchingPatientsPath">Keep Matching Patients Path (-k):</label>
<input type="text" id="keepMatchingPatientsPath" name="keepMatchingPatientsPath" placeholder="Enter keep matching patients path" /><br><br>

<label for="saveDirectory">Save Directory (--exporter.baseDirectory=):</label>
<input type="text" id="saveDirectory" name="saveDirectory" placeholder="Enter directory path" /><br><br>

<label for="configValue">Additional Config:</label>
<input type="text" id="configValue" name="configValue" placeholder="--exporter.fhir.export=true" /><br><br>

<button type="submit">Generate</button>
</form>

<script>
document.getElementById('syntheaForm').addEventListener('submit', function(event) {
event.preventDefault();

// Collect form data
const formData = new FormData(event.target);
let command = './run_synthea';

// Append form data to command
for (let [key, value] of formData.entries()) {
if (value) {
if (key === 'state' || key === 'city' || key === 'configFilePath' || key === 'modulesDirPath' ||
key === 'initialSnapshotPath' || key === 'updatedSnapshotPath' || key === 'keepMatchingPatientsPath') {
value = `"${value}"`;
command += ` -${key.charAt(0)} ${value}`;
} else if (key === 'clinicianSeed') {
// Explicitly handle clinicianSeed to use -cs
command += ` -cs ${value}`;
} else if (key === 'saveDirectory') {
// Append save directory path with --exporter.baseDirectory=
command += ` --exporter.baseDirectory="${value}"`;
} else if (key === 'configValue') {
// Directly append the additional config value without modification
command += ` ${value}`;
} else {
command += ` -${key.charAt(0)} ${value}`;
}
}
}

// Show the command in an alert
alert(`Command to run: ${command}`);
});
</script>
</body>
</html>