Skip to content

Commit d66de8c

Browse files
committed
New simple extensions added
1 parent 183a7ad commit d66de8c

11 files changed

+333
-23
lines changed

docs/CSV_Templates.xlsx

79 KB
Binary file not shown.

docs/ctc-adverse-event.csv

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
mrn,adverseEventId,adverseEventCode,adverseEventCodeSystem,adverseEventCodeVersion,adverseEventDisplayText,adverseEventText,suspectedCauseId,suspectedCauseType,seriousness,seriousnessCodeSystem,seriousnessDisplayText,category,categoryCodeSystem,categoryDisplayText,studyId,effectiveDate,recordedDate,grade
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,
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|category-code,code-system|code-system,category-display|category-display,id,1994-12-09,1994-12-09,3
4-
mrn-minimal-example,,code-from-default-system,,,,,,,,,,,,,,1994-12-09,,1
1+
mrn,adverseEventId,adverseEventCode,adverseEventCodeSystem,adverseEventCodeVersion,adverseEventDisplayText,adverseEventText,suspectedCauseId,suspectedCauseType,seriousness,seriousnessCodeSystem,seriousnessDisplayText,category,categoryCodeSystem,categoryDisplayText,studyId,effectiveDate,recordedDate,grade,expectation,resolvedDate,seriousnessOutcome
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
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
4+
mrn-minimal-example,,code-from-default-system,,,,,,,,,,,,,1994-12-09,,1,,,,,

src/extractors/CSVCTCAdverseEventExtractor.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
const path = require('path');
12
const { BaseCSVExtractor } = require('./BaseCSVExtractor');
23
const { generateMcodeResources } = require('../templates');
34
const { getEmptyBundle } = require('../helpers/fhirUtils');
45
const { getPatientFromContext } = require('../helpers/contextUtils');
56
const { formatDateTime } = require('../helpers/dateUtils');
7+
const { getDisplayFromConcept } = require('../helpers/valueSetUtils');
68
const { ctcAEGradeCodeToTextLookup } = require('../helpers/lookups/ctcAdverseEventLookup');
79
const logger = require('../helpers/logger');
810

@@ -29,6 +31,9 @@ function formatData(adverseEventData, patientId) {
2931
effectivedate: effectiveDate,
3032
recordeddate: recordedDate,
3133
grade,
34+
expectation,
35+
resolveddate: resolvedDate,
36+
seriousnessoutcome: seriousnessOutcome,
3237
} = data;
3338

3439
if (!(adverseEventCode && effectiveDate && grade)) {
@@ -43,7 +48,6 @@ function formatData(adverseEventData, patientId) {
4348
throw new Error('A category attribute on the adverse event is missing a corresponding categoryCodeSystem or categoryDisplayText value.');
4449
}
4550

46-
4751
return {
4852
...(adverseEventId && { id: adverseEventId }),
4953
subjectId: patientId,
@@ -67,6 +71,25 @@ function formatData(adverseEventData, patientId) {
6771
effectiveDateTime: formatDateTime(effectiveDate),
6872
recordedDateTime: !recordedDate ? null : formatDateTime(recordedDate),
6973
grade: { code: grade, display: ctcAEGradeCodeToTextLookup[grade] },
74+
resolvedDate: !resolvedDate ? null : formatDateTime(resolvedDate),
75+
expectation: !expectation ? null : {
76+
code: expectation,
77+
system: 'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl',
78+
display: getDisplayFromConcept(
79+
path.resolve(__dirname, '..', 'helpers', 'valueSets', 'adverse-event-expectation-value-set.json'),
80+
expectation,
81+
'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl',
82+
),
83+
},
84+
seriousnessOutcome: !seriousnessOutcome ? null : {
85+
code: seriousnessOutcome,
86+
system: 'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl',
87+
display: getDisplayFromConcept(
88+
path.resolve(__dirname, '..', 'helpers', 'valueSets', 'adverse-event-seriousness-outcome-value-set.json'),
89+
seriousnessOutcome,
90+
'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl',
91+
),
92+
},
7093
};
7194
});
7295
}

src/helpers/valueSetUtils.js

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,37 @@ function loadVs(absoluteFilepath, typeOfVS) {
3535
}
3636
}
3737

