diff --git a/docs/CSV_Templates.xlsx b/docs/CSV_Templates.xlsx index 1d187b35..48c205cd 100755 Binary files a/docs/CSV_Templates.xlsx and b/docs/CSV_Templates.xlsx differ diff --git a/docs/ctc-adverse-event.csv b/docs/ctc-adverse-event.csv index 09bc9d44..272acee8 100644 --- a/docs/ctc-adverse-event.csv +++ b/docs/ctc-adverse-event.csv @@ -1,4 +1,4 @@ -mrn,adverseEventId,adverseEventCode,adverseEventCodeSystem,adverseEventCodeVersion,adverseEventDisplayText,adverseEventText,suspectedCauseId,suspectedCauseType,seriousness,seriousnessCodeSystem,seriousnessDisplayText,category,categoryCodeSystem,categoryDisplayText,studyId,effectiveDate,recordedDate,grade,expectation,resolvedDate,seriousnessOutcome -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 -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 -mrn-minimal-example,,code-from-default-system,,,,,,,,,,,,,1994-12-09,,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 +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 +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, +mrn-minimal-example,,code-from-default-system,,,,,,,,,,,,,1994-12-09,,1,,,,,, diff --git a/src/extractors/CSVCTCAdverseEventExtractor.js b/src/extractors/CSVCTCAdverseEventExtractor.js index 6657a709..6fd941cc 100644 --- a/src/extractors/CSVCTCAdverseEventExtractor.js +++ b/src/extractors/CSVCTCAdverseEventExtractor.js @@ -34,10 +34,14 @@ function formatData(adverseEventData, patientId) { expectation, resolveddate: resolvedDate, seriousnessoutcome: seriousnessOutcome, + actor, + functioncode: functionCode, } = data; if (!(adverseEventCode && effectiveDate && grade)) { throw new Error('The adverse event is missing an expected attribute. Adverse event code, effective date, and grade are all required.'); + } else if (functionCode && !actor) { + throw new Error('The adverse event is missing an expected attribute. Adverse event actor is a required element when a functionCode value is included.'); } const categoryCodes = category.split('|'); @@ -90,6 +94,16 @@ function formatData(adverseEventData, patientId) { 'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl', ), }, + actor, + functionCode: !functionCode ? null : { + code: functionCode, + system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType', + display: getDisplayFromConcept( + path.resolve(__dirname, '..', 'helpers', 'valueSets', 'adverse-event-participant-function-value-set.json'), + functionCode, + 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType', + ), + }, }; }); } diff --git a/src/helpers/valueSets/adverse-event-participant-function-value-set.json b/src/helpers/valueSets/adverse-event-participant-function-value-set.json new file mode 100644 index 00000000..7b895e92 --- /dev/null +++ b/src/helpers/valueSets/adverse-event-participant-function-value-set.json @@ -0,0 +1,67 @@ +{ + "resourceType": "ValueSet", + "id": "adverse-event-participant-function-value-set", + "text": { + "status": "extensions", + "div": "
" + }, + "url": "http://hl7.org/fhir/us/ctcae/ValueSet/adverse-event-participant-function-value-set", + "version": "0.0.1", + "name": "AdverseEventParticipantFunctionVS", + "title": "Adverse Event Participant Function", + "status": "active", + "date": "2021-12-03T16:46:43+00:00", + "publisher": "HL7 International Clinical Interoperability Council", + "contact": [ + { + "name": "HL7 International Clinical Interoperability Council", + "telecom": [ + { + "system": "url", + "value": "http://www.mcodeinitiative.org" + } + ] + } + ], + "description": "This value set includes codes that describe the type of involvement of the actor in the adverse event", + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "code": "US", + "display": "United States of America" + } + ] + } + ], + "compose": { + "include": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", + "concept": [ + { + "code": "INF", + "display": "Informant" + }, + { + "code": "PART", + "display": "Participation" + }, + { + "code": "WIT", + "display": "Witness" + }, + { + "code": "AUT", + "display": "Author (originator)" + }, + { + "code": "AUTHEN", + "display": "Authenticator" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/src/templates/CTCAdverseEventTemplate.js b/src/templates/CTCAdverseEventTemplate.js index fb0fd1ea..02619cfb 100644 --- a/src/templates/CTCAdverseEventTemplate.js +++ b/src/templates/CTCAdverseEventTemplate.js @@ -102,13 +102,47 @@ function seriousnessOutcomeTemplate(seriousnessOutcome) { }; } +function participantFunctionTemplate(functionCode) { + return { + url: 'function', + valueCodeableConcept: { + coding: [ + coding(functionCode), + ], + }, + }; +} + +function participantActorTemplate(actor) { + return { + url: 'actor', + valueReference: { + ...reference({ id: actor }), + }, + }; +} + +function participantTemplate(actor, functionCode) { + return { + url: 'http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-participant', + ...extensionArr( + functionCode ? participantFunctionTemplate(functionCode) : null, + participantActorTemplate(actor), + ), + }; +} + function CTCAdverseEventTemplate({ id, subjectId, code, system, version, display, text, suspectedCauseId, suspectedCauseType, seriousnessCode, seriousnessCodeSystem, seriousnessDisplayText, category, - studyId, effectiveDateTime, recordedDateTime, grade, resolvedDate, expectation, seriousnessOutcome, + studyId, effectiveDateTime, recordedDateTime, grade, resolvedDate, expectation, seriousnessOutcome, actor, functionCode, }) { if (!(subjectId && code && system && effectiveDateTime && grade)) { - throw Error('Trying to render an AdverseEventTemplate, but a required argument is messing; ensure that subjectId, code, system, grade, and effectiveDateTime are all present'); + throw Error('Trying to render an AdverseEventTemplate, but a required argument is missing; ensure that subjectId, code, system, grade, and effectiveDateTime are all present'); + } + + if (functionCode && !actor) { + throw Error('Trying to render an AdverseEventTemplate, but a required argument is missing; actor is a required value when functionCode is included'); } return { @@ -119,6 +153,7 @@ function CTCAdverseEventTemplate({ resolvedDate ? resolvedDateTemplate(resolvedDate) : null, expectation ? expectationTemplate(expectation) : null, seriousnessOutcome ? seriousnessOutcomeTemplate(seriousnessOutcome) : null, + actor ? participantTemplate(actor, functionCode) : null, ), subject: reference({ id: subjectId, resourceType: 'Patient' }), ...ifSomeArgs(eventTemplate)({ code, system, version, display }, text), diff --git a/test/extractors/CSVCTCAdverseEventextractor.test.js b/test/extractors/CSVCTCAdverseEventextractor.test.js index 39c0d259..352108e6 100644 --- a/test/extractors/CSVCTCAdverseEventextractor.test.js +++ b/test/extractors/CSVCTCAdverseEventextractor.test.js @@ -36,6 +36,7 @@ describe('CSVCTCAdverseEventExtractor', () => { test('should join data appropriately and throw errors when missing required properties', () => { const expectedErrorString = 'The adverse event is missing an expected attribute. Adverse event code, effective date, and grade are all required.'; const expectedCategoryErrorString = 'A category attribute on the adverse event is missing a corresponding categoryCodeSystem or categoryDisplayText value.'; + const expectedActorErrorString = 'The adverse event is missing an expected attribute. Adverse event actor is a required element when a functionCode value is included.'; const localData = _.cloneDeep(exampleCTCCSVAdverseEventModuleResponse); const patientId = getPatientFromContext(MOCK_CONTEXT).id; @@ -60,6 +61,15 @@ describe('CSVCTCAdverseEventExtractor', () => { localData[0].categorydisplaytext = 'Product Use Error|'; expect(formatData(localData, patientId)).toEqual(expect.anything()); + // Test that deleting the actor value but leaving functionCode will throw an error + delete localData[0].actor; + expect(() => formatData(localData, patientId)).toThrow(new Error(expectedActorErrorString)); + + // Test that deleting the functionCode value works fine + localData[0].actor = 'practitioner-id'; + delete localData[0].functioncode; + expect(formatData(localData, patientId)).toEqual(expect.anything()); + // Test that deleting a mandatory value throws an error delete localData[0].grade; expect(() => formatData(localData, patientId)).toThrow(new Error(expectedErrorString)); diff --git a/test/extractors/fixtures/csv-ctc-adverse-event-bundle.json b/test/extractors/fixtures/csv-ctc-adverse-event-bundle.json index 339a2c81..53097112 100644 --- a/test/extractors/fixtures/csv-ctc-adverse-event-bundle.json +++ b/test/extractors/fixtures/csv-ctc-adverse-event-bundle.json @@ -47,6 +47,29 @@ } ] } + }, + { + "url": "http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-participant", + "extension": [ + { + "url": "function", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", + "code": "INF", + "display": "Informant" + } + ] + } + }, + { + "url": "actor", + "valueReference": { + "reference": "urn:uuid:practitioner-id" + } + } + ] } ], "subject": { diff --git a/test/extractors/fixtures/csv-ctc-adverse-event-module-response.json b/test/extractors/fixtures/csv-ctc-adverse-event-module-response.json index 0d3bfa6f..b156f157 100644 --- a/test/extractors/fixtures/csv-ctc-adverse-event-module-response.json +++ b/test/extractors/fixtures/csv-ctc-adverse-event-module-response.json @@ -21,6 +21,8 @@ "grade": "1", "resolveddate": "2021-12-01", "seriousnessoutcome": "C113380", - "expectation": "C41333" + "expectation": "C41333", + "actor": "practitioner-id", + "functioncode": "INF" } ] \ No newline at end of file diff --git a/test/sample-client-data/ctc-adverse-event-information.csv b/test/sample-client-data/ctc-adverse-event-information.csv index 794c1ee9..a767ad39 100644 --- a/test/sample-client-data/ctc-adverse-event-information.csv +++ b/test/sample-client-data/ctc-adverse-event-information.csv @@ -1,4 +1,4 @@ -mrn,adverseEventId,adverseEventCode,adverseEventCodeSystem,adverseEventCodeVersion,adverseEventDisplayText,adverseEventText,suspectedCauseId,suspectedCauseType,seriousness,seriousnessCodeSystem,seriousnessDisplayText,category,categoryCodeSystem,categoryDisplayText,studyId,effectiveDate,recordedDate,grade,expectation,resolvedDate,seriousnessOutcome -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 -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 -789,adverseEventId-3,150003,,,,,,,,,,product-use-error,,,,12-09-1994,,3,,, \ No newline at end of file +mrn,adverseEventId,adverseEventCode,adverseEventCodeSystem,adverseEventCodeVersion,adverseEventDisplayText,adverseEventText,suspectedCauseId,suspectedCauseType,seriousness,seriousnessCodeSystem,seriousnessDisplayText,category,categoryCodeSystem,categoryDisplayText,studyId,effectiveDate,recordedDate,grade,expectation,resolvedDate,seriousnessOutcome,actor,functionCode +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 +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, +789,adverseEventId-3,150003,,,,,,,,,,product-use-error,,,,12-09-1994,,3,,,,, \ No newline at end of file diff --git a/test/templates/ctcAdverseEvent.test.js b/test/templates/ctcAdverseEvent.test.js index 625cadd1..efbf6207 100644 --- a/test/templates/ctcAdverseEvent.test.js +++ b/test/templates/ctcAdverseEvent.test.js @@ -33,6 +33,12 @@ const VALID_DATA = { system: 'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl', display: 'Expected Adverse Event', }, + actor: 'practitioner-id', + functionCode: { + code: 'PART', + system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType', + display: 'Participation', + }, }; const MINIMAL_DATA = { @@ -90,6 +96,12 @@ const INVALID_DATA = { system: 'http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl', display: 'Expected Adverse Event', }, + actor: 'practitioner-id', + functionCode: { + code: 'PART', + system: 'http://terminology.hl7.org/CodeSystem/v3-ParticipationType', + display: 'Participation', + }, }; describe('test Adverse Event template', () => { @@ -97,6 +109,7 @@ describe('test Adverse Event template', () => { const generatedAdverseEvent = CTCAdverseEventTemplate(VALID_DATA); expect(generatedAdverseEvent).toEqual(maximalValidExampleAdverseEvent); + expect(isValidFHIR(generatedAdverseEvent)).toBeTruthy(); }); @@ -137,4 +150,9 @@ describe('test Adverse Event template', () => { test('invalid data should throw an error', () => { expect(() => CTCAdverseEventTemplate(INVALID_DATA)).toThrow(Error); }); + + test('Otherwise valid data including the functionCode but not an actor should throw an error', () => { + delete VALID_DATA.actor; + expect(() => CTCAdverseEventTemplate(VALID_DATA)).toThrow(Error); + }); }); diff --git a/test/templates/fixtures/maximal-ctc-adverse-event-resource.json b/test/templates/fixtures/maximal-ctc-adverse-event-resource.json index 21e40a8a..1ea37982 100644 --- a/test/templates/fixtures/maximal-ctc-adverse-event-resource.json +++ b/test/templates/fixtures/maximal-ctc-adverse-event-resource.json @@ -45,6 +45,29 @@ } ] } + }, + { + "url": "http://hl7.org/fhir/us/ctcae/StructureDefinition/adverse-event-participant", + "extension": [ + { + "url": "function", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", + "code": "PART", + "display": "Participation" + } + ] + } + }, + { + "url": "actor", + "valueReference": { + "reference": "urn:uuid:practitioner-id" + } + } + ] } ], "event": {