-
Notifications
You must be signed in to change notification settings - Fork 2
Template to transform US Birth data to OMOP #43
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
base: main
Are you sure you want to change the base?
Changes from all commits
e21d796
3b3b999
23dbc68
75acc5d
480836c
d65d07c
9894b8b
b3fc042
a92ae53
a6d6393
e790531
e7e76f3
00f72a6
1707a3c
1c9ef1a
33cf33f
59e89c7
f19ce5d
6f1684b
c3f6275
c5220de
e500592
b726abe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,219 @@ | ||
| # US Birth Data to OMOP ETL Template | ||
|
|
||
| This runbook explains how to import and run | ||
| [`ETL_US_BirthData_to_OMOP.json`](./ETL_US_BirthData_to_OMOP.json). | ||
|
|
||
| ## Data source | ||
|
|
||
| The input is the 2022 US birth public-use data from the CDC/NCHS | ||
| [Vital Statistics Online Data Portal](https://www.cdc.gov/nchs/data_access/vitalstatsonline.htm). | ||
|
|
||
| The template currently retains `nat2022` in several variable names and staging-file names. These are implementation names; when loading 2022 data, verify that the fixed-width positions used by the parser match the 2022 User Guide before running the complete dataset. | ||
|
|
||
| This guide uses: | ||
|
|
||
| ```text | ||
| Unziped_2022_US_birth_data.txt | ||
| ``` | ||
|
|
||
| The mapping input is: | ||
|
|
||
| ```text | ||
| Merged_US_Birth_Data.csv # necessary to be uploaded locally | ||
| ``` | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - A running D2E environment. | ||
| - An OMOP CDM 5.4 dataset created before running the ETL. | ||
| - The dataset ID copied from the Datasets page. | ||
| - The destination database code and schema name. | ||
| - The fixed-width birth-data file available on the host. | ||
| - The `Merged_US_Birth_Data.csv` mapping file. | ||
|
|
||
| ## Step 1: Create the dataset and set `dataset_id` | ||
|
|
||
| Create the destination OMOP CDM 5.4 dataset before running the flow. Creating it through the Dataset page, copy the dataset ID which will be used in later step 4. | ||
|
|
||
|
|
||
| ## Step 2: Mount the source-data directory | ||
|
|
||
| Mount the host directory containing the fixed-width source file into the flow container at `/app/data_load`: | ||
|
|
||
| - Open a terminal in the d2e directory. | ||
| - Run the following commands to define directories: | ||
|
|
||
| ```sh | ||
| export BIRTH_DATA_DIR="/absolute/path/to/birth_data" | ||
| yq -i '.services.alp-dataflow-gen-worker.volumes = ((.services.alp-dataflow-gen-worker.volumes // []) + [strenv(BIRTH_DATA_DIR) + ":/app/data_load"] | unique)' docker-compose.yml | ||
| ``` | ||
|
|
||
| Restart D2E to apply the updated container mount: | ||
|
|
||
| ```sh | ||
| d2e stop | ||
| d2e start | ||
| ``` | ||
|
|
||
| After the restart, verify that the worker can see the mounted files: | ||
|
|
||
| ```sh | ||
| docker exec alp-dataflow-gen-worker ls -l /app/data_load | ||
| ``` | ||
|
|
||
| ## Step 3: Import and save the template | ||
|
|
||
| 1. Import `ETL_US_BirthData_to_OMOP.json` in the ETL page within admin portal. | ||
| 2. Save the imported flow immediately. | ||
|
|
||
| ## Step 4: Configure the source filename and destination | ||
| 1. Open variables setting drawer | ||
| <img src="image-1.png" alt="alt text" width="100" /> | ||
|
|
||
| 2. Then set individual variables as showen below: | ||
| <img src="image.png" alt="alt text" width="400"/> | ||
|
|
||
| - Set dataset_id from step 1 | ||
| - Set "nat2022_filename" to the filename inside `/app/data_load`: | ||
| - Set the correct destination database and schema in the flow configuration: | ||
| ```text | ||
| dataset_id=<dataset_id> | ||
| nat2022_filename=<filename of Unziped 2022 US birth data.txt> | ||
| destination_database_code=<database code> | ||
| destination_schema_name=<OMOP schema name> | ||
| ``` | ||
|
|
||
| 3. Save the configuration of flow immediately. | ||
|
|
||
| ## Step 5: Upload local mapping table via CSV node | ||
|
|
||
| - In CSV node, upload the mapping table using exact name of `Merged_US_Birth_Data.csv`. | ||
| - Ensure that the CSV node name is `csv_node_0` | ||
|
|
||
| *The Python transform expects its mapping DataFrame from `csv_node_0`. A differently named or disconnected node will cause the flow to fail. | ||
|
|
||
| ## Step 6: Run the flow | ||
|
|
||
| The ETL writes the following tables: | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please test the ETL with full set of data also.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you help to test on 2022 full dataset? |
||
|
|
||
| - `person` | ||
| - `visit_occurrence` | ||
| - `observation` | ||
| - `measurement` | ||
| - `condition_occurrence` | ||
| - `procedure_occurrence` | ||
| - `payer_plan_period` | ||
| - `provider` | ||
|
|
||
| ## Step 7: Verify the cache tables | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The user using the template don't need to do this.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep verification step for user to make sure the tables are written. |
||
|
|
||
| ### Get the cache catalog ID | ||
|
|
||
| Run below command to query catalog_id using the dataset UUID configured in the ETL: | ||
|
|
||
| ```sh | ||
| export DATASET_ID = <dataset_id> | ||
| SCHEMA_NAME = <schema_name> | ||
| CACHE_ID="$( | ||
| docker exec d2e-minerva-postgres-1 \ | ||
| psql -U postgres -d alp -tA \ | ||
| -c "SELECT cache_id FROM portal.dataset WHERE id = '${DATASET_ID}'::uuid;" | ||
| )" | ||
| ``` | ||
|
|
||
| If the deployment uses a different `PROJECT_NAME`, replace `d2e-minerva-postgres-1` and `d2e-trex` with the actual Minerva PostgreSQL container name shown by: | ||
|
|
||
| ```sh | ||
| docker ps --filter 'name=minerva-postgres' --format '{{.Names}}' | ||
| docker ps --filter 'name=trex' --format '{{.Names}}' | ||
| ``` | ||
|
|
||
| ### 1. First list the destination tables | ||
| Run below command to get the list of all tables in target schema | ||
|
|
||
| ```sql | ||
| TREX_SQL_PASSWORD="$( | ||
| docker exec d2e-trex printenv TREX__SQL__PASSWORD | ||
| )" | ||
|
|
||
| docker exec -i \ | ||
| -e PGPASSWORD="$TREX_SQL_PASSWORD" \ | ||
| d2e-minerva-postgres-1 \ | ||
| psql \ | ||
| -h d2e-trex \ | ||
| -p 5433 \ | ||
| -U postgres \ | ||
| -d "$CACHE_ID" \ | ||
| -v table_catalog="$CACHE_ID" \ | ||
| -v table_schema="$SCHEMA_NAME" \ | ||
| -tA <<'SQL' | ||
| SELECT | ||
| table_catalog, | ||
| table_schema, | ||
| table_name | ||
| FROM information_schema.tables | ||
| WHERE table_catalog = :'table_catalog' | ||
| AND table_schema = :'table_schema' | ||
| AND table_type = 'BASE TABLE' | ||
| ORDER BY table_name; | ||
| SQL | ||
| ``` | ||
|
|
||
| ### 2. List the row counts of each table in target schema | ||
| - Generate exact `COUNT(*)` queries for all tables in the destination schema | ||
|
|
||
| ```sh | ||
| COUNT_QUERY="$( | ||
| docker exec -i \ | ||
| -e PGPASSWORD="$TREX_SQL_PASSWORD" \ | ||
| d2e-minerva-postgres-1 \ | ||
| psql \ | ||
| -h d2e-trex \ | ||
| -p 5433 \ | ||
| -U postgres \ | ||
| -d "$CACHE_ID" \ | ||
| -v table_catalog="$CACHE_ID" \ | ||
| -v table_schema="$SCHEMA_NAME" \ | ||
| -tA <<'SQL' | ||
| SELECT string_agg( | ||
| 'SELECT ''' | ||
| || replace(table_name, '''', '''''') | ||
| || ''' AS table_name, COUNT(*) AS row_count FROM "' | ||
| || replace(table_catalog, '"', '""') | ||
| || '"."' | ||
| || replace(table_schema, '"', '""') | ||
| || '"."' | ||
| || replace(table_name, '"', '""') | ||
| || '"', | ||
| ' UNION ALL ' | ||
| ) || ' ORDER BY row_count DESC' | ||
| FROM information_schema.tables | ||
| WHERE table_catalog = :'table_catalog' | ||
| AND table_schema = :'table_schema' | ||
| AND table_type = 'BASE TABLE'; | ||
| SQL | ||
| )" | ||
| ``` | ||
|
|
||
| - Display the row counts of each table in target schema | ||
|
|
||
| ```sh | ||
| docker exec \ | ||
| -e PGPASSWORD="$TREX_SQL_PASSWORD" \ | ||
| d2e-minerva-postgres-1 \ | ||
| psql \ | ||
| -h d2e-trex \ | ||
| -p 5433 \ | ||
| -U postgres \ | ||
| -d "$CACHE_ID" \ | ||
| -c "$COUNT_QUERY" | ||
| ``` | ||
| A sample dataset with 10 rows will result in 30 rows in person table: | ||
| <img src="image-2.png" alt="alt text" width="300"/> | ||
|
|
||
| ## Common failures | ||
|
|
||
| - **Import libraries disappear:** Save the flow immediately after importing the JSON. | ||
| - **Source file is not found:** Confirm the host-directory mount and `nat2022_filename` value. | ||
| - **Wrong cache receives data:** Confirm that `dataset_id` is the ID returned for the intended Web API dataset. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. User cannot confirm this from the UI. |
||
| - **Wrong destination:** Confirm both `destination_database_code` and `destination_schema_name`. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What file is this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mapping table