38+
function getConceptFromVSExpansion(valueSet, code, codeSystem) {
39+
if (!code || !codeSystem || !valueSet) return undefined;
40+
return valueSet.expansion.contains.find((containsItem) => containsItem
41+
&& containsItem.system
42+
&& code === containsItem.code
43+
&& codeSystem === containsItem.system);
44+
}
45+
46+
function getConceptFromVSCompose(valueSet, code, codeSystem) {
47+
if (!code || !codeSystem || !valueSet) return undefined;
48+
const includeItem = valueSet.compose.include.find((item) => item
49+
&& item.system
50+
&& item.system === codeSystem
51+
&& item.concept);
52+
if (!includeItem) return undefined;
53+
return includeItem.concept.find((concept) => concept.code === code);
54+
}
55+
56+
function getDisplayFromConcept(pathToValueSet, code, codeSystem) {
57+
if (!code || !codeSystem || !pathToValueSet) return undefined;
58+
const valueSet = loadVs(pathToValueSet, vsTypes.json);
59+
let concept;
60+
if (valueSet.expansion) {
61+
// If valueSet has expansion, we only need to check these codes
62+
concept = getConceptFromVSExpansion(valueSet, code, codeSystem);
63+
return (concept && concept.display) ? concept.display : undefined;
64+
}
65+
concept = getConceptFromVSCompose(valueSet, code, codeSystem);
66+
return (concept && concept.display) ? concept.display : undefined;
67+
}
68+
3869
/**
3970
* Check if code is in value set
4071
* @param {string} code value to look for in a valueset
@@ -43,22 +74,21 @@ function loadVs(absoluteFilepath, typeOfVS) {
4374
* @param {string} typeOfVS the file type of the value set to be searched
4475
* @return {boolean} true if condition is in valueSet's compose block or expansion block
4576
*/
46-
const checkCodeInVs = (code, codeSystem, valueSetFilePath, typeOfVS = vsTypes.json) => {
77+
const checkCodeInVs = (
78+
code,
79+
codeSystem,
80+
valueSetFilePath,
81+
typeOfVS = vsTypes.json,
82+
) => {
4783
const valueSet = loadVs(valueSetFilePath, typeOfVS);
4884
let inVSExpansion = false;
4985
let inVSCompose = false;
5086
if (valueSet.expansion) {
5187
// If valueSet has expansion, we only need to check these codes
52-
inVSExpansion = valueSet.expansion.contains.some((containsItem) => {
53-
if (!code || !codeSystem || !containsItem || !containsItem.system) return false;
54-
return code === containsItem.code && codeSystem === containsItem.system;
55-
});
88+
inVSExpansion = (getConceptFromVSExpansion(valueSet, code, codeSystem) !== undefined);
5689
} else {
5790
// Checks if code is in any of the valueSet.compose.include arrays
58-
inVSCompose = valueSet.compose.include.some((includeItem) => {
59-
if (!code || !codeSystem || !includeItem || !includeItem.system || !includeItem.concept) return false;
60-
return includeItem.system === codeSystem && includeItem.concept.map((concept) => concept.code).includes(code);
61-
});
91+
inVSCompose = (getConceptFromVSCompose(valueSet, code, codeSystem) !== undefined);
6292
}
6393
return inVSCompose || inVSExpansion;
6494
};
@@ -68,4 +98,7 @@ module.exports = {
6898
loadJsonVs,
6999
loadVs,
70100
checkCodeInVs,
101+
getDisplayFromConcept,
102+
getConceptFromVSCompose,
103+
getConceptFromVSExpansion,
71104
};
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"resourceType": "ValueSet",
3+
"id": "adverse-event-expectation-value-set",
4+
"text": {
5+
"status": "generated",
6+
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><ul><li>Include these codes as defined in <code>http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl</code><table class=\"none\"><tr><td style=\"white-space:nowrap\"><b>Code</b></td><td><b>Display</b></td></tr><tr><td>C41333</td><td>Expected Adverse Event</td></tr><tr><td>C41334</td><td>Unexpected Adverse Event</td></tr></table></li></ul></div>"
7+
},
8+
"url": "http://hl7.org/fhir/us/ctcae/ValueSet/adverse-event-expectation-value-set",
9+
"version": "0.0.1",
10+
"name": "AdverseEventExpectationVS",
11+
"title": "Adverse Event Expectation Value Set",
12+
"status": "active",
13+
"date": "2021-12-03T16:46:43+00:00",
14+
"publisher": "HL7 International Clinical Interoperability Council",
15+
"contact": [
16+
{
17+
"name": "HL7 International Clinical Interoperability Council",
18+
"telecom": [
19+
{
20+
"system": "url",
21+
"value": "http://www.mcodeinitiative.org"
22+
}
23+
]
24+
}
25+
],
26+
"description": "An expected adverse event is one whose nature and severity have been previously observed, identified in nature, severity, or frequency, and documented in the investigator brochure, investigational plan, protocol, current consent form, scientific publication, or in other relevant and reliable document. An unexpected adverse event is one that has not been previously observed, whether or not the event was anticipated because of the pharmacologic properties of the study agent or the nature of the medical procedure. This includes events that are more serious than expected or occur more frequently than expected, particularly, any adverse experience, the nature, severity or frequency of which is not consistent with the product label, or with the current investigator brochure for investigational agent; or with the risk information described in the investigational plan or protocol or consent form (NCI Thesaurus).",
27+
"jurisdiction": [
28+
{
29+
"coding": [
30+
{
31+
"system": "urn:iso:std:iso:3166",
32+
"code": "US",
33+
"display": "United States of America"
34+
}
35+
]
36+
}
37+
],
38+
"compose": {
39+
"include": [
40+
{
41+
"system": "http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl",
42+
"concept": [
43+
{
44+
"code": "C41333",
45+
"display": "Expected Adverse Event"
46+
},
47+
{
48+
"code": "C41334",
49+
"display": "Unexpected Adverse Event"
50+
}
51+
]
52+
}
53+
]
54+
}
55+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"resourceType": "ValueSet",
3+
"id": "adverse-event-seriousness-outcome-value-set",
4+
"text": {
5+
"status": "generated",
6+
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><ul><li>Include these codes as defined in <code>http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl</code><table class=\"none\"><tr><td style=\"white-space:nowrap\"><b>Code</b></td><td><b>Display</b></td></tr><tr><td>C84266</td><td>Life Threatening Adverse Event</td></tr><tr><td>C48275</td><td>Death Related to Adverse Event</td></tr><tr><td>C113380</td><td>Disabling Adverse Event</td></tr><tr><td>C83052</td><td>Adverse Event associated with Hospitalization</td></tr><tr><td>C2849</td><td>Congenital Abnormality</td></tr><tr><td>C52668</td><td>Intervention Required</td></tr></table></li></ul></div>"
7+
},
8+
"url": "http://hl7.org/fhir/us/ctcae/ValueSet/adverse-event-seriousness-outcome-value-set",
9+
"version": "0.0.1",
10+
"name": "AdverseEventSeriousnessOutcomeVS",
11+
"title": "Adverse Event Seriousness Outcome Value Set",
12+
"status": "active",
13+
"date": "2021-12-03T16:46:43+00:00",
14+
"publisher": "HL7 International Clinical Interoperability Council",
15+
"contact": [
16+
{
17+
"name": "HL7 International Clinical Interoperability Council",
18+
"telecom": [
19+
{
20+
"system": "url",
21+
"value": "http://www.mcodeinitiative.org"
22+
}
23+
]
24+
}
25+
],
26+
"description": "The outcome of a serious adverse event",
27+
"jurisdiction": [
28+
{
29+
"coding": [
30+
{
31+
"system": "urn:iso:std:iso:3166",
32+
"code": "US",
33+
"display": "United States of America"
34+
}
35+
]
36+
}
37+
],
38+
"compose": {
39+
"include": [
40+
{
41+
"system": "http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl",
42+
"concept": [
43+
{
44+
"code": "C84266",
45+
"display": "Life Threatening Adverse Event"
46+
},
47+
{
48+
"code": "C48275",
49+
"display": "Death Related to Adverse Event"
50+
},
51+
{
52+
"code": "C113380",
53+
"display": "Disabling Adverse Event"
54+
},
55+
{
56+
"code": "C83052",
57+
"display": "Adverse Event associated with Hospitalization"
58+
},
59+
{
60+
"code": "C2849",
61+
"display": "Congenital Abnormality"
62+
},
63+
{
64+
"code": "C52668",
65+
"display": "Intervention Required"
66+
}
67+
]
68+
}
69+
]
70+
}}

