-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #141 from cluebbehusen/master
Added bulk survey response export in csv format
- Loading branch information
Showing
7 changed files
with
325 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
'use strict'; | ||
|
||
const querystring = require('querystring'); | ||
const { get } = require('../../api'); | ||
const plainPrint = require('../../plainPrint'); | ||
|
||
exports.command = 'export-responses <projectId> <surveyId>'; | ||
exports.desc = 'Export all responses in .csv format'; | ||
exports.builder = yargs => { | ||
yargs.positional('projectId', { | ||
describe: 'The ID of the project containing the survey.', | ||
type: 'string' | ||
}).positional('surveyId', { | ||
describe: 'The ID of the survey to export responses from.', | ||
type: 'string' | ||
}); | ||
}; | ||
|
||
function buildQuestionList (surveyItem, results) { | ||
if (surveyItem.linkId && surveyItem.text && surveyItem.type && surveyItem.type !== 'group' && surveyItem.type !== 'display') { | ||
results.push({ | ||
text: surveyItem.text.replace(/\n/g, ' ').replace(/"/g, '""'), | ||
linkId: surveyItem.linkId | ||
}); | ||
} | ||
if (surveyItem.item) { | ||
(surveyItem.item || []).forEach(_ => buildQuestionList(_, results)); | ||
} | ||
} | ||
|
||
function generateLinkAnswerMap (responseItem, results) { | ||
if (responseItem.linkId && responseItem.answer) { | ||
results[responseItem.linkId] = responseItem.answer; | ||
} | ||
if (responseItem.item) { | ||
(responseItem.item || []).forEach(_ => generateLinkAnswerMap(_, results)); | ||
} | ||
} | ||
|
||
function handleDefaultValue (value) { | ||
return value[Object.keys(value)[0]]; | ||
} | ||
|
||
function handleValueCoding (coding) { | ||
if (coding.valueCoding.display) { | ||
return coding.valueCoding.display; | ||
} else if (coding.valueCoding.code) { | ||
return coding.valueCoding.code; | ||
} else { | ||
return ''; | ||
} | ||
} | ||
|
||
function handleValueQuantity (responseValue) { | ||
const { | ||
value = '', | ||
unit = '' | ||
} = responseValue.valueQuantity || {}; | ||
return `${value} ${unit}`; | ||
} | ||
|
||
exports.handler = async argv => { | ||
const surveyUrl = `/v1/survey/projects/${argv.projectId}/surveys/${argv.surveyId}`; | ||
const survey = await get(argv, surveyUrl); | ||
const results = [{text: 'Patient ID', linkId: ''}, {text: 'Date', linkId: ''}]; | ||
(survey.data.item || []).forEach(_ => buildQuestionList(_, results)); | ||
const responseItems = []; | ||
const responseQuery = querystring.stringify({ | ||
'surveyId': `${argv.surveyId}`, | ||
'pageSize': 10 | ||
}); | ||
const responsesUrl = `/v1/survey/projects/${argv.projectId}/responses?${responseQuery}`; | ||
let responses = await get(argv, responsesUrl); | ||
responseItems.push(...responses.data.items); | ||
while (responses.data.links.next) { | ||
const nextPageUrl = responses.data.links.next; | ||
responses = await get(argv, nextPageUrl); | ||
responseItems.push(...responses.data.items); | ||
} | ||
const csvRows = []; | ||
csvRows.push(`"${results.map(_ => _.text).join('","')}"`); | ||
responseItems.forEach(responseItem => { | ||
const responseColumns = []; | ||
responseColumns.push(responseItem.subject.reference); | ||
responseColumns.push(responseItem.authored); | ||
const responseItemResults = {}; | ||
(responseItem.item || []).forEach(_ => generateLinkAnswerMap(_, responseItemResults)); | ||
results.map(_ => _.linkId) | ||
.filter(_ => _ !== '') | ||
.forEach(linkId => { | ||
if (responseItemResults[linkId]) { | ||
let responseString = ''; | ||
responseItemResults[linkId].forEach(value => { | ||
if (value.valueQuantity) { | ||
responseString = responseString.concat(handleValueQuantity(value)); | ||
} else if (value.valueCoding) { | ||
responseString = responseString.concat(handleValueCoding(value)); | ||
} else { | ||
responseString = responseString.concat(handleDefaultValue(value)); | ||
} | ||
responseString = responseString.concat('|'); | ||
}); | ||
responseColumns.push(responseString.slice(0, -1).replace(/"/g, '""').replace(/\n/g, ' ')); | ||
} else { | ||
// optional questions may not have answers | ||
responseColumns.push(''); | ||
} | ||
}); | ||
csvRows.push(`"${responseColumns.join('","')}"`); | ||
}); | ||
plainPrint(csvRows.join('\n')); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
'use strict'; | ||
|
||
const options = require('../common-yargs'); | ||
|
||
exports.command = 'surveys <command>'; | ||
exports.desc = 'Perform operations on surveys.'; | ||
exports.builder = yargs => { | ||
return options(yargs.commandDir('survey_cmds')); | ||
}; | ||
exports.handler = function (argv) {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use strict'; | ||
|
||
module.exports = function (data) { | ||
console.log(data); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
'use strict'; | ||
|
||
const yargs = require('yargs'); | ||
const sinon = require('sinon'); | ||
const test = require('ava'); | ||
const proxyquire = require('proxyquire'); | ||
|
||
const questionnaire = require('./survey/questionnaire.json'); | ||
const responseOne = require('./survey/response_one.json'); | ||
const responseTwo = require('./survey/response_two.json'); | ||
|
||
const getStub = sinon.stub(); | ||
const printSpy = sinon.spy(); | ||
let callback; | ||
|
||
const mocks = { | ||
'../../api': { | ||
get: getStub | ||
}, | ||
'../../plainPrint': (data) => { | ||
printSpy(data); | ||
callback(); | ||
} | ||
}; | ||
|
||
const exportResponses = proxyquire('../../../lib/cmds/survey_cmds/export-responses', mocks); | ||
|
||
test.afterEach.always(t => { | ||
getStub.reset(); | ||
printSpy.resetHistory(); | ||
callback = null; | ||
}); | ||
|
||
test.serial.cb('The export command should list responses in csv format', t => { | ||
getStub.onFirstCall().returns(questionnaire); | ||
getStub.onSecondCall().returns(responseOne); | ||
getStub.onThirdCall().returns(responseTwo); | ||
|
||
callback = () => { | ||
t.is(getStub.callCount, 3); | ||
t.is(getStub.getCall(0).args[1], '/v1/survey/projects/projectId/surveys/surveyId'); | ||
t.is(getStub.getCall(1).args[1], '/v1/survey/projects/projectId/responses?surveyId=surveyId&pageSize=10'); | ||
t.is(getStub.getCall(2).args[1], 'testurl'); | ||
t.is(printSpy.callCount, 1); | ||
t.is(printSpy.getCall(0).args[0], '"Patient ID","Date","Systolic blood pressure","Diastolic blood pressure","What country do you live in?"\n"73d77961-a8b7-48e0-b76f-66e53485038b","2020-06-04T14:56:14.402Z","120 millimeter of mercury","80 millimeter of mercury","United States of America"\n"73d77961-a8b7-48e0-b76f-66e53485038b","2020-06-04T14:56:14.402Z","120 millimeter of mercury","80 millimeter of mercury","United States of America"'); | ||
t.end(); | ||
}; | ||
|
||
yargs.command(exportResponses) | ||
.parse('export-responses projectId surveyId'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"data": { | ||
"item": [ | ||
{ | ||
"linkId": "ba0953cc-f0fd-4f3d-bfbd-9320e0c080a2", | ||
"text": "Page 1", | ||
"type": "group", | ||
"item": [ | ||
{ | ||
"type": "quantity", | ||
"text": "Systolic blood pressure", | ||
"linkId": "f076e708-add9-4c1a-ba9a-9782b05c2bad" | ||
}, | ||
{ | ||
"type": "quantity", | ||
"text": "Diastolic blood pressure", | ||
"linkId": "cd88370d-44c8-491f-a71c-fcb53f02a451" | ||
}, | ||
{ | ||
"linkId": "17b9a36d-cb0c-44c5-83d1-c1f8597548a8", | ||
"type": "choice", | ||
"text": "What country do you live in?" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
{ | ||
"data": { | ||
"items": [ | ||
{ | ||
"subject": { | ||
"reference": "73d77961-a8b7-48e0-b76f-66e53485038b" | ||
}, | ||
"authored": "2020-06-04T14:56:14.402Z", | ||
"item": [ | ||
{ | ||
"linkId": "ba0953cc-f0fd-4f3d-bfbd-9320e0c080a2", | ||
"item": [ | ||
{ | ||
"linkId": "f076e708-add9-4c1a-ba9a-9782b05c2bad", | ||
"answer": [ | ||
{ | ||
"valueQuantity": { | ||
"unit": "millimeter of mercury", | ||
"system": "http://unitsofmeasure.org", | ||
"code": "mm[Hg]", | ||
"value": 120 | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
"linkId": "cd88370d-44c8-491f-a71c-fcb53f02a451", | ||
"answer": [ | ||
{ | ||
"valueQuantity": { | ||
"unit": "millimeter of mercury", | ||
"system": "http://unitsofmeasure.org", | ||
"code": "mm[Hg]", | ||
"value": 80 | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
"linkId": "17b9a36d-cb0c-44c5-83d1-c1f8597548a8", | ||
"answer": [ | ||
{ | ||
"valueCoding": { | ||
"display": "United States of America", | ||
"code": "US" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] | ||
} | ||
], | ||
"links": { | ||
"self": "fillin", | ||
"next": "testurl" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
{ | ||
"data": { | ||
"items": [ | ||
{ | ||
"subject": { | ||
"reference": "73d77961-a8b7-48e0-b76f-66e53485038b" | ||
}, | ||
"authored": "2020-06-04T14:56:14.402Z", | ||
"item": [ | ||
{ | ||
"linkId": "ba0953cc-f0fd-4f3d-bfbd-9320e0c080a2", | ||
"item": [ | ||
{ | ||
"linkId": "f076e708-add9-4c1a-ba9a-9782b05c2bad", | ||
"answer": [ | ||
{ | ||
"valueQuantity": { | ||
"unit": "millimeter of mercury", | ||
"system": "http://unitsofmeasure.org", | ||
"code": "mm[Hg]", | ||
"value": 120 | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
"linkId": "cd88370d-44c8-491f-a71c-fcb53f02a451", | ||
"answer": [ | ||
{ | ||
"valueQuantity": { | ||
"unit": "millimeter of mercury", | ||
"system": "http://unitsofmeasure.org", | ||
"code": "mm[Hg]", | ||
"value": 80 | ||
} | ||
} | ||
] | ||
}, | ||
{ | ||
"linkId": "17b9a36d-cb0c-44c5-83d1-c1f8597548a8", | ||
"answer": [ | ||
{ | ||
"valueCoding": { | ||
"display": "United States of America", | ||
"code": "US" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] | ||
} | ||
], | ||
"links": { | ||
"self": "fillin" | ||
} | ||
} | ||
} |