diff --git a/src/App.jsx b/src/App.jsx index 026ab2ec..fb92f96e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -92,6 +92,7 @@ export default class App extends Component { .then(questionnaire => { this.setState({ questionnaire: questionnaire }); this.setState({ response: response}); + this.setState({ isFetchingArtifacts: false}); }); }); } @@ -109,11 +110,18 @@ export default class App extends Component { let acCoverage = this.appContext.coverage; let acQuestionnaire = this.appContext.questionnaire; let acResponse = this.appContext.response; - if(acOrder && acCoverage && !acQuestionnaire && !acResponse) { - // TODO: There's an additional case where you could launch - // with just the order/coverage by invoking the operation - // but I think the endpoint extension on coverage which - // would facilitate that is going away in ballot. + if(isContainedQuestionnaire && questionnaire) { + // TODO: This is a workaround for getting adaptive forms to work + // in its current form, adaptive forms do not operate with the + // package operation + const reloadQuestionnaire = questionnaire !== undefined; + this.setState({ + isFetchingArtifacts: true, + reloadQuestionnaire + }) + this.fetchResourcesAndExecuteCql(acOrder, acCoverage, acQuestionnaire, questionnaire); + + } else if(acOrder && acCoverage && !acQuestionnaire && !acResponse) { searchByOrder(acOrder, this.smart).then((res) => { // TODO: Don't know how to deal with multiple QRs // Let user pick with a UI? Force orders to @@ -146,12 +154,12 @@ export default class App extends Component { } } - fetchResourcesAndExecuteCql(order, coverage, questionnaire) { + fetchResourcesAndExecuteCql(order, coverage, questionnaire, containedQuestionnaire) { fetchFhirVersion(this.props.smart.state.serverUrl) .then(fhirVersion => { this.fhirVersion = fhirVersion; - fetchArtifactsOperation(order, coverage, questionnaire, this.smart, this.consoleLog) + fetchArtifactsOperation(order, coverage, questionnaire, this.smart, this.consoleLog, containedQuestionnaire) .then(artifacts => { console.log("fetched needed artifacts:", artifacts); const orderResource = artifacts.order; diff --git a/src/LogPage.jsx b/src/LogPage.jsx index 119c5fcf..6fa6db4c 100644 --- a/src/LogPage.jsx +++ b/src/LogPage.jsx @@ -12,7 +12,7 @@ class LogPage extends Component { componentDidMount(){ const logRequest = new XMLHttpRequest(); - logRequest.open("GET", "../logs"); + logRequest.open("GET", "../api/logs"); logRequest.setRequestHeader("Content-Type", "application/json"); logRequest.onload = (e) => { this.setState({logs: JSON.parse(logRequest.responseText).sort((a,b)=>{return b.createdAt - a.createdAt})}); diff --git a/src/backend/database/impl.js b/src/backend/database/impl.js index 6d2f0d18..8382035d 100644 --- a/src/backend/database/impl.js +++ b/src/backend/database/impl.js @@ -31,18 +31,22 @@ function getCountAndIncrement() { } function postLog(log) { + log.createdAt = Date.now(); db.get("logs").push(log).write(); } function getLog(id) { return db.get("logs").find({id: id}).value(); } +function getLogs() { + return db.get("logs").value(); +} function putLog(id, log) { db.get("logs") - .find({ id: id }) - .assign(log) - .write(); + .find({ id: id }) + .assign(log).write(); + } function deleteClient(id) { @@ -56,6 +60,7 @@ module.exports = { deleteClient, postLog, getLog, + getLogs, putLog, getCountAndIncrement -}; \ No newline at end of file +}; diff --git a/src/backend/routes/database.js b/src/backend/routes/database.js index 7f3830ae..60ac4706 100644 --- a/src/backend/routes/database.js +++ b/src/backend/routes/database.js @@ -70,7 +70,7 @@ router.post("/logs", (req, res) => { router.put("/logs/:id", (req, res) => { const newLog = req.body; - db.putLog(req.params.id, newLog); + db.putLog(parseInt(req.params.id), newLog); res.sendStatus(200); }); @@ -84,4 +84,13 @@ router.get("/logs/:id", (req, res) => { } }); +router.get("/api/logs", (req, res) => { + const result = db.getLogs(); + if(result) { + res.send(result); + } else { + res.sendStatus(404) + } +}) + module.exports = router; \ No newline at end of file diff --git a/src/components/QuestionnaireForm/QuestionnaireForm.jsx b/src/components/QuestionnaireForm/QuestionnaireForm.jsx index e7b8a6dd..f08db2b6 100644 --- a/src/components/QuestionnaireForm/QuestionnaireForm.jsx +++ b/src/components/QuestionnaireForm/QuestionnaireForm.jsx @@ -39,7 +39,6 @@ export default class QuestionnaireForm extends Component { this.smart = props.smart; this.patientId = props.patientId; this.fhirVersion = props.fhirVersion; - this.FHIR_PREFIX = props.FHIR_PREFIX; this.appContext = props.appContext; this.partialForms = {}; this.handleGtable = this.handleGtable.bind(this); @@ -73,8 +72,7 @@ export default class QuestionnaireForm extends Component { savedResponse: mergedResponse }) } else { - - this.loadPreviousForm(); + this.loadPreviousForm(false); // If not using saved QuestionnaireResponse, create a new one let newResponse = { @@ -173,12 +171,15 @@ export default class QuestionnaireForm extends Component { return `QuestionnaireResponse?_lastUpdated=gt${updateDate.toISOString().split('T')[0]}&status=in-progress` } - loadPreviousForm() { + loadPreviousForm(showError = true) { // search for any QuestionnaireResponses - this.smart.request(this.getRetrieveSaveQuestionnaireUrl() + - "&subject=" + this.getPatient()).then((result) => { - this.popupClear("Would you like to load a previous form?", "Cancel", false); - this.processSavedQuestionnaireResponses(result, true); + let questionnaireResponseUrl = this.getRetrieveSaveQuestionnaireUrl(); + questionnaireResponseUrl = questionnaireResponseUrl + "&subject=" + this.getPatient(); + console.log("Using URL " + questionnaireResponseUrl); + + this.smart.request(questionnaireResponseUrl).then((result) => { + this.popupClear("Would you like to load a previously in-progress form?", "Cancel", false); + this.processSavedQuestionnaireResponses(result, showError); }, ((result) => { this.popupClear("Error: failed to load previous in-progress forms", "OK", true); this.popupLaunch(); @@ -187,7 +188,17 @@ export default class QuestionnaireForm extends Component { // retrieve next sets of questions loadNextQuestions() { - const url = this.props.FILE_PATH + "fhir" + "/" + this.fhirVersion + "/" + "Questionnaire/$next-question"; + // this is a temp fix for adaptive forms + // TODO: figure out what to do about next-question standardization. + let qformUrl = this.props.appContext.questionnaire; + if(qformUrl) { + const urlArray = qformUrl.split('/'); + urlArray.pop(); + qformUrl = urlArray.join('/'); + } else { + qformUrl = 'http://localhost:8090/fhir/r4/Questionnaire' + } + const url = `${qformUrl}/$next-question`; const currentQuestionnaireResponse = window.LForms.Util.getFormFHIRData('QuestionnaireResponse', this.fhirVersion, "#formContainer");; //const mergedResponse = this.mergeResponseForSameLinkId(currentQuestionnaireResponse); @@ -228,13 +239,14 @@ export default class QuestionnaireForm extends Component { let count = 0; partialResponses.entry.forEach(bundleEntry => { - let questionnaireId = null; - if(bundleEntry.resource.contained) { - questionnaireId = bundleEntry.resource?.contained[0]?.id; + let idMatch = false; + if(bundleEntry.resource.contained){ + const questionnaireId = bundleEntry.resource?.contained[0].id; + idMatch = this.props.qform.id === questionnaireId; } - const questionaireIdUrl = bundleEntry.resource.questionnaire; + const questionnaireIdUrl = bundleEntry.resource.questionnaire; - if (this.props.qform.id === questionnaireId || questionaireIdUrl.includes(this.props.qform.id)) { + if ( idMatch || questionnaireIdUrl.includes(this.props.qform.id)) { count = count + 1; // add the option to the popupOptions let date = new Date(bundleEntry.resource.authored); @@ -371,7 +383,7 @@ export default class QuestionnaireForm extends Component { e.url == "http://hl7.org/fhir/StructureDefinition/cqf-expression" || e.url == "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-initialExpression" ); - if (isGtable && containsValueExpression) { + if (isGtable && containsValueExpression && !this.props.standalone) { // check if the prepopulationResult contains any value // if yes, then need to add corresponding sub-items then provide the answer // need to figure out which value is provided from the prepopulationResult though @@ -977,7 +989,14 @@ export default class QuestionnaireForm extends Component { } console.log(requestOptions); - let url = this.FHIR_PREFIX + this.fhirVersion + "/QuestionnaireResponse"; + let url = this.props.appContext.questionnaire; + if(url) { + const urlArray = url.split('/'); + url = urlArray.slice(0, -2).join('/'); + } else { + url = 'http://localhost:8090/fhir/r4' + } + url = url + "/QuestionnaireResponse"; console.log(url); fetch(url, requestOptions).then(handleFetchErrors).then(r => { let msg = "QuestionnaireResponse sent to Payer"; diff --git a/src/launch.js b/src/launch.js index f50c2af6..eb311e58 100644 --- a/src/launch.js +++ b/src/launch.js @@ -138,6 +138,7 @@ function callback(log) { "launch=" + encodeURIComponent(log.launchContextId); } + updateLog(log); window.location.href = authRedirect; } diff --git a/src/util/fetchArtifacts.js b/src/util/fetchArtifacts.js index f8f60275..4ffd548a 100644 --- a/src/util/fetchArtifacts.js +++ b/src/util/fetchArtifacts.js @@ -1,6 +1,6 @@ import "isomorphic-fetch"; import { buildFhirUrl, isRequestReference } from "./util"; -function fetchArtifactsOperation(order, coverage, questionnaire, smart, consoleLog) { +function fetchArtifactsOperation(order, coverage, questionnaire, smart, consoleLog, containedQuestionnaire) { // fetch from operation // parse return parameters similar to function below return new Promise(function(resolve, reject) { @@ -46,9 +46,16 @@ function fetchArtifactsOperation(order, coverage, questionnaire, smart, consoleL .then((e)=> {return e.json()}).then((result) => { // TODO: Handle multiple questionnaires const bundle = result.parameter[0].resource.entry; - const questionnaire = bundle.find((e) => e.resource.resourceType === "Questionnaire")?.resource; + let questionnaire; - retVal.questionnaire = questionnaire; + if (containedQuestionnaire) { + retVal.questionnaire = containedQuestionnaire; + questionnaire = containedQuestionnaire; + } else { + questionnaire = bundle.find((e) => e.resource.resourceType === "Questionnaire")?.resource; + retVal.questionnaire = questionnaire; + } + retVal.isAdaptiveFormWithoutExtension = questionnaire.extension && questionnaire.extension.length > 0; findQuestionnaireEmbeddedCql(questionnaire.item); diff --git a/src/util/util.js b/src/util/util.js index e366c3ca..c544c96c 100644 --- a/src/util/util.js +++ b/src/util/util.js @@ -80,7 +80,9 @@ function postToLogs(log, callback) { logRequest.open("POST", "../logs"); logRequest.setRequestHeader("Content-Type", "application/json"); logRequest.onload = function() { - callback(JSON.parse(logRequest.responseText)); + if(callback) { + callback(JSON.parse(logRequest.responseText)); + } }; logRequest.send(JSON.stringify(log)); }