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

SNOMED Hydrating, Filtering, Special MR Generation #1648

Draft
wants to merge 19 commits into
base: develop
Choose a base branch
from

Conversation

jonahkaye
Copy link
Member

@jonahkaye jonahkaye commented Feb 23, 2024

Refs: #1442

Description

  • In this PR, we introduce comprehensive deduplication and filtering script as a preprocessing step for generating medical record summaries.
  • Conditions: We take every single condition a patient has, and to start, remove all duplicate and non-disorder clinical codes. Then, we use SNOMED code lookups to create a hierarchical tree mapping the relationships between all the remaining codes. For every code that is a child of another parent code, we remove that child code. So for example, if we have Diabetes Mellitus and Type II diabetes mellitus without complication (disorder), because the latter is a descendent of Type II diabetes mellitus which is a descendent of Diabetes Mellitus, we remove Type II diabetes mellitus without complication (disorder) from the list of disorders we present
  • Procedures: We remove all procedures without CPT codes in the surgical range (10004 - 69990)
  • Medications: We remove all Medications that are not linked to a Condition that made it through the condition filtering phase

Note

  • This PR introduces yet another bundle-to-html function. A cx has a specific format they want the MR in, and easier (especially since this is just a script) to put that into its own file as opposed to refactoring with backwards compatability

Testing

  • Local
    • local flow for now

Release Plan

  • Merge this
  • no prod workflow touched

@jonahkaye jonahkaye marked this pull request as draft March 21, 2024 15:12
@jonahkaye jonahkaye self-assigned this Mar 26, 2024
Copy link
Member Author

@jonahkaye jonahkaye left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conditions:

  • deduplication
  • non disorder conditions removal
  • non Root code in heirarchy removal

Procedure

  • deduplication
  • non CPT surgery range removal

Medications:

  • removal of all Meds not linked to some value in the condition heirarchy

@@ -5,7 +5,7 @@
{{>Resources/Medication.hbs medication=medAdm.substanceAdministration.consumable.manufacturedProduct.manufacturedMaterial ID=(generateUUID (toJsonString medAdm.substanceAdministration.consumable.manufacturedProduct.manufacturedMaterial))}},
{{>References/MedicationAdministration/medicationReference.hbs ID=(generateUUID (toJsonString medAdm.substanceAdministration)) REF=(concat 'Medication/' (generateUUID (toJsonString medAdm.substanceAdministration.consumable.manufacturedProduct.manufacturedMaterial)))}},

