Skip to content

Commit 882188b

Browse files
Merge pull request #164 from mcode/duplicate-ae
Create CTC Adverse Event Extractor
2 parents 91791ed + 9cdb9f6 commit 882188b

File tree

7 files changed

+215
-0
lines changed

7 files changed

+215
-0
lines changed

docs/ctc-adverse-event.csv

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
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,

src/client/MCODEClient.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
CSVCancerRelatedMedicationRequestExtractor,
77
CSVClinicalTrialInformationExtractor,
88
CSVConditionExtractor,
9+
CSVCTCAdverseEventExtractor,
910
CSVObservationExtractor,
1011
CSVPatientExtractor,
1112
CSVProcedureExtractor,
@@ -35,6 +36,7 @@ class MCODEClient extends BaseClient {
3536
CSVCancerRelatedMedicationRequestExtractor,
3637
CSVClinicalTrialInformationExtractor,
3738
CSVConditionExtractor,
39+
CSVCTCAdverseEventExtractor,
3840
CSVObservationExtractor,
3941
CSVPatientExtractor,
4042
CSVProcedureExtractor,
@@ -67,6 +69,7 @@ class MCODEClient extends BaseClient {
6769
{ type: 'CSVProcedureExtractor', dependencies: ['CSVPatientExtractor'] },
6870
{ type: 'CSVObservationExtractor', dependencies: ['CSVPatientExtractor'] },
6971
{ type: 'CSVAdverseEventExtractor', dependencies: ['CSVPatientExtractor'] },
72+
{ type: 'CSVCTCAdverseEventExtractor', dependencies: ['CSVPatientExtractor'] },
7073
];
7174
// Sort extractors based on order and dependencies
7275
this.extractorConfig = sortExtractors(this.extractorConfig, dependencyInfo);
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
const { BaseCSVExtractor } = require('./BaseCSVExtractor');
2+
const { generateMcodeResources } = require('../templates');
3+
const { getEmptyBundle } = require('../helpers/fhirUtils');
4+
const { getPatientFromContext } = require('../helpers/contextUtils');
5+
const { formatDateTime } = require('../helpers/dateUtils');
6+
const logger = require('../helpers/logger');
7+
8+
// Formats data to be passed into template-friendly format
9+
function formatData(adverseEventData, patientId) {
10+
logger.debug('Reformatting adverse event data from CSV into template format');
11+
return adverseEventData.map((data) => {
12+
const {
13+
adverseeventid: adverseEventId,
14+
adverseeventcode: adverseEventCode,
15+
adverseeventcodesystem: adverseEventCodeSystem,
16+
adverseeventdisplaytext: adverseEventDisplayText,
17+
suspectedcauseid: suspectedCauseId,
18+
suspectedcausetype: suspectedCauseType,
19+
seriousness,
20+
seriousnesscodesystem: seriousnessCodeSystem,
21+
seriousnessdisplaytext: seriousnessDisplayText,
22+
category,
23+
categorycodesystem: categoryCodeSystem,
24+
categorydisplaytext: categoryDisplayText,
25+
severity,
26+
actuality,
27+
studyid: studyId,
28+
effectivedate: effectiveDate,
29+
recordeddate: recordedDate,
30+
} = data;
31+
32+
if (!(adverseEventCode && effectiveDate)) {
33+
throw new Error('The adverse event is missing an expected attribute. Adverse event code and effective date are all required.');
34+
}
35+
36+
const categoryCodes = category.split('|');
37+
const categorySystems = categoryCodeSystem.split('|');
38+
const categoryDisplays = categoryDisplayText.split('|');
39+
40+
if (!(categoryCodes.length === categorySystems.length && categoryCodes.length === categoryDisplays.length)) {
41+
throw new Error('A category attribute on the adverse event is missing a corresponding categoryCodeSystem or categoryDisplayText value.');
42+
}
43+
44+
45+
return {
46+
...(adverseEventId && { id: adverseEventId }),
47+
subjectId: patientId,
48+
code: adverseEventCode,
49+
system: !adverseEventCodeSystem ? 'http://snomed.info/sct' : adverseEventCodeSystem,
50+
display: adverseEventDisplayText,
51+
suspectedCauseId,
52+
suspectedCauseType,
53+
seriousnessCode: seriousness,
54+
seriousnessCodeSystem: !seriousnessCodeSystem ? 'http://terminology.hl7.org/CodeSystem/adverse-event-seriousness' : seriousnessCodeSystem,
55+
seriousnessDisplayText,
56+
category: categoryCodes.map((categoryCode, index) => {
57+
if (!categoryCode) return null;
58+
const categoryCoding = { code: categoryCode, system: categorySystems[index] ? categorySystems[index] : 'http://terminology.hl7.org/CodeSystem/adverse-event-category' };
59+
if (categoryDisplays[index]) categoryCoding.display = categoryDisplays[index];
60+
return categoryCoding;
61+
}),
62+
severity,
63+
actuality: !actuality ? 'actual' : actuality,
64+
studyId,
65+
effectiveDateTime: formatDateTime(effectiveDate),
66+
recordedDateTime: !recordedDate ? null : formatDateTime(recordedDate),
67+
};
68+
});
69+
}
70+
71+
class CSVCTCAdverseEventExtractor extends BaseCSVExtractor {
72+
constructor({ filePath, url }) {
73+
super({ filePath, url });
74+
}
75+
76+
async getAdverseEventData(mrn) {
77+
logger.debug('Getting Adverse Event Data');
78+
return this.csvModule.get('mrn', mrn);
79+
}
80+
81+
async get({ mrn, context }) {
82+
const adverseEventData = await this.getAdverseEventData(mrn);
83+
if (adverseEventData.length === 0) {
84+
logger.warn('No adverse event data found for patient');
85+
return getEmptyBundle();
86+
}
87+
const patientId = getPatientFromContext(context).id;
88+
89+
// Reformat data
90+
const formattedData = formatData(adverseEventData, patientId);
91+
92+
// Fill templates
93+
return generateMcodeResources('CTCAdverseEvent', formattedData);
94+
}
95+
}
96+
97+
module.exports = {
98+
CSVCTCAdverseEventExtractor,
99+
};

src/extractors/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { CSVCancerRelatedMedicationAdministrationExtractor } = require('./CSVCanc
55
const { CSVCancerRelatedMedicationRequestExtractor } = require('./CSVCancerRelatedMedicationRequestExtractor');
66
const { CSVClinicalTrialInformationExtractor } = require('./CSVClinicalTrialInformationExtractor');
77
const { CSVConditionExtractor } = require('./CSVConditionExtractor');
8+
const { CSVCTCAdverseEventExtractor } = require('./CSVCTCAdverseEventExtractor');
89
const { CSVObservationExtractor } = require('./CSVObservationExtractor');
910
const { CSVPatientExtractor } = require('./CSVPatientExtractor');
1011
const { CSVProcedureExtractor } = require('./CSVProcedureExtractor');
@@ -32,6 +33,7 @@ module.exports = {
3233
CSVCancerRelatedMedicationRequestExtractor,
3334
CSVClinicalTrialInformationExtractor,
3435
CSVConditionExtractor,
36+
CSVCTCAdverseEventExtractor,
3537
CSVObservationExtractor,
3638
CSVPatientExtractor,
3739
CSVProcedureExtractor,
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
const { coding, reference } = require('./snippets');
2+
const { ifAllArgsObj, ifSomeArgsObj, ifAllArgs, ifSomeArgsArr } = require('../helpers/templateUtils');
3+
4+
function eventTemplate(eventCoding) {
5+
return {
6+
event: {
7+
coding: [
8+
coding(eventCoding),
9+
],
10+
},
11+
};
12+
}
13+
14+
function suspectedCauseTemplate({ suspectedCauseId, suspectedCauseType }) {
15+
return {
16+
suspectEntity: [
17+
{
18+
instance:
19+
reference({ id: suspectedCauseId, resourceType: suspectedCauseType }),
20+
},
21+
],
22+
};
23+
}
24+
25+
26+
function seriousnessTemplate(seriousnessCoding) {
27+
return {
28+
seriousness: {
29+
coding: [
30+
coding(seriousnessCoding),
31+
],
32+
},
33+
};
34+
}
35+
36+
function individualCategoryTemplate(category) {
37+
return {
38+
coding: [coding(category),
39+
],
40+
};
41+
}
42+
43+
function categoryArrayTemplate(categoryArr) {
44+
const category = categoryArr.map(individualCategoryTemplate);
45+
return { category };
46+
}
47+
48+
function severityTemplate(severityCode) {
49+
return {
50+
severity: {
51+
coding: [
52+
coding({
53+
code: severityCode,
54+
system: 'http://terminology.hl7.org/CodeSystem/adverse-event-severity',
55+
}),
56+
],
57+
},
58+
};
59+
}
60+
61+
function studyTemplate(studyId) {
62+
return {
63+
study: [
64+
reference({ id: studyId, resourceType: 'ResearchStudy' }),
65+
],
66+
};
67+
}
68+
69+
function recordedDateTemplate(recordedDateTime) {
70+
return {
71+
recordedDate: recordedDateTime,
72+
};
73+
}
74+
75+
function CTCAdverseEventTemplate({
76+
id, subjectId, code, system, display, suspectedCauseId, suspectedCauseType, seriousnessCode, seriousnessCodeSystem, seriousnessDisplayText, category,
77+
severity, actuality, studyId, effectiveDateTime, recordedDateTime,
78+
}) {
79+
if (!(subjectId && code && system && effectiveDateTime && actuality)) {
80+
throw Error('Trying to render an AdverseEventTemplate, but a required argument is messing; ensure that subjectId, code, system, actuality, and effectiveDateTime are all present');
81+
}
82+
83+
return {
84+
resourceType: 'AdverseEvent',
85+
id,
86+
subject: reference({ id: subjectId, resourceType: 'Patient' }),
87+
...ifSomeArgsObj(eventTemplate)({ code, system, display }),
88+
...ifAllArgsObj(suspectedCauseTemplate)({ suspectedCauseId, suspectedCauseType }),
89+
...ifSomeArgsObj(seriousnessTemplate)({ code: seriousnessCode, system: seriousnessCodeSystem, display: seriousnessDisplayText }),
90+
...ifSomeArgsArr(categoryArrayTemplate)(category),
91+
...ifAllArgs(severityTemplate)(severity),
92+
actuality,
93+
...ifAllArgs(studyTemplate)(studyId),
94+
date: effectiveDateTime,
95+
...ifAllArgs(recordedDateTemplate)(recordedDateTime),
96+
};
97+
}
98+
99+
module.exports = {
100+
CTCAdverseEventTemplate,
101+
};

src/templates/ResourceGenerator.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const { cancerRelatedMedicationAdministrationTemplate } = require('./CancerRelat
88
const { cancerRelatedMedicationRequestTemplate } = require('./CancerRelatedMedicationRequestTemplate');
99
const { carePlanWithReviewTemplate } = require('./CarePlanWithReviewTemplate');
1010
const { conditionTemplate } = require('./ConditionTemplate');
11+
const { CTCAdverseEventTemplate } = require('./CTCAdverseEventTemplate');
1112
const { observationTemplate } = require('./ObservationTemplate');
1213
const { patientTemplate } = require('./PatientTemplate');
1314
const { procedureTemplate } = require('./ProcedureTemplate');
@@ -23,6 +24,7 @@ const fhirTemplateLookup = {
2324
CancerRelatedMedicationRequest: cancerRelatedMedicationRequestTemplate,
2425
CarePlanWithReview: carePlanWithReviewTemplate,
2526
Condition: conditionTemplate,
27+
CTCAdverseEvent: CTCAdverseEventTemplate,
2628
Observation: observationTemplate,
2729
Patient: patientTemplate,
2830
Procedure: procedureTemplate,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
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,

0 commit comments

Comments
 (0)