├── .github ├── CODEOWNERS ├── pull_request_template.md └── workflows │ └── ci-workflow.yaml ├── test ├── application │ ├── fixtures │ │ ├── empty-run-logs.json │ │ ├── patient-mrns.csv │ │ ├── run-logs.json │ │ ├── example-clinical-trial-info.csv │ │ ├── example-patient.csv │ │ ├── example-disease-status.csv │ │ ├── example-condition.csv │ │ └── test-bundle.json │ ├── app.test.js │ └── runInstanceLogger.test.js ├── helpers │ ├── fixtures │ │ ├── valid-mrns.csv │ │ ├── invalid-mrns.csv │ │ ├── valid-mrns-bom.csv │ │ ├── emptyBundle.json │ │ ├── icd10.json │ │ ├── test-config.json │ │ ├── valid-resource.json │ │ ├── invalid-resource.json │ │ ├── count-bundle-5-same.json │ │ ├── count-bundle-5-unique.json │ │ ├── valueset-without-expansion.json │ │ ├── valueset-with-expansion.json │ │ ├── count-bundle-5-nested.json │ │ ├── condition-without-icd10.json │ │ ├── secondary-cancer-condition.json │ │ ├── condition-with-icd10.json │ │ ├── primary-cancer-condition.json │ │ └── searchsetBundleWithOneEntry.json │ ├── dateUtils.test.js │ ├── appUtils.test.js │ ├── dependencyUtils.test.js │ ├── csvParsingUtils.test.js │ ├── configUtils.test.js │ └── observationUtils.test.js ├── extractors │ ├── fixtures │ │ ├── example.csv │ │ ├── context-with-patient.json │ │ ├── csv-cancer-disease-status-module-response.json │ │ ├── csv-staging-module-response.json │ │ ├── csv-encounter-module-response.json │ │ ├── csv-treatment-plan-change-module-response.json │ │ ├── csv-clinical-trial-information-module-response.json │ │ ├── csv-observation-module-response.json │ │ ├── csv-patient-module-response.json │ │ ├── csv-appointment-module-response.json │ │ ├── csv-condition-module-response.json │ │ ├── csv-medication-administration-module-response.json │ │ ├── csv-procedure-module-response.json │ │ ├── csv-medication-request-module-response.json │ │ ├── csv-adverse-event-module-response.json │ │ ├── csv-encounter-bundle.json │ │ ├── csv-ctc-adverse-event-module-response.json │ │ ├── csv-medication-administration-bundle.json │ │ ├── csv-observation-bundle.json │ │ ├── csv-clinical-trial-information-bundle.json │ │ ├── csv-appointment-bundle.json │ │ ├── csv-adverse-event-bundle.json │ │ ├── csv-procedure-bundle.json │ │ └── patient-bundle.json │ ├── FHIREncounterExtractor.test.js │ ├── FHIRProcedureExtractor.test.js │ ├── FHIRMedicationOrderExtractor.test.js │ ├── Extractor.test.js │ ├── FHIRConditionExtractor.test.js │ ├── FHIRDocumentReferenceExtractor.test.js │ ├── FHIRMedicationStatementExtractor.test.js │ ├── FHIRAllergyIntoleranceExtractor.test.js │ ├── FHIRObservationExtractor.test.js │ ├── FHIRMedicationRequestExtractor.test.js │ ├── FHIRPatientExtractor.test.js │ ├── CSVPatientExtractor.test.js │ └── CSVConditionExtractor.test.js ├── sample-client-data │ ├── patient-mrns.csv │ ├── treatment-plan-change-information.csv │ ├── patient-information.csv │ ├── cancer-disease-status-information.csv │ ├── clinical-trial-information.csv │ ├── appointment-information.csv │ ├── observation-information.csv │ ├── encounter-information.csv │ ├── staging-information.csv │ ├── condition-information.csv │ ├── cancer-related-medication-administration-information.csv │ ├── procedure-information.csv │ ├── cancer-related-medication-request-information.csv │ ├── adverse-event-information.csv │ └── ctc-adverse-event-information.csv ├── templates │ ├── fixtures │ │ ├── minimal-reference-object.json │ │ ├── maximal-reference-object.json │ │ ├── maximal-coding-object.json │ │ ├── maximal-identifier.json │ │ ├── minimal-appointment-resource.json │ │ ├── minimal-encounter-resource.json │ │ ├── minimal-procedure-resource.json │ │ ├── identifier-array.json │ │ ├── minimal-adverse-event-resource.json │ │ ├── research-study-resource.json │ │ ├── minimal-condition-resource.json │ │ ├── research-subject-resource.json │ │ ├── maximal-encounter-resource.json │ │ ├── minimal-medication-request.json │ │ ├── minimal-medication-resource.json │ │ ├── minimal-ctc-adverse-event-resource.json │ │ ├── minimal-observation-resource.json │ │ ├── patient-resource.json │ │ ├── minimal-staging-clinical-resource.json │ │ ├── minimal-staging-pathologic-resource.json │ │ ├── nodes-category-clinical-resource.json │ │ ├── tumor-category-clinical-resource.json │ │ ├── nodes-category-pathologic-resource.json │ │ ├── tumor-category-pathologic-resource.json │ │ ├── metastases-category-clinical-resource.json │ │ ├── metastases-category-pathologic-resource.json │ │ ├── maximal-medication-resource.json │ │ ├── minimal-careplan-resource.json │ │ ├── minimal-disease-status-resource.json │ │ ├── maximal-observation-resource.json │ │ ├── maximal-appointment-resource.json │ │ ├── maximal-procedure-resource.json │ │ ├── maximal-adverse-event-resource.json │ │ ├── maximal-staging-resource.json │ │ ├── disease-status-resource.json │ │ ├── maximal-condition-resource.json │ │ └── maximal-patient-resource.json │ ├── researchStudy.test.js │ ├── snippets │ │ ├── reference.test.js │ │ ├── coding.test.js │ │ └── cancerStaging.test.js │ ├── researchSubject.test.js │ ├── encounter.test.js │ └── appointment.test.js ├── modules │ ├── fixtures │ │ ├── example-csv-empty-line.csv │ │ ├── example-csv-empty-values.csv │ │ ├── csv-response.json │ │ ├── example-csv.csv │ │ ├── example-csv-bom.csv │ │ └── patient-resource.json │ └── BaseFHIRModule.test.js └── utils.js ├── docs ├── patient-mrns.csv ├── CSV_Templates.xlsx ├── diagrams │ ├── steam-high-level-arch.png │ ├── steam-terminology-breakdown.png │ └── archive │ │ ├── 20_05-04_high-level-arch.png │ │ ├── 20_05_04_terminology-breakdown.png │ │ ├── 20_11_23_steam-high-level-arch.png │ │ ├── 21_06_11_steam-high-level-arch.png │ │ └── 20_11_23_steam-terminology-breakdown.png ├── staging.csv ├── treatment-plan-change.csv ├── patient.csv ├── clinical-trial-information.csv ├── observation.csv ├── appointment.csv ├── cancer-related-medication-administration.csv ├── encounter.csv ├── condition.csv ├── cancer-disease-status.csv ├── procedure.csv ├── cancer-related-medication-request.csv ├── adverse-event.csv ├── ctc-adverse-event.csv └── config.example.json ├── .gitignore ├── src ├── templates │ ├── index.js │ ├── snippets │ │ ├── subject.js │ │ ├── period.js │ │ ├── resource.js │ │ ├── medication.js │ │ ├── effectiveX.js │ │ ├── reference.js │ │ ├── treatmentReason.js │ │ ├── coding.js │ │ ├── bodySiteTemplate.js │ │ ├── extension.js │ │ ├── identifier.js │ │ ├── index.js │ │ └── cancerStaging.js │ ├── EncounterTemplate.js │ ├── ResearchStudyTemplate.js │ ├── ResearchSubjectTemplate.js │ ├── CancerRelatedMedicationAdministrationTemplate.js │ └── ProcedureTemplate.js ├── modules │ ├── index.js │ ├── BaseFHIRModule.js │ └── CSVFileModule.js ├── helpers │ ├── errors.js │ ├── cancerStagingUtils.js │ ├── lookups │ │ └── ctcAdverseEventLookup.js │ ├── logger.js │ ├── dateUtils.js │ ├── valueSets │ │ └── vs-expansion-explained.md │ ├── observationUtils.js │ ├── appUtils.js │ ├── csvValidator.js │ ├── lookupUtils.js │ ├── dependencyUtils.js │ └── configUtils.js ├── extractors │ ├── FHIRConditionExtractor.js │ ├── FHIREncounterExtractor.js │ ├── FHIRProcedureExtractor.js │ ├── FHIRObservationExtractor.js │ ├── FHIRMedicationOrderExtractor.js │ ├── FHIRDocumentReferenceExtractor.js │ ├── FHIRMedicationRequestExtractor.js │ ├── FHIRAllergyIntoleranceExtractor.js │ ├── FHIRMedicationStatementExtractor.js │ ├── Extractor.js │ ├── FHIRPatientExtractor.js │ ├── BaseCSVExtractor.js │ ├── MCODESurgicalProcedureExtractor.js │ ├── FHIRAdverseEventExtractor.js │ └── CSVEncounterExtractor.js ├── application │ ├── index.js │ └── tools │ │ └── mcodeExtraction.js ├── client │ └── MCODEClient.js └── cli │ └── cli.js ├── .eslintrc.json └── package.json /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @dmendelowitz 2 | -------------------------------------------------------------------------------- /test/application/fixtures/empty-run-logs.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /test/application/fixtures/patient-mrns.csv: -------------------------------------------------------------------------------- 1 | mrn 2 | 123 -------------------------------------------------------------------------------- /docs/patient-mrns.csv: -------------------------------------------------------------------------------- 1 | mrn 2 | mrn-1 3 | mrn-2 4 | mrn-3 5 | -------------------------------------------------------------------------------- /test/helpers/fixtures/valid-mrns.csv: -------------------------------------------------------------------------------- 1 | mrn 2 | 123 3 | 456 4 | 789 5 | -------------------------------------------------------------------------------- /test/extractors/fixtures/example.csv: -------------------------------------------------------------------------------- 1 | example,columns 2 | example,values 3 | -------------------------------------------------------------------------------- /test/helpers/fixtures/invalid-mrns.csv: -------------------------------------------------------------------------------- 1 | not-mrn 2 | 123 3 | 456 4 | 789 5 | -------------------------------------------------------------------------------- /test/helpers/fixtures/valid-mrns-bom.csv: -------------------------------------------------------------------------------- 1 | mrn 2 | 123 3 | 456 4 | 789 5 | -------------------------------------------------------------------------------- /test/sample-client-data/patient-mrns.csv: -------------------------------------------------------------------------------- 1 | mrn 2 | 123 3 | 456 4 | 789 5 | 1011 6 | -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-reference-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "reference": "urn:uuid:example-id" 3 | } 4 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | ## New behavior 3 | ## Code changes 4 | # Testing guidance 5 | -------------------------------------------------------------------------------- /docs/CSV_Templates.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcode/mcode-extraction-framework/HEAD/docs/CSV_Templates.xlsx -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode/ 3 | .DS_Store 4 | output/ 5 | logs/ 6 | config/* 7 | !config/csv.config.example.json 8 | -------------------------------------------------------------------------------- /test/application/fixtures/run-logs.json: -------------------------------------------------------------------------------- 1 | [{"dateRun":"2021-06-17T15:10:49.601Z","toDate":"2020-06-16","fromDate":"2019-01-01"}] -------------------------------------------------------------------------------- /docs/diagrams/steam-high-level-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcode/mcode-extraction-framework/HEAD/docs/diagrams/steam-high-level-arch.png -------------------------------------------------------------------------------- /test/helpers/fixtures/emptyBundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Bundle", 3 | "type": "searchset", 4 | "total": 0, 5 | "entry": [ 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /docs/diagrams/steam-terminology-breakdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcode/mcode-extraction-framework/HEAD/docs/diagrams/steam-terminology-breakdown.png -------------------------------------------------------------------------------- /src/templates/index.js: -------------------------------------------------------------------------------- 1 | const { generateMcodeResources } = require('./ResourceGenerator'); 2 | 3 | module.exports = { 4 | generateMcodeResources, 5 | }; 6 | -------------------------------------------------------------------------------- /docs/diagrams/archive/20_05-04_high-level-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcode/mcode-extraction-framework/HEAD/docs/diagrams/archive/20_05-04_high-level-arch.png -------------------------------------------------------------------------------- /test/templates/fixtures/maximal-reference-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "reference": "urn:uuid:example-id", 3 | "display": "example-name", 4 | "type": "ExampleType" 5 | } 6 | -------------------------------------------------------------------------------- /docs/diagrams/archive/20_05_04_terminology-breakdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcode/mcode-extraction-framework/HEAD/docs/diagrams/archive/20_05_04_terminology-breakdown.png -------------------------------------------------------------------------------- /docs/diagrams/archive/20_11_23_steam-high-level-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcode/mcode-extraction-framework/HEAD/docs/diagrams/archive/20_11_23_steam-high-level-arch.png -------------------------------------------------------------------------------- /docs/diagrams/archive/21_06_11_steam-high-level-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcode/mcode-extraction-framework/HEAD/docs/diagrams/archive/21_06_11_steam-high-level-arch.png -------------------------------------------------------------------------------- /docs/diagrams/archive/20_11_23_steam-terminology-breakdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcode/mcode-extraction-framework/HEAD/docs/diagrams/archive/20_11_23_steam-terminology-breakdown.png -------------------------------------------------------------------------------- /docs/staging.csv: -------------------------------------------------------------------------------- 1 | mrn,conditionId,stageGroup,t,n,m,type,stagingSystem,stagingCodeSystem,effectiveDate 2 | mrn-1,example-condition-id,3C,cT3,cN3,cM0,Clinical,443830009,http://snomed.info/sct,2020-01-01 3 | -------------------------------------------------------------------------------- /test/helpers/fixtures/icd10.json: -------------------------------------------------------------------------------- 1 | { 2 | "system": "http://hl7.org/fhir/sid/icd-10-cm", 3 | "code": "C50.211", 4 | "display": "Malignant neoplasm of upper-inner quadrant of right female breast" 5 | } 6 | -------------------------------------------------------------------------------- /docs/treatment-plan-change.csv: -------------------------------------------------------------------------------- 1 | mrn,reasonCode,reasonDisplayText,changed,dateOfCarePlan,dateRecorded 2 | mrn-1,281647001,Adverse reaction (disorder),true,2020-04-15,2020-05-01 3 | mrn-2,,,false,2020-03-30,2020-05-01 4 | -------------------------------------------------------------------------------- /test/templates/fixtures/maximal-coding-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "system": "example-sys", 3 | "version": "v3.1.4", 4 | "code": "example-code", 5 | "display": "A string of display text", 6 | "userSelected": true 7 | } 8 | -------------------------------------------------------------------------------- /test/templates/fixtures/maximal-identifier.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": { 3 | "system": "http://system.com/codesystem", 4 | "value": "90210", 5 | "type": { 6 | "text": "Text explaining what this code system value is" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/templates/snippets/subject.js: -------------------------------------------------------------------------------- 1 | const { reference } = require('./reference'); 2 | 3 | function subjectTemplate({ id }) { 4 | return { 5 | subject: reference({ id, resourceType: 'Patient' }), 6 | }; 7 | } 8 | 9 | module.exports = { 10 | subjectTemplate, 11 | }; 12 | -------------------------------------------------------------------------------- /test/modules/fixtures/example-csv-empty-line.csv: -------------------------------------------------------------------------------- 1 | mrn,trialSubjectID,enrollmentStatus,trialResearchID,trialStatus,dateRecorded 2 | example-mrn-1,subjectId-1,status-1,researchId-1,trialStatus-1,2020-01-10 3 | 4 | example-mrn-2,subjectId-3,status-3,researchId-3,trialStatus-3,2020-06-10 -------------------------------------------------------------------------------- /test/extractors/fixtures/context-with-patient.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Bundle", 3 | "entry": [ 4 | { 5 | "fullUrl": "context-url", 6 | "resource": { 7 | "resourceType": "Patient", 8 | "id": "mrn-1" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /test/modules/fixtures/example-csv-empty-values.csv: -------------------------------------------------------------------------------- 1 | mrn,trialSubjectID,enrollmentStatus,trialResearchID,trialStatus,dateRecorded 2 | example-mrn-1,subjectId-1,status-1,researchId-1,trialStatus-1,2020-01-10 3 | , , , , , 4 | , , , ,, 5 | ,,, , , 6 | example-mrn-not-ignored,,,,, -------------------------------------------------------------------------------- /src/modules/index.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRModule } = require('./BaseFHIRModule'); 2 | const { CSVFileModule } = require('./CSVFileModule'); 3 | const { CSVURLModule } = require('./CSVURLModule'); 4 | 5 | module.exports = { 6 | BaseFHIRModule, 7 | CSVFileModule, 8 | CSVURLModule, 9 | }; 10 | -------------------------------------------------------------------------------- /docs/patient.csv: -------------------------------------------------------------------------------- 1 | mrn,familyName,givenName,gender,birthsex,dateOfBirth,race,ethnicity,language,addressLine,city,state,zip 2 | pat-mrn-1,Doe,Jane,female,F,1991-06-21,ASKU,2186-5,no,2 West Side Rd,Malden,MA,02186 3 | pat-mrn-2,Doe,John,male,M,1986-11-08,2106-3,2186-5,en-US,1 Main street,Brooklyn,NY,11201 4 | -------------------------------------------------------------------------------- /src/templates/snippets/period.js: -------------------------------------------------------------------------------- 1 | function periodTemplate({ startDate, endDate }) { 2 | return { 3 | period: { 4 | ...(startDate && { start: startDate }), 5 | ...(endDate && { end: endDate }), 6 | }, 7 | }; 8 | } 9 | 10 | module.exports = { 11 | periodTemplate, 12 | }; 13 | -------------------------------------------------------------------------------- /test/modules/fixtures/csv-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "example-mrn-1", 4 | "trialsubjectid": "subjectId-1", 5 | "enrollmentstatus": "status-1", 6 | "trialresearchid": "researchId-1", 7 | "trialstatus": "trialStatus-1", 8 | "daterecorded": "2020-01-10" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /src/helpers/errors.js: -------------------------------------------------------------------------------- 1 | class NotImplementedError extends Error { 2 | constructor(...args) { 3 | super(...args); 4 | 5 | this.name = 'NotImplementedError'; 6 | Error.captureStackTrace(this, NotImplementedError); 7 | } 8 | } 9 | 10 | module.exports = { 11 | NotImplementedError, 12 | }; 13 | -------------------------------------------------------------------------------- /test/helpers/fixtures/test-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "patientIdCsvPath": "test/application/fixtures/patient-mrns.csv", 3 | "commonExtractorArgs": {}, 4 | "extractors": [], 5 | "notificationInfo": { 6 | "host": "smtp.example.com", 7 | "to": [ 8 | "demo@example.com" 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/helpers/fixtures/valid-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Patient", 3 | "id": "valid-patient", 4 | "name": [ 5 | { 6 | "family": "Frege", 7 | "given": [ 8 | "Gottlob" 9 | ] 10 | } 11 | ], 12 | "gender": "male", 13 | "birthDate": "1848-11-08" 14 | } 15 | -------------------------------------------------------------------------------- /test/sample-client-data/treatment-plan-change-information.csv: -------------------------------------------------------------------------------- 1 | mrn,reasonCode,reasonDisplayText,changed,dateOfCarePlan,dateRecorded 2 | 123,281647001,Adverse reaction (disorder),true,2020-04-15,2020-04-15 3 | 456,405613005,Planned Procedure (situation),true,2020-03-30,2020-04-15 4 | 789,,,false,2020-04-22,2020-06-17 5 | -------------------------------------------------------------------------------- /test/helpers/fixtures/invalid-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Patient", 3 | "id": "invalid-patient", 4 | "name": [ 5 | { 6 | "family": "Godel", 7 | "given": [ 8 | "Kurt" 9 | ] 10 | } 11 | ], 12 | "gender": "invalid-enum-value", 13 | "birthDate": "1906-04-28" 14 | } 15 | -------------------------------------------------------------------------------- /src/templates/snippets/resource.js: -------------------------------------------------------------------------------- 1 | function meta(profiles) { 2 | return { 3 | meta: { 4 | profile: profiles, 5 | }, 6 | }; 7 | } 8 | 9 | function narrative(status, div) { 10 | return { 11 | status, 12 | div, 13 | }; 14 | } 15 | 16 | module.exports = { 17 | meta, 18 | narrative, 19 | }; 20 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-cancer-disease-status-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "conditionid": "cond-1", 5 | "diseasestatuscode": "USCRS-352236", 6 | "dateofobservation": "2019-12-02", 7 | "evidence": "363679005|252416005", 8 | "observationstatus": "amended" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /docs/clinical-trial-information.csv: -------------------------------------------------------------------------------- 1 | mrn,trialSubjectID,enrollmentStatus,trialResearchID,trialStatus,trialResearchSystem,startDate,endDate 2 | 123,subjectId-1,example-status,researchId-1,example-trialStatus,research-system,YYYY-MM-DD,YYYY-MM-DD 3 | 456,subjectId-2,example-status,researchId-1,example-trialStatus,research-system,YYYY-MM-DD,YYYY-MM-DD 4 | -------------------------------------------------------------------------------- /test/application/fixtures/example-clinical-trial-info.csv: -------------------------------------------------------------------------------- 1 | mrn,trialSubjectID,enrollmentStatus,trialResearchID,trialStatus,trialResearchSystem 2 | 123,subjectId-1,potential-candidate,researchId-1,approved,system-1 3 | 456,subjectId-2,on-study-intervention,researchId-1,completed,system-2 4 | 789,subjectId-3,on-study-observation,researchId-2,active, 5 | -------------------------------------------------------------------------------- /test/modules/fixtures/example-csv.csv: -------------------------------------------------------------------------------- 1 | mrn,trialSubjectID,enrollmentStatus,trialResearchID,trialStatus,dateRecorded 2 | example-mrn-1,subjectId-1,status-1,researchId-1,trialStatus-1,2020-01-10 3 | example-mrn-2,subjectId-2,status-2,researchId-2,trialStatus-2,2020-01-10 4 | example-mrn-2,subjectId-3,status-3,researchId-3,trialStatus-3,2020-06-10 5 | -------------------------------------------------------------------------------- /test/modules/fixtures/example-csv-bom.csv: -------------------------------------------------------------------------------- 1 | mrn,trialSubjectID,enrollmentStatus,trialResearchID,trialStatus,dateRecorded 2 | example-mrn-1,subjectId-1,status-1,researchId-1,trialStatus-1,2020-01-10 3 | example-mrn-2,subjectId-2,status-2,researchId-2,trialStatus-2,2020-01-10 4 | example-mrn-2,subjectId-3,status-3,researchId-3,trialStatus-3,2020-06-10 5 | -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-appointment-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Appointment", 3 | "id": "appointmentId-3", 4 | "status": "cancelled", 5 | "participant": [ 6 | { 7 | "actor": { 8 | "reference": "urn:uuid:789", 9 | "type": "Patient" 10 | }, 11 | "status": "tentative" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-encounter-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Encounter", 3 | "id": "encounterId-1", 4 | "status": "arrived", 5 | "class": { 6 | "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", 7 | "code": "AMB" 8 | }, 9 | "subject": { 10 | "reference": "urn:uuid:123", 11 | "type": "Patient" 12 | } 13 | } -------------------------------------------------------------------------------- /test/application/fixtures/example-patient.csv: -------------------------------------------------------------------------------- 1 | mrn,familyName,givenName,gender,birthsex,dateOfBirth,race,ethnicity,language,addressLine,city,state,zip 2 | 123,Doe,Jane,female,F,1980-01-01,2028-9,2186-5,en,2 West Side Rd,Malden,MA,02148 3 | 456,Doe,John,male,M,1970-01-01,2106-3,2186-5,en-US,3 East Side Rd,Brooklyn,NY,11201 4 | 789,Doe,Jimmy,other,UNK,1980-03-01,ASKU,2186-5,ar,,,,90001 5 | -------------------------------------------------------------------------------- /test/sample-client-data/patient-information.csv: -------------------------------------------------------------------------------- 1 | mrn,familyName,givenName,gender,birthsex,dateOfBirth,race,ethnicity,language,addressLine,city,state,zip 2 | 123,Doe,Jane,female,F,1980-01-01,2028-9,2186-5,en,2 West Side Rd,Malden,MA,02148 3 | 456,Doe,John,male,M,1970-01-01,2106-3,2186-5,en-US,3 East Side Rd,Brooklyn,NY,11201 4 | 789,Doe,Jimmy,other,UNK,1980-03-01,ASKU,2186-5,ar,,,,90001 5 | -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-procedure-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Procedure", 3 | "id": "example-id", 4 | "status": "completed", 5 | "code": { 6 | "coding": [{ "system": "http://snomed.info/sct", "code": "152198000" }] 7 | }, 8 | "subject": { "reference": "urn:uuid:example-mrn", "type": "Patient" }, 9 | "performedDateTime": "2020-01-01" 10 | } 11 | -------------------------------------------------------------------------------- /test/sample-client-data/cancer-disease-status-information.csv: -------------------------------------------------------------------------------- 1 | mrn,conditionId,diseaseStatusCode,diseaseStatusText,dateOfObservation,evidence,observationStatus,dateRecorded 2 | 123,conditionId-1,268910001,responding,2019-12-02,363679005|252416005,preliminary,2020-01-10 3 | 789,conditionId-2,709137006,not evaluated,2020-04-22,,final,2020-06-10 4 | 123,conditionId-1,not-asked,,2020-01-12,,, 5 | -------------------------------------------------------------------------------- /src/templates/snippets/medication.js: -------------------------------------------------------------------------------- 1 | const { coding } = require('./coding'); 2 | 3 | function medicationTemplate({ code, codeSystem, displayText }) { 4 | return { 5 | medicationCodeableConcept: { 6 | coding: [coding({ system: codeSystem, code, display: displayText }), 7 | ], 8 | }, 9 | }; 10 | } 11 | 12 | module.exports = { 13 | medicationTemplate, 14 | }; 15 | -------------------------------------------------------------------------------- /test/sample-client-data/clinical-trial-information.csv: -------------------------------------------------------------------------------- 1 | mrn,trialSubjectID,enrollmentStatus,trialResearchID,trialStatus,trialResearchSystem,startDate,endDate 2 | 123,subjectId-1,potential-candidate,researchId-1,approved,system-1,2020-01-01,2021-01-03 3 | 456,subjectId-2,on-study-intervention,researchId-1,completed,system-2,2023-05-03, 4 | 789,subjectId-3,on-study-observation,researchId-2,active,,, 5 | -------------------------------------------------------------------------------- /test/templates/fixtures/identifier-array.json: -------------------------------------------------------------------------------- 1 | { 2 | "identifier": [ 3 | { 4 | "system": "http://system.com/codesystem", 5 | "value": "90210", 6 | "type": { 7 | "text": "Text explaining what this code system value is" 8 | } 9 | }, 10 | { 11 | "system": "http://system.com/testing", 12 | "value": "02135" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/templates/snippets/effectiveX.js: -------------------------------------------------------------------------------- 1 | function effectiveX(effective) { 2 | if (effective.high || effective.low) { 3 | return { 4 | effectivePeriod: { 5 | high: effective.high, 6 | low: effective.low, 7 | }, 8 | }; 9 | } 10 | return { 11 | effectiveDateTime: effective, 12 | }; 13 | } 14 | 15 | 16 | module.exports = { 17 | effectiveX, 18 | }; 19 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-staging-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "conditionid": "cond-1", 5 | "stagegroup": "3C", 6 | "t": "cT3", 7 | "n": "cN3", 8 | "m": "cM0", 9 | "type": "Clinical", 10 | "stagingsystem": "444256004", 11 | "stagingcodesystem": "http://snomed.info/sct", 12 | "effectivedate": "2020-01-01" 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /test/application/fixtures/example-disease-status.csv: -------------------------------------------------------------------------------- 1 | mrn,conditionId,diseaseStatusCode,diseaseStatusText,dateOfObservation,evidence,observationStatus,dateRecorded 2 | 123,conditionId-1,268910001,responding,2019-12-02,363679005|252416005,preliminary,2020-01-10 3 | 456,conditionId-2,359746009,stable,2020-01-12,363679005,amended,2020-01-12 4 | 789,conditionId-2,709137006,not evaluated,2020-04-22,,final,2020-06-10 5 | -------------------------------------------------------------------------------- /src/templates/snippets/reference.js: -------------------------------------------------------------------------------- 1 | function reference({ id, name, resourceType }) { 2 | if (!id) throw Error('Trying to render a reference snippet, but the id argument is missing.'); 3 | 4 | return { 5 | reference: `urn:uuid:${id}`, 6 | ...(name && { display: name }), 7 | ...(resourceType && { type: resourceType }), 8 | }; 9 | } 10 | 11 | module.exports = { 12 | reference, 13 | }; 14 | -------------------------------------------------------------------------------- /docs/observation.csv: -------------------------------------------------------------------------------- 1 | mrn,observationId,status,code,codeSystem,displayName,value,valueCodeSystem,effectiveDate,bodySite,laterality 2 | mrn-1,example-id-1,example-status,example-code,example-system,example-name,example-value,example-system,YYYY-MM-DD,example-site,example-laterality 3 | mrn-2,example-id-2,example-status,example-code,example-system,example-name,example-value,example-system,YYYY-MM-DD,example-site,example-laterality -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-encounter-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "encounterid": "encounterId-1", 5 | "status": "arrived", 6 | "classcode": "AMB", 7 | "classsystem": "http://terminology.hl7.org/CodeSystem/v3-ActCode", 8 | "typecode": "11429006", 9 | "typesystem": "http://snomed.info/sct", 10 | "startdate": "2020-01-10", 11 | "enddate": "2020-01-10" 12 | } 13 | ] -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-adverse-event-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "AdverseEvent", 3 | "id": "adverseEventId-1", 4 | "subject": { 5 | "reference": "urn:uuid:mrn-1", 6 | "type": "Patient" 7 | }, 8 | "event": { 9 | "coding": [ 10 | { 11 | "system": "code-system", 12 | "code": "109006" 13 | } 14 | ] 15 | }, 16 | "actuality": "actual", 17 | "date": "1994-12-09" 18 | } -------------------------------------------------------------------------------- /src/helpers/cancerStagingUtils.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { checkCodeInVs } = require('./valueSetUtils'); 3 | 4 | function isCancerStagingSystem(code, system) { 5 | const cancerStagingSystemVSPath = path.resolve(__dirname, 'valueSets', 'ValueSet-mcode-cancer-staging-system-vs.json'); 6 | return checkCodeInVs(code, system, cancerStagingSystemVSPath); 7 | } 8 | 9 | module.exports = { 10 | isCancerStagingSystem, 11 | }; 12 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-treatment-plan-change-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "subjectId": "mrn-1", 4 | "dateofcareplan": "2020-04-15", 5 | "reasoncode": "281647001", 6 | "reasondisplaytext": "Adverse reaction (disorder)", 7 | "changed": "true" 8 | }, 9 | { 10 | "subjectId": "mrn-1", 11 | "dateofcareplan": "2020-04-30", 12 | "reasoncode": "405613005", 13 | "changed": "true" 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /docs/appointment.csv: -------------------------------------------------------------------------------- 1 | mrn,appointmentId,status,serviceCategory,serviceType,appointmentType,specialty,start,end,cancelationCode,description 2 | mrn-1,exampleId-1,status-code,service-cat,service-type,app-type,specialty-code,YYYY-MM-DD,YYYY-MM-DD,cancel-code,"Example description 1" 3 | mrn-2,exampleId-2,status-code,service-cat,service-type,app-type,specialty-code,YYYY-MM-DD,YYYY-MM-DD,cancel-code,"Example description 2" 4 | mrn-2,exampleId-3,status-code,,,,,,,, -------------------------------------------------------------------------------- /src/extractors/FHIRConditionExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | class FHIRConditionExtractor extends BaseFHIRExtractor { 4 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 5 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 6 | this.resourceType = 'Condition'; 7 | } 8 | } 9 | 10 | module.exports = { 11 | FHIRConditionExtractor, 12 | }; 13 | -------------------------------------------------------------------------------- /src/extractors/FHIREncounterExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | class FHIREncounterExtractor extends BaseFHIRExtractor { 4 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 5 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 6 | this.resourceType = 'Encounter'; 7 | } 8 | } 9 | 10 | module.exports = { 11 | FHIREncounterExtractor, 12 | }; 13 | -------------------------------------------------------------------------------- /src/extractors/FHIRProcedureExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | class FHIRProcedureExtractor extends BaseFHIRExtractor { 4 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 5 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 6 | this.resourceType = 'Procedure'; 7 | } 8 | } 9 | 10 | module.exports = { 11 | FHIRProcedureExtractor, 12 | }; 13 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-clinical-trial-information-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "patientId": "mrn-1", 4 | "trialsubjectid": "example-subjectId", 5 | "enrollmentstatus": "example-enrollment-status", 6 | "trialresearchid": "example-researchId", 7 | "trialstatus": "example-trialStatus", 8 | "trialresearchsystem":"example-system", 9 | "startdate": "2020-01-01", 10 | "enddate": "2021-01-01" 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /src/application/index.js: -------------------------------------------------------------------------------- 1 | const { mcodeApp } = require('./app'); 2 | const { RunInstanceLogger } = require('./tools/RunInstanceLogger'); 3 | const { sendEmailNotification, zipErrors } = require('./tools/emailNotifications'); 4 | const { extractDataForPatients } = require('./tools/mcodeExtraction'); 5 | 6 | module.exports = { 7 | mcodeApp, 8 | RunInstanceLogger, 9 | extractDataForPatients, 10 | sendEmailNotification, 11 | zipErrors, 12 | }; 13 | -------------------------------------------------------------------------------- /src/extractors/FHIRObservationExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | class FHIRObservationExtractor extends BaseFHIRExtractor { 4 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 5 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 6 | this.resourceType = 'Observation'; 7 | } 8 | } 9 | 10 | module.exports = { 11 | FHIRObservationExtractor, 12 | }; 13 | -------------------------------------------------------------------------------- /test/application/fixtures/example-condition.csv: -------------------------------------------------------------------------------- 1 | mrn,conditionId,codeSystem,code,displayName,category,dateOfDiagnosis,clinicalStatus,verificationStatus,bodySite,laterality,histology 2 | 456,conditionId-2,http://snomed.info/sct,363346000,Prostate Cancer,problem-list-item,2019-11-11,remission,confirmed,251007,24028007,2735009 3 | 789,conditionId-2,http://snomed.info/sct,363346000,Lung Cancer,encounter-diagnosis,2020-02-14,resolved,confirmed,251007,66459002,2985005 -------------------------------------------------------------------------------- /test/sample-client-data/appointment-information.csv: -------------------------------------------------------------------------------- 1 | mrn,appointmentId,status,serviceCategory,serviceType,appointmentType,specialty,start,end,cancelationCode,description 2 | 123,appointmentId-1,arrived,35,175,CHECKUP,394592004,2019-12-10T09:00:00Z,2019-12-10T11:00:00Z,,"Example description 1" 3 | 456,appointmentId-2,pending,35,175,FOLLOWUP,394592004,2020-12-10T09:00:00Z,2020-12-10T11:00:00Z,,"Example description 2" 4 | 789,appointmentId-3,cancelled,,,,,,,pat-cpp, -------------------------------------------------------------------------------- /docs/cancer-related-medication-administration.csv: -------------------------------------------------------------------------------- 1 | mrn,medicationId,code,codeSystem,displayText,startDate,endDate,treatmentReasonCode,treatmentReasonCodeSystem,treatmentReasonDisplayText,treatmentIntent,status 2 | pat-mrn-1,medicationId-1,code,code-system,code-text,YYYY-MM-DD,YYYY-MM-DD,code,code-system,display-text,intent-code,example-status 3 | pat-mrn-2,medicationId-2,code,code-system,code-text,YYYY-MM-DD,YYYY-MM-DD,code,code-system,display-text,intent-code,example-status -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-observation-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "observationid": "observation-id", 5 | "status": "final", 6 | "code": "1695-6", 7 | "codesystem": "http://loinc.org", 8 | "displayname": "", 9 | "value": "10828004", 10 | "valuecodesystem": "http://snomed.info/sct", 11 | "effectivedate": "2020-01-02", 12 | "bodysite": "12345", 13 | "laterality": "678910" 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /src/extractors/FHIRMedicationOrderExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | class FHIRMedicationOrderExtractor extends BaseFHIRExtractor { 4 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 5 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 6 | this.resourceType = 'MedicationOrder'; 7 | } 8 | } 9 | 10 | module.exports = { 11 | FHIRMedicationOrderExtractor, 12 | }; 13 | -------------------------------------------------------------------------------- /src/extractors/FHIRDocumentReferenceExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | class FHIRDocumentReferenceExtractor extends BaseFHIRExtractor { 4 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 5 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 6 | this.resourceType = 'DocumentReference'; 7 | } 8 | } 9 | 10 | module.exports = { 11 | FHIRDocumentReferenceExtractor, 12 | }; 13 | -------------------------------------------------------------------------------- /src/extractors/FHIRMedicationRequestExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | class FHIRMedicationRequestExtractor extends BaseFHIRExtractor { 4 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 5 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 6 | this.resourceType = 'MedicationRequest'; 7 | } 8 | } 9 | 10 | module.exports = { 11 | FHIRMedicationRequestExtractor, 12 | }; 13 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-patient-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "119147111821125", 4 | "familyname": "Marshall", 5 | "givenname": "Archy", 6 | "gender": "male", 7 | "birthsex": "male", 8 | "dateofbirth":"1994-08-24", 9 | "language": "en", 10 | "addressline": "57 Adams St", 11 | "city": "New Rochelle", 12 | "state": "NY", 13 | "zip": "10801", 14 | "race": "1002-5", 15 | "ethnicity": "2186-5" 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /test/sample-client-data/observation-information.csv: -------------------------------------------------------------------------------- 1 | mrn,observationId,status,code,codeSystem,displayName,value,valueCodeSystem,effectiveDate,bodySite,laterality 2 | 123,observation-id-1,final,1695-6,http://loinc.org,,10828004,http://snomed.info/sct,2020-01-02,251007,66459002 3 | 456,observation-id-2,final,8302-2,http://loinc.org,Body height,66.89 [in_i],,2020-01-02,251007,66459002 4 | 789,observation-id-3,final,29463-7,http://loinc.org,Body Weight,185 [lb_av],,2020-01-02,251007,66459002 -------------------------------------------------------------------------------- /docs/encounter.csv: -------------------------------------------------------------------------------- 1 | mrn,encounterId,status,classCode,classSystem,typeCode,typeSystem,startDate,endDate 2 | mrn-1,exampleId-1,status-code,class-code,http://terminology.hl7.org/CodeSystem/v3-ActCode,type-code,http://snomed.info/sct,YYYY-MM-DD,YYYY-MM-DD 3 | mrn-2,exampleId-2,status-code,class-code,http://terminology.hl7.org/CodeSystem/v3-ActCode,type-code,http://snomed.info/sct,YYYY-MM-DD,YYYY-MM-DD 4 | mrn-3,exampleId-3,status-code,class-code,http://terminology.hl7.org/CodeSystem/v3-ActCode,,,, -------------------------------------------------------------------------------- /test/sample-client-data/encounter-information.csv: -------------------------------------------------------------------------------- 1 | mrn,encounterId,status,classCode,classSystem,typeCode,typeSystem,startDate,endDate 2 | 123,encounterId-1,arrived,AMB,http://terminology.hl7.org/CodeSystem/v3-ActCode,11429006,http://snomed.info/sct,2020-01-10,2020-01-10 3 | 456,encounterId-2,planned,AMB,http://terminology.hl7.org/CodeSystem/v3-ActCode,270427003,http://snomed.info/sct,2021-02-10,2021-05-12 4 | 789,encounterId-3,cancelled,IMP,http://terminology.hl7.org/CodeSystem/v3-ActCode,,,, -------------------------------------------------------------------------------- /src/extractors/FHIRAllergyIntoleranceExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | 4 | class FHIRAllergyIntoleranceExtractor extends BaseFHIRExtractor { 5 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 6 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 7 | this.resourceType = 'AllergyIntolerance'; 8 | } 9 | } 10 | 11 | module.exports = { 12 | FHIRAllergyIntoleranceExtractor, 13 | }; 14 | -------------------------------------------------------------------------------- /src/extractors/FHIRMedicationStatementExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | 3 | class FHIRMedicationStatementExtractor extends BaseFHIRExtractor { 4 | constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) { 5 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 6 | this.resourceType = 'MedicationStatement'; 7 | } 8 | } 9 | 10 | module.exports = { 11 | FHIRMedicationStatementExtractor, 12 | }; 13 | -------------------------------------------------------------------------------- /docs/condition.csv: -------------------------------------------------------------------------------- 1 | mrn,conditionId,codeSystem,code,displayName,category,dateOfDiagnosis,clinicalStatus,verificationStatus,bodySite,laterality,histology 2 | mrn-1,conditionId-1,example-code-system,example-code,Example Name,example-category,YYYY-MM-DD,example-status,example-status,example-site,example-laterality,example-histology 3 | mrn-2,conditionId-2,example-code-system,example-code,Example Name,example-category,YYYY-MM-DD,example-status,example-status,example-site,example-laterality,example-histology -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-appointment-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "appointmentid": "appointmentId-1", 5 | "status": "arrived", 6 | "servicecategory": "35", 7 | "servicetype": "175", 8 | "appointmenttype": "CHECKUP", 9 | "specialty": "394592004", 10 | "start": "2019-12-10T09:00:00+00:00", 11 | "end": "2019-12-10T11:00:00+00:00", 12 | "cancelationcode": "pat-cpp", 13 | "description": "Example description 1" 14 | } 15 | ] -------------------------------------------------------------------------------- /docs/cancer-disease-status.csv: -------------------------------------------------------------------------------- 1 | mrn,conditionId,diseaseStatusCode,diseaseStatusText,dateOfObservation,evidence,observationStatus,dateRecorded 2 | pat-mrn-1,cond-1,268910001,Patient's Condition improved,2019-12-02,363679005|252416005,registered,2020-01-10 3 | pat-mrn-2,cond-2,268910001,Patient's Condition improved,2019-12-03,363679005,amended,2020-01-10 4 | pat-mrn-3,cond-3,260415000,Not detected,2019-12-04,363679005,,2020-01-10 5 | pat-mrn-4,cond-4,268910001,Patient's Condition improved,2019-12-05,,final,2020-05-10 -------------------------------------------------------------------------------- /src/templates/snippets/treatmentReason.js: -------------------------------------------------------------------------------- 1 | const { coding } = require('./coding'); 2 | 3 | function treatmentReasonTemplate({ treatmentReasonCode, treatmentReasonCodeSystem, treatmentReasonDisplayText }) { 4 | return { 5 | reasonCode: [ 6 | { 7 | coding: [coding({ system: treatmentReasonCodeSystem, code: treatmentReasonCode, display: treatmentReasonDisplayText }), 8 | ], 9 | }, 10 | ], 11 | }; 12 | } 13 | 14 | module.exports = { 15 | treatmentReasonTemplate, 16 | }; 17 | -------------------------------------------------------------------------------- /src/extractors/Extractor.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | const { NotImplementedError } = require('../helpers/errors'); 3 | 4 | class Extractor { 5 | updateRequestHeaders() { 6 | throw new NotImplementedError('Extractor must implement the "updateRequestHeaders" function if WebService-enabled modules are used'); 7 | } 8 | 9 | get() { 10 | throw new NotImplementedError('Extractor must implement the "get" function'); 11 | } 12 | } 13 | 14 | module.exports = { 15 | Extractor, 16 | }; 17 | -------------------------------------------------------------------------------- /test/extractors/FHIREncounterExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIREncounterExtractor } = require('../../src/extractors/FHIREncounterExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | 6 | const extractor = new FHIREncounterExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 7 | describe('FHIREncounterExtractor', () => { 8 | test('Constructor sets resourceType as Encounter', () => { 9 | expect(extractor.resourceType).toEqual('Encounter'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/extractors/FHIRProcedureExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRProcedureExtractor } = require('../../src/extractors/FHIRProcedureExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | 6 | const extractor = new FHIRProcedureExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 7 | describe('FHIRProcedureExtractor', () => { 8 | test('Constructor sets resourceType as Procedure', () => { 9 | expect(extractor.resourceType).toEqual('Procedure'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/sample-client-data/staging-information.csv: -------------------------------------------------------------------------------- 1 | mrn,conditionId,stageGroup,t,n,m,type,stagingSystem,stagingCodeSystem,effectiveDate 2 | 123,conditionId-1,3C,cT3,cN3,cM0,Clinical,C146985,http://ncimeta.nci.nih.gov,2020-01-01 3 | 456,conditionId-2,3C,cT3,cN3,cM0,Clinical,443830009,http://snomed.info/sct,2020-01-01 4 | 789,conditionId-3,3C,,cN3,cM0,Clinical,444256004,http://snomed.info/sct,2020-01-02 5 | 789,conditionId-3,3C,cT3,,cM0,Clinical,,,2020-01-03 6 | 789,conditionId-3,3C,cT3,cN3,,Clinical,258235000,http://snomed.info/sct,2020-01-04 -------------------------------------------------------------------------------- /test/extractors/FHIRMedicationOrderExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRMedicationOrderExtractor } = require('../../src/extractors/FHIRMedicationOrderExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | 6 | const extractor = new FHIRMedicationOrderExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 7 | describe('FHIRMedicationOrderExtractor', () => { 8 | test('Constructor sets resourceType as MedicationOrder', () => { 9 | expect(extractor.resourceType).toEqual('MedicationOrder'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/extractors/Extractor.test.js: -------------------------------------------------------------------------------- 1 | const { NotImplementedError } = require('../../src/helpers/errors'); 2 | const { Extractor } = require('../../src/extractors'); 3 | 4 | const extractor = new Extractor(); 5 | 6 | describe('base extractor', () => { 7 | test('get should throw NotImplementedError', () => { 8 | expect(() => extractor.get()).toThrow(NotImplementedError); 9 | }); 10 | test('updateRequestHeaders should throw NotImplementedError', () => { 11 | expect(() => extractor.updateRequestHeaders()).toThrow(NotImplementedError); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/extractors/FHIRConditionExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRConditionExtractor } = require('../../src/extractors/FHIRConditionExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | 6 | 7 | const extractor = new FHIRConditionExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 8 | 9 | describe('FHIRConditionExtractor', () => { 10 | describe('Constructor', () => { 11 | test('sets resourceType to Condition', () => { 12 | expect(extractor.resourceType).toEqual('Condition'); 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/extractors/FHIRDocumentReferenceExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRDocumentReferenceExtractor } = require('../../src/extractors/FHIRDocumentReferenceExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | 6 | const extractor = new FHIRDocumentReferenceExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 7 | describe('FHIRDocumentReferenceExtractor', () => { 8 | test('Constructor sets resourceType as DocumentReference', () => { 9 | expect(extractor.resourceType).toEqual('DocumentReference'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-condition-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "conditionid": "conditionId-1", 5 | "codesystem": "http://hl7.org/fhir/sid/icd-10-cm", 6 | "code": "C02.0", 7 | "displayname": "Some Cancer Condition", 8 | "category": "example-category", 9 | "dateofdiagnosis": "YYYY-MM-DD", 10 | "clinicalstatus": "example-status", 11 | "verificationstatus": "example-status", 12 | "bodysite": "example-site", 13 | "laterality": "example-laterality", 14 | "histology": "example-histology" 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /test/extractors/FHIRMedicationStatementExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRMedicationStatementExtractor } = require('../../src/extractors/FHIRMedicationStatementExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | 6 | const extractor = new FHIRMedicationStatementExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 7 | describe('FHIRMedicationStatementExtractor', () => { 8 | test('Constructor sets resourceType as MedicationStatement', () => { 9 | expect(extractor.resourceType).toEqual('MedicationStatement'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /test/sample-client-data/condition-information.csv: -------------------------------------------------------------------------------- 1 | mrn,conditionId,codeSystem,code,displayName,category,dateOfDiagnosis,clinicalStatus,verificationStatus,bodySite,laterality,histology 2 | 123,conditionId-1,http://snomed.info/sct,363346000,Breast Cancer,encounter-diagnosis,2020-07-12,relapse,confirmed,251007,7771000,2424003 3 | 456,conditionId-2,http://snomed.info/sct,363346000,Prostate Cancer,problem-list-item,2019-11-11,remission,confirmed,251007,24028007,2735009 4 | 789,conditionId-3,http://snomed.info/sct,363346000,Lung Cancer,encounter-diagnosis,2020-02-14,resolved,confirmed,251007,66459002,2985005 -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-medication-administration-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "medicationid": "medicationId-1", 5 | "code": "example-code", 6 | "codesystem": "example-code-system", 7 | "displaytext": "Example Text", 8 | "startdate": "YYYY-MM-DD", 9 | "enddate": "YYYY-MM-DD", 10 | "treatmentreasoncode": "example-reason", 11 | "treatmentreasoncodesystem": "example-code-system", 12 | "treatmentreasondisplaytext": "Example Text", 13 | "treatmentintent": "example-code", 14 | "status": "example-status" 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /test/templates/fixtures/research-study-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "ResearchStudy", 3 | "id": "id-for-research-study", 4 | "status": "active", 5 | "site": [ 6 | { 7 | "display": "ID associated with Clinical Trial", 8 | "identifier": { 9 | "system": "EXAMPLE_SITE_SYSTEM", 10 | "value": "EXAMPLE_SITE_ID" 11 | } 12 | } 13 | ], 14 | "identifier": [ 15 | { 16 | "value": "AFT1235", 17 | "system": "example-system", 18 | "type": { 19 | "text": "Clinical Trial Research ID" 20 | } 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /docs/procedure.csv: -------------------------------------------------------------------------------- 1 | mrn,procedureId,conditionId,status,code,codeSystem,displayName,reasonCode,reasonCodeSystem,reasonDisplayName,effectiveDate,bodySite,laterality,treatmentIntent 2 | mrn-1,example-id-1,example-condition-id-1,example-status,example-code,example-system,example-name,example-reason-code,example-system,example-reason-display,YYYY-MM-DD,example-site,example-laterality,example-treatment-intent 3 | mrn-2,example-id-2,example-condition-id-2,example-status,example-code,example-system,example-name,example-reason-code,example-system,example-reason-display,YYYY-MM-DD,example-site,example-laterality,example-treatment-intent -------------------------------------------------------------------------------- /test/helpers/fixtures/count-bundle-5-same.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Bundle", 3 | "entry": [ 4 | { 5 | "resource": { 6 | "resourceType": "Resource-1" 7 | } 8 | }, 9 | { 10 | "resource": { 11 | "resourceType": "Resource-1" 12 | } 13 | }, 14 | { 15 | "resource": { 16 | "resourceType": "Resource-1" 17 | } 18 | }, 19 | { 20 | "resource": { 21 | "resourceType": "Resource-1" 22 | } 23 | }, 24 | { 25 | "resource": { 26 | "resourceType": "Resource-1" 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /test/helpers/fixtures/count-bundle-5-unique.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Bundle", 3 | "entry": [ 4 | { 5 | "resource": { 6 | "resourceType": "Resource-1" 7 | } 8 | }, 9 | { 10 | "resource": { 11 | "resourceType": "Resource-2" 12 | } 13 | }, 14 | { 15 | "resource": { 16 | "resourceType": "Resource-3" 17 | } 18 | }, 19 | { 20 | "resource": { 21 | "resourceType": "Resource-4" 22 | } 23 | }, 24 | { 25 | "resource": { 26 | "resourceType": "Resource-5" 27 | } 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-condition-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Condition", 3 | "id": "example-id", 4 | "category": [ 5 | { 6 | "coding": [ 7 | { 8 | "system": "http://terminology.hl7.org/CodeSystem/condition-category", 9 | "code": "example-code" 10 | } 11 | ] 12 | } 13 | ], 14 | "code": { 15 | "coding": [ 16 | { 17 | "system": "example-system", 18 | "code": "example-code" 19 | } 20 | ] 21 | }, 22 | "subject": { 23 | "reference": "urn:uuid:example-subject-id", 24 | "type": "Patient" 25 | } 26 | } -------------------------------------------------------------------------------- /test/extractors/FHIRAllergyIntoleranceExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRAllergyIntoleranceExtractor } = require('../../src/extractors/FHIRAllergyIntoleranceExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | 6 | const extractor = new FHIRAllergyIntoleranceExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 7 | 8 | describe('FHIRAllergyIntoleranceExtractor', () => { 9 | describe('Constructor', () => { 10 | test('sets resourceType as AllergyIntolerance', () => { 11 | expect(extractor.resourceType).toEqual('AllergyIntolerance'); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/templates/fixtures/research-subject-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "ResearchSubject", 3 | "id": "id-for-research-subject", 4 | "identifier": [ 5 | { 6 | "type": { 7 | "text": "Clinical Trial Subject ID" 8 | }, 9 | "system": "http://example.com/clinicaltrialsubjectids", 10 | "value": "123" 11 | } 12 | ], 13 | "status": "candidate", 14 | "study": { 15 | "reference": "ResearchStudy/rs1" 16 | }, 17 | "individual": { 18 | "reference": "urn:uuid:mCODEPatient1" 19 | }, 20 | "period": { 21 | "start": "2020-01-01", 22 | "end": "2021-01-01" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-procedure-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "procedureid": "procedure-1", 5 | "conditionid": "condition-id", 6 | "status": "completed", 7 | "codesystem": "http://snomed.info/sct", 8 | "code": "152198000", 9 | "displayname": "Brachytherapy (procedure)", 10 | "reasoncode": "example-code", 11 | "reasoncodesystem": "example-system", 12 | "reasondisplayname": "example-name", 13 | "bodysite": "example-site", 14 | "laterality": "example-laterality", 15 | "treatmentintent": "example-treatment-intent", 16 | "effectivedate": "2020-01-01" 17 | } 18 | ] -------------------------------------------------------------------------------- /test/templates/fixtures/maximal-encounter-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Encounter", 3 | "id": "encounterId-1", 4 | "status": "arrived", 5 | "class": { 6 | "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", 7 | "code": "AMB" 8 | }, 9 | "subject": { 10 | "reference": "urn:uuid:123", 11 | "type": "Patient" 12 | }, 13 | "type": [ 14 | { 15 | "coding": [ 16 | { 17 | "system": "http://snomed.info/sct", 18 | "code": "11429006" 19 | } 20 | ] 21 | } 22 | ], 23 | "period": { 24 | "start": "2020-01-10", 25 | "end": "2020-01-10" 26 | } 27 | } -------------------------------------------------------------------------------- /test/extractors/FHIRObservationExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRObservationExtractor } = require('../../src/extractors/FHIRObservationExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | 6 | 7 | // Construct extractor and create spies for mocking responses 8 | const extractor = new FHIRObservationExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 9 | 10 | describe('FHIRObservationExtractor', () => { 11 | describe('Constructor', () => { 12 | test('sets resourceType as Observation', () => { 13 | expect(extractor.resourceType).toEqual('Observation'); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/helpers/fixtures/valueset-without-expansion.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "ValueSet", 3 | "id": "valueset-example", 4 | "compose": { 5 | "include": [ 6 | { 7 | "system": "http://hl7.org/fhir/sid/icd-10-cm", 8 | "concept": [ 9 | { 10 | "code": "C00.0", 11 | "display": "Malignant neoplasm of external upper lip" 12 | } 13 | ] 14 | }, 15 | { 16 | "concept": [ 17 | { 18 | "code": "C00.1", 19 | "display": "Malignant neoplasm of external upper lip" 20 | } 21 | ] 22 | } 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/extractors/FHIRMedicationRequestExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRMedicationRequestExtractor } = require('../../src/extractors/FHIRMedicationRequestExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | // Construct extractor and create spies for mocking responses 6 | const extractor = new FHIRMedicationRequestExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 7 | 8 | describe('FHIRMedicationRequestExtractor', () => { 9 | describe('Constructor', () => { 10 | test('sets resourceType as MedicationRequest', () => { 11 | expect(extractor.resourceType).toEqual('MedicationRequest'); 12 | }); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-medication-request.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "MedicationRequest", 3 | "meta": { 4 | "profile": [ 5 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-cancer-related-medication-request" 6 | ] 7 | }, 8 | "status": "example-status", 9 | "intent": "example-intent", 10 | "medicationCodeableConcept": { 11 | "coding": [ 12 | { 13 | "system": "example-code-system", 14 | "code": "example-code" 15 | } 16 | ] 17 | }, 18 | "subject": { 19 | "reference": "urn:uuid:mrn-1", 20 | "type": "Patient" 21 | }, 22 | "requester": { 23 | "reference": "urn:uuid:example-requester" 24 | } 25 | } -------------------------------------------------------------------------------- /src/helpers/lookups/ctcAdverseEventLookup.js: -------------------------------------------------------------------------------- 1 | const { createInvertedLookup, createLowercaseLookup } = require('../lookupUtils'); 2 | 3 | const ctcAEGradeTextToCodeLookup = { 4 | 'Absent Adverse Event': '0', 5 | 'Mild Adverse Event': '1', 6 | 'Moderate Adverse Event': '2', 7 | 'Severe Adverse Event': '3', 8 | 'Life Threatening or Disabling Adverse Event': '4', 9 | 'Death Related to Adverse Event': '5', 10 | }; 11 | 12 | const ctcAEGradeCodeToTextLookup = createInvertedLookup(ctcAEGradeTextToCodeLookup); 13 | 14 | module.exports = { 15 | ctcAEGradeCodeToTextLookup: createLowercaseLookup(ctcAEGradeCodeToTextLookup), 16 | ctcAEGradeTextToCodeLookup: createLowercaseLookup(ctcAEGradeTextToCodeLookup), 17 | }; 18 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "commonjs": true, 5 | "es6": true, 6 | "jest": true 7 | }, 8 | "extends": [ 9 | "airbnb-base" 10 | ], 11 | "globals": { 12 | "Atomics": "readonly", 13 | "SharedArrayBuffer": "readonly" 14 | }, 15 | "parserOptions": { 16 | "ecmaVersion": 2018 17 | }, 18 | "rules": { 19 | "no-console": "off", 20 | "max-len": ["error", { "code": 200 }], 21 | "object-curly-newline": ["error", { 22 | "ObjectPattern": { "minProperties": 5 } 23 | }], 24 | "no-underscore-dangle": ["error", { "allow": ["__get__", "__set__","_include"] }] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/helpers/logger.js: -------------------------------------------------------------------------------- 1 | const { loggers, format, transports } = require('winston'); 2 | 3 | const LOGGER_NAME = 'cli'; 4 | const logFormat = format.printf(({ level, message, timestamp }) => (`${timestamp} [${level}]: ${message}`)); 5 | 6 | if (!loggers.has(LOGGER_NAME)) { 7 | loggers.add(LOGGER_NAME, { 8 | level: process.env.LOGGING || 'info', 9 | format: format.combine( 10 | format.colorize(), 11 | format.timestamp({ format: 'HH:mm:ss.SS' }), 12 | format.align(), 13 | logFormat, 14 | ), 15 | transports: [ 16 | new transports.Console({ 17 | silent: process.env.LOGGING === 'none', 18 | }), 19 | ], 20 | }); 21 | } 22 | 23 | module.exports = loggers.get(LOGGER_NAME); 24 | -------------------------------------------------------------------------------- /docs/cancer-related-medication-request.csv: -------------------------------------------------------------------------------- 1 | mrn,requestId,code,codeSystem,displayText,treatmentReasonCode,treatmentReasonCodeSystem,treatmentReasonDisplayText,procedureIntent,status,intent,authoredOn,requesterId,dosageRoute,asNeededCode,doseRateType,doseQuantityValue,doseQuantityUnit,timingCode,timingEvent 2 | mrn-1,requestId-1,code,code-system,code-text,code,code-system,display-text,procedure-intent-code,status-code,intent-code,YYY-MM-DD,requesterId-1,route-code,needed-code,rate-type,quantity-value,quantity-unit,timing-code,YYYY-MM-DD 3 | mrn-2,requestId-2,code,code-system,code-text,code,code-system,display-text,procedure-intent-code,status-code,intent-code,YYY-MM-DD,requesterId-2,route-code,needed-code,rate-type,quantity-value,quantity-unit,timing-code,YYYY-MM-DD -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-medication-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "MedicationAdministration", 3 | "meta": { 4 | "profile": [ 5 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-cancer-related-medication-administration" 6 | ] 7 | }, 8 | "status": "example-status", 9 | "medicationCodeableConcept": { 10 | "coding": [ 11 | { 12 | "system": "example-code-system", 13 | "code": "example-code" 14 | } 15 | ] 16 | }, 17 | "subject": { 18 | "reference": "urn:uuid:mrn-1", 19 | "type": "Patient" 20 | }, 21 | "effectivePeriod": { 22 | "extension": [ 23 | { 24 | "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", 25 | "valueCode": "unknown" 26 | } 27 | ] 28 | } 29 | } -------------------------------------------------------------------------------- /.github/workflows/ci-workflow.yaml: -------------------------------------------------------------------------------- 1 | name: Lint and Test 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | lint: 7 | name: Lint 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v1 11 | - uses: actions/setup-node@v1 12 | with: 13 | node-version: '12.x' 14 | - run: npm ci 15 | - run: npm run lint 16 | env: 17 | CI: true 18 | test: 19 | name: Tests on ${{ matrix.os }} 20 | runs-on: ${{ matrix.os }} 21 | strategy: 22 | matrix: 23 | os: [ubuntu-latest, windows-latest, macos-latest] 24 | steps: 25 | - uses: actions/checkout@v1 26 | - uses: actions/setup-node@v1 27 | with: 28 | node-version: '12.x' 29 | - run: npm ci 30 | - run: npm test 31 | env: 32 | CI: true -------------------------------------------------------------------------------- /docs/adverse-event.csv: -------------------------------------------------------------------------------- 1 | mrn,adverseEventId,adverseEventCode,adverseEventCodeSystem,adverseEventDisplayText,suspectedCauseId,suspectedCauseType,seriousness,seriousnessCodeSystem,seriousnessDisplayText,category,categoryCodeSystem,categoryDisplayText,severity,actuality,studyId,effectiveDate,recordedDate 2 | mrn-full-example,example-id-1,event-code,code-system,code-display,cause-id,resourceType,seriousness-code,code-system,seriousness-display,category-code,code-system,category-dislpay,mild,actual,id,1994-12-09,1994-12-09 3 | mrn-two-category-example,example-id-2,event-code,code-system,code-display,cause-id,resourceType,seriousness-code,code-system,seriousness-display,category-code|category-code,code-system|code-system,category-display|category-display,mild,actual,id,1994-12-09,1994-12-09 4 | mrn-minimal-example,,code-from-default-system,,,,,,,,,,,,,,1994-12-09, 5 | -------------------------------------------------------------------------------- /test/sample-client-data/cancer-related-medication-administration-information.csv: -------------------------------------------------------------------------------- 1 | mrn,medicationId,code,codeSystem,displayText,startDate,endDate,treatmentReasonCode,treatmentReasonCodeSystem,treatmentReasonDisplayText,treatmentIntent,status 2 | 123,medicationId-1,10760,http://www.nlm.nih.gov/research/umls/rxnorm,Triamcinolone Oral Paste,2020-01-01,2020-07-05,999000,http://snomed.info/sct,Mixed islet cell and exocrine adenocarcinoma,373808002,on-hold 3 | 456,medicationId-2,91318,http://www.nlm.nih.gov/research/umls/rxnorm,Coal Tar Topical Solution,2020-02-17,2020-08-13,915007,http://snomed.info/sct,Malignant melanoma in junctional nevus,373808002,completed 4 | 789,medicationId-3,91833,http://www.nlm.nih.gov/research/umls/rxnorm,Vitamin K1 Injectable Solution [Aquamephyton],2020-01-12,2020-10-01,900006,http://snomed.info/sct,Mucin-producing adenocarcinoma,363676003,stopped -------------------------------------------------------------------------------- /test/helpers/fixtures/valueset-with-expansion.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "ValueSet", 3 | "id": "valueset-example", 4 | "compose": { 5 | "include": [ 6 | { 7 | "system": "http://hl7.org/fhir/sid/icd-10-cm", 8 | "concept": [ 9 | { 10 | "code": "C00.0", 11 | "display": "Malignant neoplasm of external upper lip" 12 | } 13 | ] 14 | } 15 | ] 16 | }, 17 | "expansion": { 18 | "timestamp": "2020-04-02T12:54:59-0400", 19 | "contains": [ 20 | { 21 | "code": "C00.1", 22 | "display": "Malignant neoplasm of external lower lip" 23 | }, 24 | { 25 | "code": "C00.2", 26 | "display": "Malignant neoplasm of external lower lip", 27 | "system": "http://hl7.org/fhir/sid/icd-10-cm" 28 | } 29 | ] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/extractors/FHIRPatientExtractor.test.js: -------------------------------------------------------------------------------- 1 | const { FHIRPatientExtractor } = require('../../src/extractors/FHIRPatientExtractor.js'); 2 | 3 | const MOCK_URL = 'http://example.com'; 4 | const MOCK_HEADERS = {}; 5 | const MOCK_MRN = '123456789'; 6 | 7 | const extractor = new FHIRPatientExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS }); 8 | describe('FHIRPatientExtractor', () => { 9 | test('Constructor sets resourceType as Patient', () => { 10 | expect(extractor.resourceType).toEqual('Patient'); 11 | }); 12 | 13 | test('parametrizeArgsForFHIRModule should translate the MRN value into the identifier param', async () => { 14 | const params = await extractor.parametrizeArgsForFHIRModule({ mrn: MOCK_MRN }); 15 | expect(params).toHaveProperty('identifier'); 16 | expect(params.identifier).toEqual(`MRN|${MOCK_MRN}`); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-ctc-adverse-event-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "AdverseEvent", 3 | "id": "adverseEventId-1", 4 | "subject": { 5 | "reference": "urn:uuid:mrn-1", 6 | "type": "Patient" 7 | }, 8 | "extension" : [ 9 | { 10 | "url" : "http://hl7.org/fhir/us/ctcae/StructureDefinition/ctcae-grade", 11 | "valueCodeableConcept" : { 12 | "coding" : [ 13 | { 14 | "system" : "http://hl7.org/fhir/us/ctcae/CodeSystem/ctcae-grade-code-system", 15 | "code" : "2", 16 | "display" : "Moderate Adverse Event" 17 | } 18 | ] 19 | } 20 | } 21 | ], 22 | "event": { 23 | "coding": [ 24 | { 25 | "system": "code-system", 26 | "code": "109006" 27 | } 28 | ] 29 | }, 30 | "actuality": "actual", 31 | "date": "1994-12-09" 32 | } -------------------------------------------------------------------------------- /src/templates/snippets/coding.js: -------------------------------------------------------------------------------- 1 | const { ifSomeArgsObj } = require('../../helpers/templateUtils'); 2 | 3 | // Template for FHIR Coding, based on https://www.hl7.org/fhir/datatypes.html#Coding 4 | function coding({ 5 | system, version, code, display, userSelected, 6 | }) { 7 | return ifSomeArgsObj( 8 | ({ 9 | system: system_, version: version_, code: code_, display: display_, userSelected: userSelected_, // using the _ to avoid duplicating vars across scopes 10 | }) => ({ 11 | ...(system_ && { system: system_ }), 12 | ...(version_ && { version: version_ }), 13 | ...(code_ && { code: code_ }), 14 | ...(display_ && { display: display_ }), 15 | ...(userSelected_ && { userSelected: userSelected_ }), 16 | }), 17 | )({ system, version, code, display, userSelected }); 18 | } 19 | 20 | module.exports = { 21 | coding, 22 | }; 23 | -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-observation-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "status": "final", 5 | "category": [ 6 | { 7 | "coding": [ 8 | { 9 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 10 | "code": "vital-signs", 11 | "display": "Vital Signs" 12 | } 13 | ] 14 | } 15 | ], 16 | "code": { 17 | "coding": [ 18 | { 19 | "system": "http://loinc.org", 20 | "code": "8302-2" 21 | 22 | } 23 | ] 24 | }, 25 | "subject" : { 26 | "reference": "urn:uuid:example-mrn", 27 | "type": "Patient" 28 | }, 29 | "effectiveDateTime" : "2020-01-01", 30 | "valueQuantity": { 31 | "value": 70, 32 | "unit": "in", 33 | "code": "[in_i]", 34 | "system": "http://unitsofmeasure.org" 35 | } 36 | } -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-medication-request-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "requestid": "requestId-1", 5 | "code": "example-code", 6 | "codesystem": "example-code-system", 7 | "displaytext": "Example Text", 8 | "authoredon": "YYYY-MM-DD", 9 | "treatmentreasoncode": "example-reason", 10 | "treatmentreasoncodesystem": "example-code-system", 11 | "treatmentreasondisplaytext": "Example Text", 12 | "procedureintent": "example-code", 13 | "status": "example-status", 14 | "requesterid": "example-requester", 15 | "intent": "example-intent", 16 | "dosageroute": "example-route", 17 | "asneededcode": "example-asneeded", 18 | "doseratetype": "example-type", 19 | "dosequantityvalue": "111", 20 | "dosequantityunit": "example-unit", 21 | "timingevent": "YYYY-MM-DD", 22 | "timingcode": "example-code" 23 | } 24 | ] -------------------------------------------------------------------------------- /src/templates/snippets/bodySiteTemplate.js: -------------------------------------------------------------------------------- 1 | const { valueX } = require('./valueX'); 2 | const { coding } = require('./coding'); 3 | const { ifAllArgsObj } = require('../../helpers/templateUtils'); 4 | 5 | function lateralityTemplate({ laterality }) { 6 | return { 7 | extension: [ 8 | { 9 | url: 'http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-laterality', 10 | ...valueX({ code: laterality, system: 'http://snomed.info/sct' }, 'valueCodeableConcept'), 11 | }, 12 | ], 13 | }; 14 | } 15 | 16 | function bodySiteTemplate({ bodySite, laterality }) { 17 | if (!bodySite) return null; 18 | 19 | return { 20 | bodySite: { 21 | ...ifAllArgsObj(lateralityTemplate)({ laterality }), 22 | coding: [coding({ 23 | system: 'http://snomed.info/sct', 24 | code: bodySite, 25 | })], 26 | }, 27 | }; 28 | } 29 | 30 | module.exports = { 31 | bodySiteTemplate, 32 | }; 33 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-adverse-event-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "adverseeventid": "adverseEventId-1", 5 | "adverseeventcode": "109006", 6 | "adverseeventcodesystem": "code-system", 7 | "adverseeventdisplaytext": "Anxiety disorder of childhood OR adolescence", 8 | "suspectedcauseid": "procedure-id", 9 | "suspectedcausetype": "Procedure", 10 | "seriousness": "serious", 11 | "seriousnesscodesystem": "http://terminology.hl7.org/CodeSystem/adverse-event-seriousness", 12 | "seriousnessdisplaytext": "Serious", 13 | "category": "product-use-error", 14 | "categorycodesystem": "http://terminology.hl7.org/CodeSystem/adverse-event-category", 15 | "categorydisplaytext": "Product Use Error", 16 | "severity": "severe", 17 | "actuality": "actual", 18 | "studyid": "researchId-1", 19 | "effectivedate": "12-09-1994", 20 | "recordeddate": "12-09-1994" 21 | } 22 | ] -------------------------------------------------------------------------------- /src/client/MCODEClient.js: -------------------------------------------------------------------------------- 1 | const { BaseClient } = require('./BaseClient'); 2 | const { allExtractors, dependencyInfo } = require('../extractors'); 3 | const { sortExtractors } = require('../helpers/dependencyUtils.js'); 4 | 5 | class MCODEClient extends BaseClient { 6 | constructor({ extractors, commonExtractorArgs, webServiceAuthConfig }) { 7 | super(); 8 | this.registerExtractors(...allExtractors); 9 | // Store the extractors defined by the configuration file as local state 10 | this.extractorConfig = extractors; 11 | // Sort extractors based on order and dependencies 12 | this.extractorConfig = sortExtractors(this.extractorConfig, dependencyInfo); 13 | // Store webServiceAuthConfig if provided` 14 | this.authConfig = webServiceAuthConfig; 15 | this.commonExtractorArgs = { 16 | implementation: 'mcode', 17 | ...commonExtractorArgs, 18 | }; 19 | } 20 | } 21 | module.exports = { 22 | MCODEClient, 23 | }; 24 | -------------------------------------------------------------------------------- /test/sample-client-data/procedure-information.csv: -------------------------------------------------------------------------------- 1 | mrn,procedureId,conditionId,status,code,codeSystem,displayName,reasonCode,reasonCodeSystem,reasonDisplayName,effectiveDate,bodySite,laterality,treatmentIntent 2 | 123,procedure-id-1,condition-id-1,completed,152198000,http://snomed.info/sct,Brachytherapy (procedure),363346000,http://snomed.info/sct,Malignant tumor,2020-01-01,41224006,51440002,373808002 3 | 456,procedure-id-2,condition-id-2,in-progress,174337000,http://snomed.info/sct,Destruction of lesion,363346000,http://snomed.info/sct,Malignant tumor,2020-01-12,41224006,51440002,373808002 4 | 789,procedure-id-3,condition-id-3,completed,172043006,http://snomed.info/sct,Total mastectomy,363346000,http://snomed.info/sct,Malignant tumor,2020-06-30,41224006,51440002,373808002 5 | 789,procedure-id-4,condition-id-3,in-progress,10611004,http://snomed.info/sct,Teleradiotherapy protons (procedure),363346000,http://snomed.info/sct,Malignant tumor,2020-01-12,41224006,51440002,373808002 -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-encounter-bundle.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Bundle", 3 | "type": "collection", 4 | "entry": [ 5 | { 6 | "fullUrl": "urn:uuid:encounterId-1", 7 | "resource": { 8 | "resourceType": "Encounter", 9 | "id": "encounterId-1", 10 | "status": "arrived", 11 | "class": { 12 | "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", 13 | "code": "AMB" 14 | }, 15 | "subject": { 16 | "reference": "urn:uuid:mrn-1", 17 | "type": "Patient" 18 | }, 19 | "type": [ 20 | { 21 | "coding": [ 22 | { 23 | "system": "http://snomed.info/sct", 24 | "code": "11429006" 25 | } 26 | ] 27 | } 28 | ], 29 | "period": { 30 | "start": "2020-01-10", 31 | "end": "2020-01-10" 32 | } 33 | } 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /src/templates/snippets/extension.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const { ifSomeArgs } = require('../../helpers/templateUtils'); 3 | 4 | function extensionArr(...extensions) { // 0. Spread since 1..n extensions 5 | // Extensions should always return if at least one is provided; use ifSomeArgs 6 | return ifSomeArgs( 7 | (...extArr) => ({ extension: _.compact(extArr) }), // 2. Spread since 1...n extensions; _.compact to drop the falsy ones 8 | )(...extensions); // 1. Spread to pass each extension individually 9 | } 10 | 11 | // See http://hl7.org/fhir/R4/extension-data-absent-reason.html 12 | // reasonCode is any code from Value Set http://hl7.org/fhir/R4/valueset-data-absent-reason.html 13 | function dataAbsentReasonExtension(reasonCode) { 14 | return { 15 | url: 'http://hl7.org/fhir/StructureDefinition/data-absent-reason', 16 | valueCode: reasonCode, 17 | }; 18 | } 19 | 20 | module.exports = { 21 | dataAbsentReasonExtension, 22 | extensionArr, 23 | }; 24 | -------------------------------------------------------------------------------- /test/templates/fixtures/patient-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Patient", 3 | "id": "SomeId", 4 | "identifier": [ 5 | { 6 | "type": { 7 | "coding": [ 8 | { 9 | "system": "http://terminology.hl7.org/CodeSystem/v2-0203", 10 | "code": "MR", 11 | "display": "Medical Record Number" 12 | } 13 | ], 14 | "text": "Medical Record Number" 15 | }, 16 | "system": "http://example.com/system/mrn", 17 | "value": "1234" 18 | } 19 | ], 20 | "name": [ 21 | { 22 | "extension": [ 23 | { 24 | "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", 25 | "valueCode": "unknown" 26 | } 27 | ] 28 | } 29 | ], 30 | "_gender": { 31 | "extension": [ 32 | { 33 | "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason", 34 | "valueCode": "unknown" 35 | } 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/modules/BaseFHIRModule.js: -------------------------------------------------------------------------------- 1 | const { FHIRClient } = require('fhir-crud-client'); 2 | const logger = require('../helpers/logger'); 3 | const { getBundleResourcesByType, logOperationOutcomeInfo } = require('../helpers/fhirUtils'); 4 | 5 | class BaseFHIRModule { 6 | constructor(baseUrl, requestHeaders) { 7 | this.baseUrl = baseUrl; 8 | this.client = new FHIRClient(this.baseUrl, requestHeaders); 9 | } 10 | 11 | updateRequestHeaders(newHeaders) { 12 | this.client.updateRequestHeaders(newHeaders); 13 | } 14 | 15 | async search(resourceType, params) { 16 | logger.debug(`GET ${this.baseUrl}/${resourceType}`); 17 | const result = await this.client.search({ resourceType, params }); 18 | const operationOutcomeEntry = getBundleResourcesByType(result, 'OperationOutcome', {}, true); 19 | if (operationOutcomeEntry) { 20 | logOperationOutcomeInfo(operationOutcomeEntry); 21 | } 22 | return result; 23 | } 24 | } 25 | 26 | module.exports = { 27 | BaseFHIRModule, 28 | }; 29 | -------------------------------------------------------------------------------- /test/helpers/fixtures/count-bundle-5-nested.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Bundle", 3 | "entry": [ 4 | { 5 | "resource": { 6 | "resourceType": "Resource-1" 7 | }, 8 | "contained": [ 9 | { 10 | "resource": { 11 | "resourceType": "Resource-1" 12 | }, 13 | "contained": [ 14 | { 15 | "resource": { 16 | "resourceType": "Resource-1" 17 | }, 18 | "contained": [ 19 | { 20 | "resource": { 21 | "resourceType": "Resource-1" 22 | }, 23 | "contained": [ 24 | { 25 | "resource": { 26 | "resourceType": "Resource-1" 27 | } 28 | } 29 | ] 30 | } 31 | ] 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /test/sample-client-data/cancer-related-medication-request-information.csv: -------------------------------------------------------------------------------- 1 | mrn,requestId,code,codeSystem,displayText,treatmentReasonCode,treatmentReasonCodeSystem,treatmentReasonDisplayText,procedureIntent,status,intent,authoredOn,requesterId,dosageRoute,asNeededCode,doseRateType,doseQuantityValue,doseQuantityUnit,timingCode,timingEvent 2 | 123,requestId-1,10760,http://www.nlm.nih.gov/research/umls/rxnorm,Triamcinolone Oral Paste,999000,http://snomed.info/sct,Mixed islet cell and exocrine adenocarcinoma,373808002,active,order,2020-01-01,requester-1,26643006,1152001,calculated,100,mg,QD,2023-01-01 3 | 456,requestId-2,91318,http://www.nlm.nih.gov/research/umls/rxnorm,Coal Tar Topical Solution,915007,http://snomed.info/sct,Malignant melanoma in junctional nevus,373808002,on-hold,proposal,2019-02-02,requester-2,46713006,1140008,ordered,50,mg,AM,2023-04-04 4 | 789,requestId-3,91833,http://www.nlm.nih.gov/research/umls/rxnorm,Vitamin K1 Injectable Solution [Aquamephyton],900006,http://snomed.info/sct,Mucin-producing adenocarcinoma,363676003,cancelled,plan,,requester-3,,,,,,, 5 | -------------------------------------------------------------------------------- /src/templates/snippets/identifier.js: -------------------------------------------------------------------------------- 1 | const { ifSomeArgs, ifSomeArgsObj } = require('../../helpers/templateUtils'); 2 | 3 | function identifier({ system, type, value }) { 4 | return ifSomeArgsObj( 5 | ({ system: system_, type: type_, value: value_ }) => ({ 6 | identifier: { 7 | ...(system_ && { system: system_ }), 8 | ...(value_ && { value: value_ }), 9 | ...(type_ && { type: type_ }), 10 | }, 11 | }), 12 | )({ system, type, value }); 13 | } 14 | 15 | // Transform a list of identifier-objects into a list of identifier vales. Shape of return value below 16 | // identifier: [ 17 | // { system: "http://sys.com", value:"12345"}, 18 | // { system: "http://sys.com", value:"54321"}, 19 | // ] 20 | function identifierArr(...identifiers) { 21 | return ifSomeArgs( 22 | (...idenArr) => ({ identifier: idenArr.map((idenData) => identifier(idenData).identifier) }), 23 | )(...identifiers); // 1. Spread to pass each extension individually 24 | } 25 | 26 | module.exports = { 27 | identifier, 28 | identifierArr, 29 | }; 30 | -------------------------------------------------------------------------------- /docs/ctc-adverse-event.csv: -------------------------------------------------------------------------------- 1 | mrn,adverseEventId,adverseEventCode,adverseEventCodeSystem,adverseEventCodeVersion,adverseEventDisplayText,adverseEventText,suspectedCauseId,suspectedCauseType,seriousness,seriousnessCodeSystem,seriousnessDisplayText,category,categoryCodeSystem,categoryDisplayText,studyId,effectiveDate,recordedDate,grade,expectation,resolvedDate,seriousnessOutcome,actor,functionCode 2 | mrn-full-example,example-id-1,event-code,code-system,code-version,code-display,event-text,cause-id,resourceType,seriousness-code,code-system,seriousness-display,category-code,code-system,category-dislpay,id,1994-12-09,1994-12-09,1,expectation-code,YYYY-MM-DD,seriousness-outcome-code,practitioner-id,function-code 3 | mrn-two-category-example,example-id-2,event-code,code-system,code-version,code-display,event-text,cause-id,resourceType,seriousness-code,code-system,seriousness-display,category-code,code-system,category-dislpay,id,1994-12-09,1994-12-09,1,expectation-code,YYYY-MM-DD,seriousness-outcome-code,practitioner-id, 4 | mrn-minimal-example,,code-from-default-system,,,,,,,,,,,,,1994-12-09,,1,,,,,, 5 | -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-staging-clinical-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-cancer-stage-group" 7 | ] 8 | }, 9 | "status": "final", 10 | "category": [ 11 | { 12 | "coding": [ 13 | { 14 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 15 | "code": "survey" 16 | } 17 | ] 18 | } 19 | ], 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://loinc.org", 24 | "code": "21908-9" 25 | } 26 | ] 27 | }, 28 | "subject": { 29 | "reference": "urn:uuid:example-mrn", 30 | "type": "Patient" 31 | }, 32 | "effectiveDateTime": "2020-01-01", 33 | "valueCodeableConcept": { 34 | "coding": [ 35 | { 36 | "system": "http://cancerstaging.org", 37 | "code": "3C" 38 | } 39 | ] 40 | }, 41 | "focus": [ 42 | { 43 | "reference": "urn:uuid:example-condition-id", 44 | "type": "Condition" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-staging-pathologic-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-cancer-stage-group" 7 | ] 8 | }, 9 | "status": "final", 10 | "category": [ 11 | { 12 | "coding": [ 13 | { 14 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 15 | "code": "laboratory" 16 | } 17 | ] 18 | } 19 | ], 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://loinc.org", 24 | "code": "21902-2" 25 | } 26 | ] 27 | }, 28 | "subject": { 29 | "reference": "urn:uuid:example-mrn", 30 | "type": "Patient" 31 | }, 32 | "effectiveDateTime": "2020-01-01", 33 | "valueCodeableConcept": { 34 | "coding": [ 35 | { 36 | "system": "http://cancerstaging.org", 37 | "code": "3C" 38 | } 39 | ] 40 | }, 41 | "focus": [ 42 | { 43 | "reference": "urn:uuid:example-condition-id", 44 | "type": "Condition" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /test/templates/fixtures/nodes-category-clinical-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-tnm-regional-nodes-category" 7 | ] 8 | }, 9 | "status": "final", 10 | "category": [ 11 | { 12 | "coding": [ 13 | { 14 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 15 | "code": "survey" 16 | } 17 | ] 18 | } 19 | ], 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://loinc.org", 24 | "code": "21906-3" 25 | } 26 | ] 27 | }, 28 | "subject": { 29 | "reference": "urn:uuid:example-mrn", 30 | "type": "Patient" 31 | }, 32 | "effectiveDateTime": "2020-01-01", 33 | "valueCodeableConcept": { 34 | "coding": [ 35 | { 36 | "system": "http://cancerstaging.org", 37 | "code": "cN3" 38 | } 39 | ] 40 | }, 41 | "focus": [ 42 | { 43 | "reference": "urn:uuid:example-condition-id", 44 | "type": "Condition" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /test/templates/fixtures/tumor-category-clinical-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-tnm-primary-tumor-category" 7 | ] 8 | }, 9 | "status": "final", 10 | "category": [ 11 | { 12 | "coding": [ 13 | { 14 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 15 | "code": "survey" 16 | } 17 | ] 18 | } 19 | ], 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://loinc.org", 24 | "code": "21905-5" 25 | } 26 | ] 27 | }, 28 | "subject": { 29 | "reference": "urn:uuid:example-mrn", 30 | "type": "Patient" 31 | }, 32 | "effectiveDateTime": "2020-01-01", 33 | "valueCodeableConcept": { 34 | "coding": [ 35 | { 36 | "system": "http://cancerstaging.org", 37 | "code": "cT3" 38 | } 39 | ] 40 | }, 41 | "focus": [ 42 | { 43 | "reference": "urn:uuid:example-condition-id", 44 | "type": "Condition" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /test/templates/fixtures/nodes-category-pathologic-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-tnm-regional-nodes-category" 7 | ] 8 | }, 9 | "status": "final", 10 | "category": [ 11 | { 12 | "coding": [ 13 | { 14 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 15 | "code": "laboratory" 16 | } 17 | ] 18 | } 19 | ], 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://loinc.org", 24 | "code": "21900-6" 25 | } 26 | ] 27 | }, 28 | "subject": { 29 | "reference": "urn:uuid:example-mrn", 30 | "type": "Patient" 31 | }, 32 | "effectiveDateTime": "2020-01-01", 33 | "valueCodeableConcept": { 34 | "coding": [ 35 | { 36 | "system": "http://cancerstaging.org", 37 | "code": "pN3" 38 | } 39 | ] 40 | }, 41 | "focus": [ 42 | { 43 | "reference": "urn:uuid:example-condition-id", 44 | "type": "Condition" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /test/templates/fixtures/tumor-category-pathologic-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-tnm-primary-tumor-category" 7 | ] 8 | }, 9 | "status": "final", 10 | "category": [ 11 | { 12 | "coding": [ 13 | { 14 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 15 | "code": "laboratory" 16 | } 17 | ] 18 | } 19 | ], 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://loinc.org", 24 | "code": "21899-0" 25 | } 26 | ] 27 | }, 28 | "subject": { 29 | "reference": "urn:uuid:example-mrn", 30 | "type": "Patient" 31 | }, 32 | "effectiveDateTime": "2020-01-01", 33 | "valueCodeableConcept": { 34 | "coding": [ 35 | { 36 | "system": "http://cancerstaging.org", 37 | "code": "pT3" 38 | } 39 | ] 40 | }, 41 | "focus": [ 42 | { 43 | "reference": "urn:uuid:example-condition-id", 44 | "type": "Condition" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /test/templates/fixtures/metastases-category-clinical-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-tnm-distant-metastases-category" 7 | ] 8 | }, 9 | "status": "final", 10 | "category": [ 11 | { 12 | "coding": [ 13 | { 14 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 15 | "code": "survey" 16 | } 17 | ] 18 | } 19 | ], 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://loinc.org", 24 | "code": "21907-1" 25 | } 26 | ] 27 | }, 28 | "subject": { 29 | "reference": "urn:uuid:example-mrn", 30 | "type": "Patient" 31 | }, 32 | "effectiveDateTime": "2020-01-01", 33 | "valueCodeableConcept": { 34 | "coding": [ 35 | { 36 | "system": "http://cancerstaging.org", 37 | "code": "cM0" 38 | } 39 | ] 40 | }, 41 | "focus": [ 42 | { 43 | "reference": "urn:uuid:example-condition-id", 44 | "type": "Condition" 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /test/templates/fixtures/metastases-category-pathologic-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Observation", 3 | "id": "example-id", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-tnm-distant-metastases-category" 7 | ] 8 | }, 9 | "status": "final", 10 | "category": [ 11 | { 12 | "coding": [ 13 | { 14 | "system": "http://terminology.hl7.org/CodeSystem/observation-category", 15 | "code": "laboratory" 16 | } 17 | ] 18 | } 19 | ], 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://loinc.org", 24 | "code": "21901-4" 25 | } 26 | ] 27 | }, 28 | "subject": { 29 | "reference": "urn:uuid:example-mrn", 30 | "type": "Patient" 31 | }, 32 | "effectiveDateTime": "2020-01-01", 33 | "valueCodeableConcept": { 34 | "coding": [ 35 | { 36 | "system": "http://cancerstaging.org", 37 | "code": "pM0" 38 | } 39 | ] 40 | }, 41 | "focus": [ 42 | { 43 | "reference": "urn:uuid:example-condition-id", 44 | "type": "Condition" 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /src/templates/snippets/index.js: -------------------------------------------------------------------------------- 1 | const { coding } = require('./coding'); 2 | const { valueX } = require('./valueX'); 3 | const { reference } = require('./reference'); 4 | const { meta, narrative } = require('./resource'); 5 | const { extensionArr, dataAbsentReasonExtension } = require('./extension'); 6 | const { effectiveX } = require('./effectiveX'); 7 | const { identifier, identifierArr } = require('./identifier'); 8 | const { bodySiteTemplate } = require('./bodySiteTemplate'); 9 | const { stagingMethodTemplate } = require('./cancerStaging'); 10 | const { medicationTemplate } = require('./medication'); 11 | const { subjectTemplate } = require('./subject'); 12 | const { treatmentReasonTemplate } = require('./treatmentReason'); 13 | const { periodTemplate } = require('./period'); 14 | 15 | module.exports = { 16 | bodySiteTemplate, 17 | coding, 18 | dataAbsentReasonExtension, 19 | effectiveX, 20 | extensionArr, 21 | identifier, 22 | identifierArr, 23 | medicationTemplate, 24 | meta, 25 | narrative, 26 | periodTemplate, 27 | reference, 28 | stagingMethodTemplate, 29 | subjectTemplate, 30 | treatmentReasonTemplate, 31 | valueX, 32 | }; 33 | -------------------------------------------------------------------------------- /test/extractors/fixtures/csv-ctc-adverse-event-module-response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "mrn": "mrn-1", 4 | "adverseeventid": "adverseEventId-1", 5 | "adverseeventcode": "109006", 6 | "adverseeventcodesystem": "code-system", 7 | "adverseeventcodeversion": "code-version", 8 | "adverseeventdisplaytext": "Anxiety disorder of childhood OR adolescence", 9 | "adverseeventtext": "event-text", 10 | "suspectedcauseid": "procedure-id", 11 | "suspectedcausetype": "Procedure", 12 | "seriousness": "serious", 13 | "seriousnesscodesystem": "http://terminology.hl7.org/CodeSystem/adverse-event-seriousness", 14 | "seriousnessdisplaytext": "Serious", 15 | "category": "product-use-error", 16 | "categorycodesystem": "http://terminology.hl7.org/CodeSystem/adverse-event-category", 17 | "categorydisplaytext": "Product Use Error", 18 | "studyid": "researchId-1", 19 | "effectivedate": "12-09-1994", 20 | "recordeddate": "12-09-1994", 21 | "grade": "1", 22 | "resolveddate": "2021-12-01", 23 | "seriousnessoutcome": "C113380", 24 | "expectation": "C41333", 25 | "actor": "practitioner-id", 26 | "functioncode": "INF" 27 | } 28 | ] -------------------------------------------------------------------------------- /test/sample-client-data/adverse-event-information.csv: -------------------------------------------------------------------------------- 1 | mrn,adverseEventId,adverseEventCode,adverseEventCodeSystem,adverseEventDisplayText,suspectedCauseId,suspectedCauseType,seriousness,seriousnessCodeSystem,seriousnessDisplayText,category,categoryCodeSystem,categoryDisplayText,severity,actuality,studyId,effectiveDate,recordedDate 2 | 123,adverseEventId-1,109006,code-system,Anxiety disorder of childhood OR adolescence,procedure-id,Procedure,serious,http://terminology.hl7.org/CodeSystem/adverse-event-seriousness,Serious,product-use-error|product-quality|wrong-rate,http://terminology.hl7.org/CodeSystem/adverse-event-category|http://snomed.info/sct|http://terminology.hl7.org/CodeSystem/adverse-event-category,Product Use Error|Product Quality|Wrong Rate,severe,actual,researchId-1,12-09-1994,12-09-1994 3 | 456,adverseEventId-2,134006,http://snomed.info/sct,Decreased hair growth,medicationId-1,Medication,non-serious,http://terminology.hl7.org/CodeSystem/adverse-event-seriousness,Non-serious,product-quality|wrong-rate,http://terminology.hl7.org/CodeSystem/adverse-event-category|,Product Quality|,mild,potential,researchId-2,12-10-1995,12-10-1995 4 | 789,adverseEventId-3,150003,,,,,,,,product-use-error,,,,,,12-09-1994, -------------------------------------------------------------------------------- /src/extractors/FHIRPatientExtractor.js: -------------------------------------------------------------------------------- 1 | const { BaseFHIRExtractor } = require('./BaseFHIRExtractor'); 2 | const { maskPatientData } = require('../helpers/patientUtils.js'); 3 | 4 | class FHIRPatientExtractor extends BaseFHIRExtractor { 5 | constructor({ 6 | baseFhirUrl, requestHeaders, version, mask = [], searchParameters, 7 | }) { 8 | super({ baseFhirUrl, requestHeaders, version, searchParameters }); 9 | this.resourceType = 'Patient'; 10 | this.mask = mask; 11 | } 12 | 13 | // Override default behavior for PatientExtractor; just use MRN directly 14 | // eslint-disable-next-line class-methods-use-this 15 | async parametrizeArgsForFHIRModule({ mrn }) { 16 | return { 17 | identifier: `MRN|${mrn}`, 18 | }; 19 | } 20 | 21 | async get(argumentObject) { 22 | const bundle = await super.get(argumentObject); 23 | // mask specified fields in the patient data 24 | if (typeof this.mask === 'string' && this.mask === 'all') { 25 | maskPatientData(bundle, [], true); 26 | } else if (this.mask.length > 0) maskPatientData(bundle, this.mask); 27 | return bundle; 28 | } 29 | } 30 | 31 | module.exports = { 32 | FHIRPatientExtractor, 33 | }; 34 | -------------------------------------------------------------------------------- /test/application/app.test.js: -------------------------------------------------------------------------------- 1 | const rewire = require('rewire'); 2 | 3 | const app = rewire('../../src/application/app.js'); 4 | const checkInputAndConfig = app.__get__('checkInputAndConfig'); 5 | 6 | describe('App Tests', () => { 7 | describe('checkInputAndConfig', () => { 8 | const config = { patientIdCsvPath: '', extractors: [] }; 9 | it('should throw error when fromDate is invalid.', () => { 10 | expect(() => checkInputAndConfig(config, '2020-06-31')).toThrowError('-f/--from-date is not a valid date.'); 11 | }); 12 | it('should throw error when toDate is invalid date.', () => { 13 | expect(() => checkInputAndConfig(config, '2020-06-30', '2020-06-31')).toThrowError('-t/--to-date is not a valid date.'); 14 | }); 15 | it('should throw error when config is not valid', () => { 16 | expect(() => checkInputAndConfig({})) 17 | .toThrowError('Error(s) found in config file: config should have required property \'patientIdCsvPath\', config should have required property \'extractors\''); 18 | }); 19 | it('should not throw error when all args are valid', () => { 20 | expect(() => checkInputAndConfig(config, '2020-06-01', '2020-06-30')).not.toThrowError(); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/modules/fixtures/patient-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Patient", 3 | "id": "mCODEPatientExample01", 4 | "meta": { 5 | "profile": ["http://hl7.org/fhir/us/mcode/StructureDefinition/obf-Patient"] 6 | }, 7 | "identifier": [ 8 | { 9 | "use": "usual", 10 | "system": "http://example.com/clinicaltrialids", 11 | "type": { 12 | "text": "Clinical Trial Research ID" 13 | }, 14 | "value": "AFT-05" 15 | }, 16 | { 17 | "use": "usual", 18 | "system": "http://example.com/clinicaltrialsubjectids", 19 | "type": { 20 | "text": "Clinical Trial Subject ID" 21 | }, 22 | "value": "AFT-05-patient-01" 23 | }, 24 | { 25 | "use": "usual", 26 | "type": { 27 | "coding": [ 28 | { 29 | "system": "http://hl7.org/fhir/v2/0203", 30 | "code": "MR", 31 | "display": "Medical Record Number" 32 | } 33 | ], 34 | "text": "Medical Record Number" 35 | }, 36 | "system": "http://hospital.example.org", 37 | "value": "m123" 38 | } 39 | ], 40 | "name": [ 41 | { 42 | "family": "Anyperson", 43 | "given": ["John", "B."] 44 | } 45 | ], 46 | "gender": "male" 47 | } 48 | -------------------------------------------------------------------------------- /test/templates/researchStudy.test.js: -------------------------------------------------------------------------------- 1 | const { isValidFHIR } = require('../../src/helpers/fhirUtils'); 2 | const validResearchStudy = require('./fixtures/research-study-resource.json'); 3 | const { researchStudyTemplate } = require('../../src/templates/ResearchStudyTemplate'); 4 | 5 | const VALID_DATA = { 6 | id: 'id-for-research-study', 7 | trialStatus: 'active', 8 | trialResearchID: 'AFT1235', 9 | clinicalSiteID: 'EXAMPLE_SITE_ID', 10 | clinicalSiteSystem: 'EXAMPLE_SITE_SYSTEM', 11 | trialResearchSystem: 'example-system', 12 | }; 13 | 14 | const INVALID_DATA = { 15 | // Omitting 'trialResearchID' field which is required 16 | trialStatus: 'completed', 17 | trialResearchID: null, 18 | }; 19 | 20 | describe('test ResearchStudy template', () => { 21 | test('valid data passed into template should generate valid FHIR resource', () => { 22 | const generatedResearchStudy = researchStudyTemplate(VALID_DATA); 23 | // Relevant fields should match the valid FHIR 24 | expect(generatedResearchStudy).toEqual(validResearchStudy); 25 | expect(isValidFHIR(generatedResearchStudy)).toBeTruthy(); 26 | }); 27 | 28 | test('invalid data should throw an error', () => { 29 | expect(() => researchStudyTemplate(INVALID_DATA)).toThrow(Error); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/helpers/dateUtils.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const logger = require('./logger'); 3 | 4 | moment.suppressDeprecationWarnings = true; // We handle invalid date formats 5 | const dateFormat = 'YYYY-MM-DD'; 6 | const dateTimeFormat = 'YYYY-MM-DDTHH:mm:ssZ'; 7 | 8 | function formatDate(date) { 9 | const parsedDate = moment.utc(date); 10 | if (!parsedDate.isValid()) { 11 | logger.warn(`Invalid date provided: ${date}. Provided value will be used.`); 12 | return date; // Use the provided date rather than 'Invalid date' 13 | } 14 | 15 | return parsedDate.format(dateFormat); 16 | } 17 | 18 | function formatDateTime(date) { 19 | const parsedDate = moment.utc(date); 20 | if (!parsedDate.isValid()) { 21 | logger.warn(`Invalid date provided: ${date}. Provided value will be used.`); 22 | return date; // Use the provided date rather than 'Invalid date' 23 | } 24 | 25 | // HACKY: If there is a minute, second, or hour, then we should treat this as a datetimestamp 26 | if (parsedDate.hour() || parsedDate.minute() || parsedDate.second()) { 27 | return parsedDate.format(dateTimeFormat); 28 | } 29 | return parsedDate.format(dateFormat); 30 | } 31 | 32 | module.exports = { 33 | formatDate, 34 | formatDateTime, 35 | dateFormat, 36 | dateTimeFormat, 37 | }; 38 | -------------------------------------------------------------------------------- /test/helpers/dateUtils.test.js: -------------------------------------------------------------------------------- 1 | const { formatDate, formatDateTime } = require('../../src/helpers/dateUtils'); 2 | 3 | test('formatDate reformats date', () => { 4 | expect(formatDate('04/12/19')).toEqual('2019-04-12'); 5 | expect(formatDate('2019-04-12T18:00:00')).toEqual('2019-04-12'); 6 | }); 7 | 8 | test('formatDate does not reformat invalid date', () => { 9 | expect(formatDate('102/30/19')).toEqual('102/30/19'); 10 | }); 11 | 12 | test('formatDateTime reformats date with a time if provided', () => { 13 | expect(formatDateTime('04/12/19')).toEqual('2019-04-12'); 14 | expect(formatDateTime('2019-04-12T08:00:00')).toEqual('2019-04-12T08:00:00+00:00'); 15 | }); 16 | 17 | test('formatDateTime respects timeZone information if provided', () => { 18 | const dateTimeWithZone = '2020-05-08T18:00:00+00:00'; 19 | expect(formatDateTime(dateTimeWithZone)).toEqual('2020-05-08T18:00:00+00:00'); 20 | const secondDate = '2020-05-08T18:00:00Z'; 21 | expect(formatDateTime(secondDate)).toEqual('2020-05-08T18:00:00+00:00'); 22 | const thirdDate = '2019-04-12T08:00:00+01:00'; 23 | expect(formatDateTime(thirdDate)).toEqual('2019-04-12T07:00:00+00:00'); 24 | }); 25 | 26 | test('formatDateTime does not reformat invalid date', () => { 27 | expect(formatDateTime('2019-104-12T18:00:00')).toEqual('2019-104-12T18:00:00'); 28 | }); 29 | -------------------------------------------------------------------------------- /test/templates/fixtures/maximal-medication-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "MedicationAdministration", 3 | "id": "medicationId-1", 4 | "meta": { 5 | "profile": [ 6 | "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-cancer-related-medication-administration" 7 | ] 8 | }, 9 | "extension": [ 10 | { 11 | "url": "http://hl7.org/fhir/us/mcode/StructureDefinition/mcode-treatment-intent", 12 | "valueCodeableConcept": { 13 | "coding": [ 14 | { 15 | "system": "http://snomed.info/sct", 16 | "code": "example-code" 17 | } 18 | ] 19 | } 20 | } 21 | ], 22 | "status": "example-status", 23 | "medicationCodeableConcept": { 24 | "coding": [ 25 | { 26 | "system": "example-code-system", 27 | "code": "example-code", 28 | "display": "Example Text" 29 | } 30 | ] 31 | }, 32 | "subject": { 33 | "reference": "urn:uuid:mrn-1", 34 | "type": "Patient" 35 | }, 36 | "effectivePeriod": { 37 | "start": "2020-01-01", 38 | "end": "2020-02-01" 39 | }, 40 | "reasonCode": [ 41 | { 42 | "coding": [ 43 | { 44 | "system": "example-code-system", 45 | "code": "example-reason", 46 | "display": "Example Text" 47 | } 48 | ] 49 | } 50 | ] 51 | } -------------------------------------------------------------------------------- /test/templates/fixtures/minimal-careplan-resource.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "CarePlan", 3 | "id": "test-id", 4 | "meta": { 5 | "profile": [ 6 | "http://mcodeinitiative.org/codex/us/icare/StructureDefinition/icare-care-plan-with-review" 7 | ] 8 | }, 9 | "text": { 10 | "status": "additional", 11 | "div": "