Skip to content

Commit 0fee38f

Browse files
Merge pull request #101 from mcode/adverse-event-uses-study-context
Use ResearchStudy id from context in AdverseEvent search
2 parents fac2f08 + db5d986 commit 0fee38f

File tree

4 files changed

+104
-7
lines changed

4 files changed

+104
-7
lines changed

src/extractors/FHIRAdverseEventExtractor.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');
2+
const { getResearchStudiesFromContext } = require('../helpers/contextUtils');
3+
const logger = require('../helpers/logger');
24

35
const BASE_STUDY = ''; // No base study specified
46

@@ -12,14 +14,28 @@ class FHIRAdverseEventExtractor extends BaseFHIRExtractor {
1214
// In addition to default parametrization, add study if specified
1315
async parametrizeArgsForFHIRModule({ context }) {
1416
const paramsWithID = await super.parametrizeArgsForFHIRModule({ context });
17+
let allResearchStudyResources = [];
18+
try {
19+
allResearchStudyResources = getResearchStudiesFromContext(context);
20+
} catch (e) {
21+
logger.error(e.message);
22+
logger.debug(e.stack);
23+
}
24+
1525
// The patient is referenced in the 'subject' field of an AdverseEvent
1626
paramsWithID.subject = paramsWithID.patient;
1727
delete paramsWithID.patient;
18-
// Only add study to parameters if it has been specified
19-
return {
28+
29+
// If there are research study resources, create a parameters object for each call to be made
30+
const newStudyIds = allResearchStudyResources.map((rs) => rs.id).join(',');
31+
const studyIdsForCurrentPatient = `${this.study}${this.study && newStudyIds ? ',' : ''}${newStudyIds}`;
32+
33+
// Only add study to parameters if it has been specified or was included from context
34+
const obj = {
2035
...paramsWithID,
21-
...(this.study && { study: this.study }),
36+
...(studyIdsForCurrentPatient && { study: studyIdsForCurrentPatient }),
2237
};
38+
return obj;
2339
}
2440
}
2541

src/helpers/contextUtils.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,20 @@ function getEncountersFromContext(context) {
6262
return encounterResourcesInContext;
6363
}
6464

65+
function getResearchStudiesFromContext(context) {
66+
logger.debug('Getting ResearchStudy resources from context');
67+
const researchStudyResourcesInContext = getBundleResourcesByType(context, 'ResearchStudy', {}, false);
68+
if (researchStudyResourcesInContext.length === 0) {
69+
throw Error('Could not find any ResearchStudy resources in context; ensure that a ClinicalTrialInformationExtractor or ResearchStudyExtractor is used earlier in your extraction configuration');
70+
}
71+
logger.debug(`ResearchStudy resources found in context. Found ${researchStudyResourcesInContext.length} ResearchStudy resources.`);
72+
return researchStudyResourcesInContext;
73+
}
74+
6575
module.exports = {
6676
getConditionEntriesFromContext,
6777
getConditionsFromContext,
6878
getEncountersFromContext,
6979
getPatientFromContext,
80+
getResearchStudiesFromContext,
7081
};

test/extractors/FHIRAdverseEventExtractor.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,28 @@ const MOCK_CONTEXT = {
1515
},
1616
],
1717
};
18+
const researchStudyResource = {
19+
resourceType: 'ResearchStudy',
20+
id: 'ResearchStudyExample01',
21+
};
22+
const MOCK_CONTEXT_WITH_RESEARCH_STUDY = {
23+
resourceType: 'Bundle',
24+
type: 'collection',
25+
entry: [
26+
{
27+
fullUrl: 'context-url-1',
28+
resource: { resourceType: 'Patient', id: MOCK_MRN },
29+
},
30+
{
31+
fullUrl: 'context-url-2',
32+
resource: researchStudyResource,
33+
},
34+
{
35+
fullUrl: 'context-url-3',
36+
resource: { ...researchStudyResource, id: 'ResearchStudyExample02' },
37+
},
38+
],
39+
};
1840

