Skip to content

Commit 469efde

Browse files
authored
Merge pull request #171 from mcode/ctcae-complex-extension
Participation Extension added
2 parents d81800d + 3658f33 commit 469efde

11 files changed

+203
-11
lines changed

docs/CSV_Templates.xlsx

130 Bytes
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,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,,,,,
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,,,,,,

src/extractors/CSVCTCAdverseEventExtractor.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,14 @@ function formatData(adverseEventData, patientId) {
3434
expectation,
3535
resolveddate: resolvedDate,
3636
seriousnessoutcome: seriousnessOutcome,
37+
actor,
38+
functioncode: functionCode,
3739
} = data;
3840

3941
if (!(adverseEventCode && effectiveDate && grade)) {
4042
throw new Error('The adverse event is missing an expected attribute. Adverse event code, effective date, and grade are all required.');
43+
} else if (functionCode && !actor) {
44+
throw new Error('The adverse event is missing an expected attribute. Adverse event actor is a required element when a functionCode value is included.');
4145
}
4246

4347
const categoryCodes = category.split('|');
@@ -90,6 +94,16 @@ function formatData(adverseEventData, patientId) {
9094
'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl',
9195
),
9296
},
97+
actor,
98+
functionCode: !functionCode ? null : {
99+
code: functionCode,
100+
system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType',
101+
display: getDisplayFromConcept(
102+
path.resolve(__dirname, '..', 'helpers', 'valueSets', 'adverse-event-participant-function-value-set.json'),
103+
functionCode,
104+
'http://terminology.hl7.org/CodeSystem/v3-ParticipationType',
105+
),
106+
},
93107
};
94108
});
95109
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"resourceType": "ValueSet",
3+
"id": "adverse-event-participant-function-value-set",
4+
"text": {
5+
"status": "extensions",
6+
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\"><ul><li>Include these codes as defined in <a href=\"http://terminology.hl7.org/2.1.0/CodeSystem-v3-ParticipationType.html\"><code>http://terminology.hl7.org/CodeSystem/v3-ParticipationType</code></a><table class=\"none\"><tr><td style=\"white-space:nowrap\"><b>Code</b></td><td><b>Display</b></td><td><b>Definition</b></td></tr><tr><td><a href=\"http://terminology.hl7.org/2.1.0/CodeSystem-v3-ParticipationType.html#v3-ParticipationType-INF\">INF</a></td><td>Informant</td><td>A source of reported information (e.g., a next of kin who answers questions about the patient's history). For history questions, the patient is logically an informant, yet the informant of history questions is implicitly the subject.</td></tr><tr><td><a href=\"http://terminology.hl7.org/2.1.0/CodeSystem-v3-ParticipationType.html#v3-ParticipationType-PART\">PART</a></td><td>Participation</td><td>Indicates that the target of the participation is involved in some manner in the act, but does not qualify how.</td></tr><tr><td><a href=\"http://terminology.hl7.org/2.1.0/CodeSystem-v3-ParticipationType.html#v3-ParticipationType-WIT\">WIT</a></td><td>Witness</td><td>Only with service events. A person witnessing the action happening without doing anything. A witness is not necessarily aware, much less approves of anything stated in the service event. Example for a witness is students watching an operation or an advanced directive witness.</td></tr><tr><td><a href=\"http://terminology.hl7.org/2.1.0/CodeSystem-v3-ParticipationType.html#v3-ParticipationType-AUT\">AUT</a></td><td>Author (originator)</td><td>**Definition:** A party that originates the Act and therefore has responsibility for the information given in the Act and ownership of this Act.<br/><br/>**Example:** the report writer, the person writing the act definition, the guideline author, the placer of an order, the EKG cart (device) creating a report etc. Every Act should have an author. Authorship is regardless of mood always actual authorship.<br/><br/>Examples of such policies might include:<br/><br/> * The author and anyone they explicitly delegate may update the report;<br/> * All administrators within the same clinic may cancel and reschedule appointments created by other administrators within that clinic;<br/><br/>A party that is neither an author nor a party who is extended authorship maintenance rights by policy, may only amend, reverse, override, replace, or follow up in other ways on this Act, whereby the Act remains intact and is linked to another Act authored by that other party.</td></tr><tr><td><a href=\"http://terminology.hl7.org/2.1.0/CodeSystem-v3-ParticipationType.html#v3-ParticipationType-AUTHEN\">AUTHEN</a></td><td>Authenticator</td><td>A verifier who attests to the accuracy of an act, but who does not have privileges to legally authenticate the act. An example would be a resident physician who sees a patient and dictates a note, then later signs it. Their signature constitutes an authentication.</td></tr></table></li></ul></div>"
7+
},
8+
"url": "http://hl7.org/fhir/us/ctcae/ValueSet/adverse-event-participant-function-value-set",
9+
"version": "0.0.1",
10+
"name": "AdverseEventParticipantFunctionVS",
11+
"title": "Adverse Event Participant Function",
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": "This value set includes codes that describe the type of involvement of the actor in the 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://terminology.hl7.org/CodeSystem/v3-ParticipationType",
42+
"concept": [
43+
{
44+
"code": "INF",
45+
"display": "Informant"
46+
},
47+
{
48+
"code": "PART",
49+
"display": "Participation"
50+
},
51+
{
52+
"code": "WIT",
53+
"display": "Witness"
54+
},
55+
{
56+
"code": "AUT",
57+
"display": "Author (originator)"
58+
},
59+
{
60+
"code": "AUTHEN",
61+
"display": "Authenticator"
62+
}
63+
]
64+
}
65+
]
66+
}
67+
}