src/templates/CTCAdverseEventTemplate.js

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { coding, reference, extensionArr } = require('./snippets');
1+
const { coding, reference, extensionArr, valueX } = require('./snippets');
22
const {
33
ifAllArgsObj, ifSomeArgsObj, ifAllArgs, ifSomeArgs, ifSomeArgsArr,
44
} = require('../helpers/templateUtils');
@@ -73,9 +73,39 @@ function gradeTemplate(grade) {
7373
};
7474
}
7575

76+
function resolvedDateTemplate(resolvedDate) {
77+
return {
78+
url: 'http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-resolved-date',
79+
...valueX(resolvedDate, 'valueDateTime'),
80+
};
81+
}
82+
83+
function expectationTemplate(expectation) {
84+
return {
85+
url: 'http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-expectation',
86+
valueCodeableConcept: {
87+
coding: [
88+
coding(expectation),
89+
],
90+
},
91+
};
92+
}
93+
94+
function seriousnessOutcomeTemplate(seriousnessOutcome) {
95+
return {
96+
url: 'http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-seriousness-outcome',
97+
valueCodeableConcept: {
98+
coding: [
99+
coding(seriousnessOutcome),
100+
],
101+
},
102+
};
103+
}
104+
105+
76106
function CTCAdverseEventTemplate({
77107
id, subjectId, code, system, version, display, text, suspectedCauseId, suspectedCauseType, seriousnessCode, seriousnessCodeSystem, seriousnessDisplayText, category,
78-
studyId, effectiveDateTime, recordedDateTime, grade,
108+
studyId, effectiveDateTime, recordedDateTime, grade, resolvedDate, expectation, seriousnessOutcome,
79109
}) {
80110
if (!(subjectId && code && system && effectiveDateTime && grade)) {
81111
throw Error('Trying to render an AdverseEventTemplate, but a required argument is messing; ensure that subjectId, code, system, grade, and effectiveDateTime are all present');
@@ -84,7 +114,12 @@ function CTCAdverseEventTemplate({
84114
return {
85115
resourceType: 'AdverseEvent',
86116
id,
87-
...extensionArr(gradeTemplate(grade)),
117+
...extensionArr(
118+
gradeTemplate(grade),
119+
resolvedDate ? resolvedDateTemplate(resolvedDate) : null,
120+
expectation ? expectationTemplate(expectation) : null,
121+
seriousnessOutcome ? seriousnessOutcomeTemplate(seriousnessOutcome) : null,
122+
),
88123
subject: reference({ id: subjectId, resourceType: 'Patient' }),
89124
...ifSomeArgs(eventTemplate)({ code, system, version, display }, text),
90125
...ifAllArgsObj(suspectedCauseTemplate)({ suspectedCauseId, suspectedCauseType }),

test/extractors/fixtures/csv-ctc-adverse-event-bundle.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,34 @@
1919
}
2020
]
2121
}
22+
},
23+
{
24+
"url": "http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-resolved-date",
25+
"valueDateTime": "2021-12-01"
26+
},
27+
{
28+
"url": "http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-expectation",
29+
"valueCodeableConcept": {
30+
"coding": [
31+
{
32+
"system": "http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl",
33+
"code": "C41333",
34+
"display": "Expected Adverse Event"
35+
}
36+
]
37+
}
38+
},
39+
{
40+
"url": "http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-seriousness-outcome",
41+
"valueCodeableConcept": {
42+
"coding": [
43+
{
44+
"system": "http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl",
45+
"code": "C113380",
46+
"display": "Disabling Adverse Event"
47+
}
48+
]
49+
}
2250
}
2351
],
2452
"subject": {

test/extractors/fixtures/csv-ctc-adverse-event-module-response.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
"studyid": "researchId-1",
1919
"effectivedate": "12-09-1994",
2020
"recordeddate": "12-09-1994",
21-
"grade": "1"
21+
"grade": "1",
22+
"resolveddate": "2021-12-01",
23+
"seriousnessoutcome": "C113380",
24+
"expectation": "C41333"
2225
}
2326
]

0 commit comments

Comments
 (0)