1941
// Construct extractor and create spies for mocking responses
2042
const extractor = new FHIRAdverseEventExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS });
@@ -40,6 +62,12 @@ describe('FHIRAdverseEventExtractor', () => {
4062
expect(params).not.toHaveProperty('study');
4163
});
4264

65+
test('should add study id for all ResearchStudy resources that are in context', async () => {
66+
const params = await extractor.parametrizeArgsForFHIRModule({ context: MOCK_CONTEXT_WITH_RESEARCH_STUDY });
67+
expect(params).toHaveProperty('study');
68+
expect(params.study).toEqual(`${researchStudyResource.id},ResearchStudyExample02`);
69+
});
70+
4371
describe('pass in optional study parameter', () => {
4472
test('should add study when set to param values', async () => {
4573
const params = await extractorWithStudy.parametrizeArgsForFHIRModule({ context: MOCK_CONTEXT });
@@ -51,6 +79,12 @@ describe('FHIRAdverseEventExtractor', () => {
5179
const params = await extractorWithStudy.parametrizeArgsForFHIRModule({ context: MOCK_CONTEXT });
5280
expect(params).not.toHaveProperty('patient');
5381
});
82+
83+
test('should add study from study parameter and from context', async () => {
84+
const params = await extractorWithStudy.parametrizeArgsForFHIRModule({ context: MOCK_CONTEXT_WITH_RESEARCH_STUDY });
85+
expect(params).toHaveProperty('study');
86+
expect(params.study).toEqual(`${extractorWithStudy.study},${researchStudyResource.id},ResearchStudyExample02`);
87+
});
5488
});
5589
});
5690
});

test/helpers/contextUtils.test.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
const { getConditionEntriesFromContext, getConditionsFromContext, getEncountersFromContext, getPatientFromContext } = require('../../src/helpers/contextUtils');
2-
3-
const MOCK_PATIENT_MRN = '123';
1+
const {
2+
getConditionEntriesFromContext,
3+
getConditionsFromContext,
4+
getEncountersFromContext,
5+
getPatientFromContext,
6+
getResearchStudiesFromContext,
7+
} = require('../../src/helpers/contextUtils');
48

59
describe('getPatientFromContext', () => {
610
const patientResource = {
@@ -124,7 +128,39 @@ describe('getEncountersFromContext', () => {
124128
});
125129

126130
test('Should throw an error if there are no encounters in context', () => {
127-
expect(() => getEncountersFromContext(MOCK_PATIENT_MRN, {}))
131+
expect(() => getEncountersFromContext({}))
128132
.toThrow('Could not find any encounter resources in context; ensure that an EncounterExtractor is used earlier in your extraction configuration');
129133
});
130134
});
135+
136+
describe('getResearchStudyFromContext', () => {
137+
const researchStudyResource = {
138+
resourceType: 'ResearchStudy',
139+
id: 'ResearchStudyExample01',
140+
};
141+
const researchStudyContext = {
142+
resourceType: 'Bundle',
143+
type: 'collection',
144+
entry: [
145+
{
146+
fullUrl: 'context-url-1',
147+
resource: researchStudyResource,
148+
},
149+
{
150+
fullUrl: 'context-url-2',
151+
resource: { ...researchStudyResource, id: 'ResearchStudyExample02' },
152+
},
153+
],
154+
};
155+
156+
test('Should return all ResearchStudy resources in context', () => {
157+
const researchStudyResources = getResearchStudiesFromContext(researchStudyContext);
158+
expect(researchStudyResources).toHaveLength(2);
159+
expect(researchStudyResources[0]).toEqual(researchStudyResource);
160+
});
161+
162+
test('Should throw an error if there are no research studies in context', () => {
163+
expect(() => getResearchStudiesFromContext({}))
164+
.toThrow('Could not find any ResearchStudy resources in context; ensure that a ClinicalTrialInformationExtractor or ResearchStudyExtractor is used earlier in your extraction configuration');
165+
});
166+
});

0 commit comments

Comments
 (0)