src/templates/CTCAdverseEventTemplate.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,47 @@ function seriousnessOutcomeTemplate(seriousnessOutcome) {
102102
};
103103
}
104104

105+
function participantFunctionTemplate(functionCode) {
106+
return {
107+
url: 'function',
108+
valueCodeableConcept: {
109+
coding: [
110+
coding(functionCode),
111+
],
112+
},
113+
};
114+
}
115+
116+
function participantActorTemplate(actor) {
117+
return {
118+
url: 'actor',
119+
valueReference: {
120+
...reference({ id: actor }),
121+
},
122+
};
123+
}
124+
125+
function participantTemplate(actor, functionCode) {
126+
return {
127+
url: 'http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-participant',
128+
...extensionArr(
129+
functionCode ? participantFunctionTemplate(functionCode) : null,
130+
participantActorTemplate(actor),
131+
),
132+
};
133+
}
134+
105135

106136
function CTCAdverseEventTemplate({
107137
id, subjectId, code, system, version, display, text, suspectedCauseId, suspectedCauseType, seriousnessCode, seriousnessCodeSystem, seriousnessDisplayText, category,
108-
studyId, effectiveDateTime, recordedDateTime, grade, resolvedDate, expectation, seriousnessOutcome,
138+
studyId, effectiveDateTime, recordedDateTime, grade, resolvedDate, expectation, seriousnessOutcome, actor, functionCode,
109139
}) {
110140
if (!(subjectId && code && system && effectiveDateTime && grade)) {
111-
throw Error('Trying to render an AdverseEventTemplate, but a required argument is messing; ensure that subjectId, code, system, grade, and effectiveDateTime are all present');
141+
throw Error('Trying to render an AdverseEventTemplate, but a required argument is missing; ensure that subjectId, code, system, grade, and effectiveDateTime are all present');
142+
}
143+
144+
if (functionCode && !actor) {
145+
throw Error('Trying to render an AdverseEventTemplate, but a required argument is missing; actor is a required value when functionCode is included');
112146
}
113147

114148
return {
@@ -119,6 +153,7 @@ function CTCAdverseEventTemplate({
119153
resolvedDate ? resolvedDateTemplate(resolvedDate) : null,
120154
expectation ? expectationTemplate(expectation) : null,
121155
seriousnessOutcome ? seriousnessOutcomeTemplate(seriousnessOutcome) : null,
156+
actor ? participantTemplate(actor, functionCode) : null,
122157
),
123158
subject: reference({ id: subjectId, resourceType: 'Patient' }),
124159
...ifSomeArgs(eventTemplate)({ code, system, version, display }, text),

test/extractors/CSVCTCAdverseEventextractor.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ describe('CSVCTCAdverseEventExtractor', () => {
3636
test('should join data appropriately and throw errors when missing required properties', () => {
3737
const expectedErrorString = 'The adverse event is missing an expected attribute. Adverse event code, effective date, and grade are all required.';
3838
const expectedCategoryErrorString = 'A category attribute on the adverse event is missing a corresponding categoryCodeSystem or categoryDisplayText value.';
39+
const expectedActorErrorString = 'The adverse event is missing an expected attribute. Adverse event actor is a required element when a functionCode value is included.';
3940
const localData = _.cloneDeep(exampleCTCCSVAdverseEventModuleResponse);
4041
const patientId = getPatientFromContext(MOCK_CONTEXT).id;
4142

@@ -60,6 +61,15 @@ describe('CSVCTCAdverseEventExtractor', () => {
6061
localData[0].categorydisplaytext = 'Product Use Error|';
6162
expect(formatData(localData, patientId)).toEqual(expect.anything());
6263

64+
// Test that deleting the actor value but leaving functionCode will throw an error
65+
delete localData[0].actor;
66+
expect(() => formatData(localData, patientId)).toThrow(new Error(expectedActorErrorString));
67+
68+
// Test that deleting the functionCode value works fine
69+
localData[0].actor = 'practitioner-id';
70+
delete localData[0].functioncode;
71+
expect(formatData(localData, patientId)).toEqual(expect.anything());
72+
6373
// Test that deleting a mandatory value throws an error
6474
delete localData[0].grade;
6575
expect(() => formatData(localData, patientId)).toThrow(new Error(expectedErrorString));

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,29 @@
4747
}
4848
]
4949
}
50+
},
51+
{
52+
"url": "http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-participant",
53+
"extension": [
54+
{
55+
"url": "function",
56+
"valueCodeableConcept": {
57+
"coding": [
58+
{
59+
"system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType",
60+
"code": "INF",
61+
"display": "Informant"
62+
}
63+
]
64+
}
65+
},
66+
{
67+
"url": "actor",
68+
"valueReference": {
69+
"reference": "urn:uuid:practitioner-id"
70+
}
71+
}
72+
]
5073
}
5174
],
5275
"subject": {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
"grade": "1",
2222
"resolveddate": "2021-12-01",
2323
"seriousnessoutcome": "C113380",
24-
"expectation": "C41333"
24+
"expectation": "C41333",
25+
"actor": "practitioner-id",
26+
"functioncode": "INF"
2527
}
2628
]
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,expectation,resolvedDate,seriousnessOutcome
2-
123,adverseEventId-1,10012174,http://terminology.hl7.org/CodeSystem/MDRAE,20.0,Dehydration,DHN IV given,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,researchId-1,12-09-1994,12-09-1994,1,C41333,2021-12-01,C113380
3-
456,adverseEventId-2,C143283,http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl,20.0,Anemia,AIHA NGTD,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|,researchId-2,12-10-1995,12-10-1995,2,C41333,2020-10-02,C113380
4-
789,adverseEventId-3,150003,,,,,,,,,,product-use-error,,,,12-09-1994,,3,,,
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+
123,adverseEventId-1,10012174,http://terminology.hl7.org/CodeSystem/MDRAE,20.0,Dehydration,DHN IV given,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,researchId-1,12-09-1994,12-09-1994,1,C41333,2021-12-01,C113380,practitioner-1,PART
3+
456,adverseEventId-2,C143283,http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl,20.0,Anemia,AIHA NGTD,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|,researchId-2,12-10-1995,12-10-1995,2,C41333,2020-10-02,C113380,practitioner-2,
4+
789,adverseEventId-3,150003,,,,,,,,,,product-use-error,,,,12-09-1994,,3,,,,,

test/templates/ctcAdverseEvent.test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ const VALID_DATA = {
3333
system: 'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl',
3434
display: 'Expected Adverse Event',
3535
},
36+
actor: 'practitioner-id',
37+
functionCode: {
38+
code: 'PART',
39+
system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType',
40+
display: 'Participation',
41+
},
3642
};
3743

3844
const MINIMAL_DATA = {
@@ -90,13 +96,20 @@ const INVALID_DATA = {
9096
system: 'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl',
9197
display: 'Expected Adverse Event',
9298
},
99+
actor: 'practitioner-id',
100+
functionCode: {
101+
code: 'PART',
102+
system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType',
103+
display: 'Participation',
104+
},
93105
};
94106

95107
describe('test Adverse Event template', () => {
96108
test('valid data passed into template should generate FHIR resource', () => {
97109
const generatedAdverseEvent = CTCAdverseEventTemplate(VALID_DATA);
98110

99111
expect(generatedAdverseEvent).toEqual(maximalValidExampleAdverseEvent);
112+
100113
expect(isValidFHIR(generatedAdverseEvent)).toBeTruthy();
101114
});
102115

@@ -137,4 +150,9 @@ describe('test Adverse Event template', () => {
137150
test('invalid data should throw an error', () => {
138151
expect(() => CTCAdverseEventTemplate(INVALID_DATA)).toThrow(Error);
139152
});
153+
154+
test('Otherwise valid data including the functionCode but not an actor should throw an error', () => {
155+
delete VALID_DATA.actor;
156+
expect(() => CTCAdverseEventTemplate(VALID_DATA)).toThrow(Error);
157+
});
140158
});

0 commit comments

Comments
 (0)