{{#with (evaluate 'Utils/GenerateOrganizationId.hbs' obj=edAdm.substanceAdministration.performer.assignedEntity.representedOrganization) as |orgId|}}
{{#with (evaluate 'Utils/GenerateOrganizationId.hbs' obj=medAdm.substanceAdministration.performer.assignedEntity.representedOrganization) as |orgId|}}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bug

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch, this is stuff that our tooling should catch for us. Isn't there a way we can parse/compile those? At least to check if the variable names are correct and not misspelled?

Also, is there a way we can have unit testing for this? We can def create a ticket to set it up.

Just because w/ unit tests we can also create new tests as we find bugs that would catch those if they were in place.

@@ -45,6 +45,14 @@

{{/each}}
{{/if}}
{{#if medEntry.substanceAdministration.precondition}}
Copy link
Member Author

@jonahkaye jonahkaye Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grabbing conditions linked to medications.
This gets us a few more conditions but which empirically seem to not be the most valuable. Optional to keep this, though my preference is to default on the side of getting as much data from the convert and filtering out downstream.

@@ -110,3 +111,35 @@ export function convertHtmlTablesToCsv(html: string) {

return convertedCsv;
}

function main() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utility to call in the command line

@@ -1,15 +1,21 @@
import { bundleToHtml } from "@metriport/core/external/aws/lambda-logic/bundle-to-html";
import fs from "fs";

// get xml file from this folder and bundle to html
// Check if a file path is provided
if (process.argv.length < 3) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utility to call in command line

@jonahkaye jonahkaye changed the title feat(filter): snomed hydrating SNOMED Hydrating, Filtering, Special MR Generation Apr 3, 2024
@jonahkaye jonahkaye marked this pull request as ready for review April 3, 2024 17:59
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Analysis done on preconditions on sample patient set for reference:

Total #of Conditions in the directory: 401308
Total #of Conditions generated from <Precondition> in the directory: 685
Total #of Conditions generated from <Precondition> in the directory of type disorder: 56

15802004 with count 1 is a disorder: Dystonia (disorder)
16932000 with count 20 is a disorder: Nausea and vomiting (disorder)
37796009 with count 3 is a disorder: Migraine (disorder)
38341003 with count 3 is a disorder: Hypertensive disorder, systemic arterial (disorder)
39579001 with count 2 is a disorder: Anaphylaxis (disorder)
70153002 with count 3 is a disorder: Hemorrhoids (disorder)
193462001 with count 6 is a disorder: Insomnia (disorder)
242253008 with count 2 is a disorder: Overdose of opiate (disorder)
302866003 with count 4 is a disorder: Hypoglycemia (disorder)
419076005 with count 1 is a disorder: Allergic reaction (disorder)
422400008 with count 10 is a disorder: Vomiting (disorder)
860914002 with count 1 is a disorder: Erectile dysfunction (disorder)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main customer facing output of this PR. A script that for a given customer, gets all their patients consolidated data and performs the filtering and MR summary generation

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Full script for running this whole process

{{/with}}
{{/if}}
{{else if (contains (toJsonString entryRelationship.observation.templateId) '2.16.840.1.113883.10.20.22.4.19')}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't tell what the OID is without looking up online. This is a yellow flag, as it increases dependency on the internet and external services. Maybe we can start moving those shared constants on a single file?

@@ -5,7 +5,7 @@
{{>Resources/Medication.hbs medication=medAdm.substanceAdministration.consumable.manufacturedProduct.manufacturedMaterial ID=(generateUUID (toJsonString medAdm.substanceAdministration.consumable.manufacturedProduct.manufacturedMaterial))}},
{{>References/MedicationAdministration/medicationReference.hbs ID=(generateUUID (toJsonString medAdm.substanceAdministration)) REF=(concat 'Medication/' (generateUUID (toJsonString medAdm.substanceAdministration.consumable.manufacturedProduct.manufacturedMaterial)))}},

{{#with (evaluate 'Utils/GenerateOrganizationId.hbs' obj=edAdm.substanceAdministration.performer.assignedEntity.representedOrganization) as |orgId|}}
{{#with (evaluate 'Utils/GenerateOrganizationId.hbs' obj=medAdm.substanceAdministration.performer.assignedEntity.representedOrganization) as |orgId|}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch, this is stuff that our tooling should catch for us. Isn't there a way we can parse/compile those? At least to check if the variable names are correct and not misspelled?

Also, is there a way we can have unit testing for this? We can def create a ticket to set it up.

Just because w/ unit tests we can also create new tests as we find bugs that would catch those if they were in place.

Comment on lines +46 to +48
} catch (error) {
console.error(`Error fetching FHIR data for patient ${patientId}:`, error);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is failing silently and the code will keep executing. Is that expected?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add documentation explaining what it does and how to use it, please.

Comment on lines +1 to +4
// This script is used to process all of a customers patients through a snomed filtering pipeline, and
// then generate MR summaries and CSV files from the resulting bundles
// The script makes consolidated queries, inserts a patient FHIR resource, and then calls the filtering
// and MR generation logic.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use multi-line comments

also, please indicate how to use it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add documentation explaining what it does and how to use it, please.

@@ -54,8 +54,8 @@ dayjs.extend(duration);
* - fhirBaseUrl: the URL of the FHIR server;
*/

const cdaLocation = ``;
const converterBaseUrl = "http:https://localhost:8777";
const cdaLocation = `/Users/jonahkaye/Desktop/2024-01-23T08:02:29.892Z/test-patient-sample`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left-over

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add documentation explaining what it does and how to use it, please.

Comment on lines +352 to +359
async function main() {
const [directoryPath] = process.argv.slice(2);
if (!directoryPath) {
console.error("Please provide a directory path as an argument.");
process.exit(1);
}
await fullProcessing(directoryPath);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It's a pain to have to scroll to the bottom and make the way up instead of just following the text from top to bottom as one scrolls it down

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add documentation explaining what it does and how to use it, please.

@jonahkaye jonahkaye marked this pull request as draft May 7, 2024 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants