Skip to content

Commit

Permalink
Moved load ecqm-content script to dbSetup.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
elsaperelli committed Oct 2, 2024
1 parent e853f82 commit 1400b53
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 158 deletions.
1 change: 1 addition & 0 deletions service/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules
.DS_Store
cache
ecqm-content-r4-2021
ecqm-content-qicore-2024
6 changes: 6 additions & 0 deletions service/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ To load multiple bundles from a directory, run the same script with the desired
npm run db:loadBundle <path to directory>
```

To load [ecqm-content-qicore-2024](https://github.com/cqframework/ecqm-content-qicore-2024) into your database, clone the repository in the service directory of this repository. Then you can run the following script:

```
npm run db:loadEcqmContent <optional service url, default: http://localhost:3000/4_0_1>
```

### Bundle Upload Details

Upon uploading a Measure resource, the Measure's main library is added to the `relatedArtifact` array with an [isOwned extension](https://build.fhir.org/ig/HL7/fhir-extensions/StructureDefinition-artifact-isOwned.html).
Expand Down
4 changes: 2 additions & 2 deletions service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"db:setup": "ts-node ./scripts/dbSetup.ts create",
"db:loadBundle": "ts-node ./scripts/dbSetup.ts loadBundle",
"db:postBundle": "ts-node ./scripts/dbSetup.ts postBundle",
"loadBundles": "ts-node ./scripts/loadBundles.ts",
"db:loadEcqmContent": "ts-node ./scripts/dbSetup.ts loadEcqmContent",
"lint": "eslint \"./src/**/*.{js,ts}\"",
"lint:fix": "eslint \"./src/**/*.{js,ts}\" --fix",
"prettier": "prettier --check \"./src/**/*.{js,ts}\"",
Expand Down Expand Up @@ -58,4 +58,4 @@
"uuid": "^9.0.0",
"zod": "^3.20.2"
}
}
}
127 changes: 127 additions & 0 deletions service/scripts/dbSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,123 @@ dotenv.config();

const DB_URL = process.env.DATABASE_URL || 'mongodb://localhost:27017/measure-repository';
const COLLECTION_NAMES = ['Measure', 'Library'];
const ECQM_CONTENT_PATH = 'ecqm-content-qicore-2024/bundles/measure';
const MEASURES_PATH = path.join(ECQM_CONTENT_PATH);

/**
* Gets the paths for the bundles in ecqm-content-qicore-2024
*/
function getBundlePaths(): string[] {
const filePaths: string[] = [];

fs.readdirSync(MEASURES_PATH, { withFileTypes: true }).forEach(ent => {
// if this item is a directory, look for .json files under it
if (ent.isDirectory()) {
fs.readdirSync(path.join(MEASURES_PATH, ent.name), { withFileTypes: true }).forEach(subEnt => {
if (!subEnt.isDirectory() && subEnt.name.endsWith('.json')) {
filePaths.push(path.join(MEASURES_PATH, ent.name, subEnt.name));
}
});
}
});
return filePaths;
}

/**
* Changes the incorrect ecqi.healthit.gov references on the Libraries in
* ecqm-content-qicore-2024 to the correct madie.cms.gov references
*/
async function fixAndPutLibraries(bundle: fhir4.Bundle, url: string) {
const libraries: fhir4.Library[] = bundle.entry
?.filter(entry => entry.resource?.resourceType === 'Library')
.map(entry => entry.resource as fhir4.Library) as fhir4.Library[];

for (const library of libraries) {
console.log(` Library ${library.id}`);
if (library.relatedArtifact) {
for (let index = 0; index < library.relatedArtifact.length; index++) {
const ra = library.relatedArtifact[index];
// if a related artifact is using a ecqi.healthit.gov reference it needs to be
// changed to a madie.cms.gov reference to match the urls the resources use
if (
(ra.type === 'depends-on' || ra.type === 'composed-of') &&
ra.resource?.startsWith('http://ecqi.healthit.gov/ecqms/Library')
) {
const newRef = ra.resource.replace(
'http://ecqi.healthit.gov/ecqms/Library',
'https://madie.cms.gov/Library/'
);
console.log(` replacing ra ${ra.resource} with ${newRef}`);
ra.resource = newRef;
}
}
}
try {
console.log(` PUT ${url}/Library/${library.id}`);

const resp = await fetch(`${url}/Library/${library.id}`, {
method: 'PUT',
body: JSON.stringify(library),
headers: {
'Content-Type': 'application/json+fhir'
}
});
console.log(` ${resp.status}`);
} catch (e) {
console.error(e);
}
}
}

/**
* Changes the Measures in ecqm-content-qicore-2024 to have status 'active'
* before being added to the database with a PUT
*/
async function putMeasure(bundle: fhir4.Bundle, url: string) {
const measure: fhir4.Measure = bundle.entry?.find(entry => entry.resource?.resourceType === 'Measure')
?.resource as fhir4.Measure;

console.log(` Measure ${measure.id}`);
try {
console.log(` PUT ${url}/Measure/${measure.id}`);
measure.status = 'active';
const resp = await fetch(`${url}/Measure/${measure.id}`, {
method: 'PUT',
body: JSON.stringify(measure),
headers: {
'Content-Type': 'application/json+fhir'
}
});
console.log(` ${resp.status}`);
if (resp.status >= 400) {
const responseBody = await resp.json();
if (responseBody.resourceType === 'OperationOutcome') {
console.log(JSON.stringify(responseBody, null, 2));
}
}
} catch (e) {
console.error(e);
}
}

/**
* Loads ecqm-content-qicore-2024 into the database
*/
async function loadEcqmContent(bundlePaths: string[], url: string) {
for (const path of bundlePaths) {
const bundle = JSON.parse(fs.readFileSync(path, 'utf8')) as fhir4.Bundle;
if (bundle.resourceType === 'Bundle') {
if (bundle?.entry) {
console.log('FILE' + path);
bundle.entry = modifyEntriesForUpload(bundle.entry);
await fixAndPutLibraries(bundle, url);
await putMeasure(bundle, url);
}
} else {
console.warn(`${path} is not a Bundle`);
}
}
}

async function createCollections() {
await Connection.connect(DB_URL);
Expand Down Expand Up @@ -310,6 +427,16 @@ if (process.argv[2] === 'delete') {
console.log('Done');
})
.catch(console.error);
} else if (process.argv[2] === 'loadEcqmContent') {
let url = 'http://localhost:3000/4_0_1';
if (process.argv.length < 4) {
console.log('Defaulting service url to http://localhost:3000/4_0_1');
} else {
url = process.argv[3];
}

const bundlePaths = getBundlePaths();
loadEcqmContent(bundlePaths, url);
} else {
console.log('Usage: ts-node src/scripts/dbSetup.ts <create|delete|reset>');
}
156 changes: 0 additions & 156 deletions service/scripts/loadBundles.ts

This file was deleted.

0 comments on commit 1400b53

Please sign in to comment.