From 4f6432b241e9c143e0d139fea9d7895ceeb31928 Mon Sep 17 00:00:00 2001
From: Liu <yuliu@mitre.org>
Date: Mon, 4 Oct 2021 11:07:37 -0400
Subject: [PATCH 01/30] add endpoint for next question

---
 .../org/cdshooks/CoverageRequirements.java    |  9 +++++
 .../cdshooks/services/crd/CdsService.java     |  9 ++++-
 .../services/crd/r4/OrderSignService.java     |  8 ++++
 .../controllers/QuestionnaireController.java  | 40 +++++++++++++++++++
 4 files changed, 65 insertions(+), 1 deletion(-)
 create mode 100644 server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java

diff --git a/resources/src/main/java/org/cdshooks/CoverageRequirements.java b/resources/src/main/java/org/cdshooks/CoverageRequirements.java
index 28be93d9b..135e3b5b6 100644
--- a/resources/src/main/java/org/cdshooks/CoverageRequirements.java
+++ b/resources/src/main/java/org/cdshooks/CoverageRequirements.java
@@ -12,6 +12,7 @@ public class CoverageRequirements {
   private String questionnairePARequestUri;
   private String questionnairePlanOfCareUri;
   private String questionnaireDispenseUri;
+  private String questionnaireAdditionalUri;
   private String requestId;
   private boolean priorAuthRequired;
   private boolean documentationRequired;
@@ -137,4 +138,12 @@ public CoverageRequirements setDocumentationRequired(boolean documentationRequir
     this.documentationRequired = documentationRequired;
     return this;
   }
+
+  public String getQuestionnaireAdditionalUri() {
+    return questionnaireAdditionalUri;
+  }
+
+  public void setQuestionnaireAdditionalUri(String questionnaireAdditionalUri) {
+    this.questionnaireAdditionalUri = questionnaireAdditionalUri;
+  }
 }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
index c3439d028..8d5badbb5 100755
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
@@ -177,7 +177,8 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
               || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireProgressNoteUri())
               || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePARequestUri())
               || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePlanOfCareUri())
-              || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireDispenseUri()))) {
+              || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireDispenseUri())
+              || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireAdditionalUri()))) {
             List<Link> smartAppLinks = createQuestionnaireLinks(request, applicationBaseUrl, lookupResult, results);
             response.addCard(CardBuilder.transform(results, smartAppLinks));
 
@@ -265,6 +266,12 @@ private List<Link> createQuestionnaireLinks(requestTypeT request, URL applicatio
           coverageRequirements.getQuestionnaireDispenseUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
           coverageRequirements.isPriorAuthRequired(), "Dispense Form"));
     }
+
+    if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireAdditionalUri())) {
+      listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
+          coverageRequirements.getQuestionnaireAdditionalUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
+          coverageRequirements.isPriorAuthRequired(), "Additional Form"));
+    }
     return listOfLinks;
   }
 
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSignService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSignService.java
index 304a4e663..5c26c4fe7 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSignService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSignService.java
@@ -151,6 +151,14 @@ protected CqlResultsForCard executeCqlAndGetRelevantResults(Context context, Str
         logger.info("-- No PA Request questionnaire defined");
       }
 
+      try {
+        if (evaluateStatement("RESULT_QuestionnaireAdditionalUri", context) != null) {
+          coverageRequirements.setQuestionnaireAdditionalUri(evaluateStatement("RESULT_QuestionnaireAdditionalUri", context).toString());
+        }
+      } catch (Exception e) {
+        logger.info("-- No additional questionnaire defined");
+      }
+
       // process the alternative therapies
       try {
         if (evaluateStatement("ALTERNATIVE_THERAPY", context) != null) {
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
new file mode 100644
index 000000000..650ceb131
--- /dev/null
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -0,0 +1,40 @@
+package org.hl7.davinci.endpoint.controllers;
+
+import org.hl7.davinci.FhirResourceInfo;
+import org.hl7.davinci.endpoint.Application;
+import org.hl7.davinci.endpoint.Utils;
+import org.hl7.davinci.endpoint.database.FhirResource;
+import org.hl7.davinci.endpoint.database.FhirResourceRepository;
+import org.hl7.davinci.endpoint.files.FileResource;
+import org.hl7.davinci.endpoint.files.FileStore;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.logging.Logger;
+import javax.servlet.http.HttpServletRequest;
+
+@CrossOrigin
+@RestController
+@RequestMapping("/Questionnaire")
+public class QuestionnaireController {
+    private static Logger logger = Logger.getLogger(Application.class.getName());
+
+    @PostMapping(value = "/$next-question", consumes = { MediaType.APPLICATION_JSON_VALUE, "application/fhir+json" })
+    public ResponseEntity<String> retrieveNextQuestion(HttpServletRequest request, HttpEntity<String> entity) {
+        return getNextQuestionOperation(entity.getBody(), request);
+    }
+
+    private ResponseEntity<String> getNextQuestionOperation(String body,  HttpServletRequest request) {
+        logger.info("POST /Questionnaire/$next-question fhir+" );
+        return ResponseEntity.status(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
+        .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
+        .body("Request received");
+    }
+}

From 8c4651ab1ff4b3eb3f1feeb1580cc3ff9c2450d5 Mon Sep 17 00:00:00 2001
From: Liu <yuliu@mitre.org>
Date: Thu, 7 Oct 2021 11:43:02 -0400
Subject: [PATCH 02/30] add item to questionnaire

---
 .../controllers/QuestionnaireController.java  | 86 +++++++++++++++++--
 1 file changed, 80 insertions(+), 6 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index 650ceb131..d00ff015a 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -7,8 +7,9 @@
 import org.hl7.davinci.endpoint.database.FhirResourceRepository;
 import org.hl7.davinci.endpoint.files.FileResource;
 import org.hl7.davinci.endpoint.files.FileStore;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IDomainResource;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.Resource;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
@@ -16,10 +17,26 @@
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
+import ca.uhn.fhir.context.FhirContext;
+import org.hl7.davinci.r4.FhirComponents;
+import ca.uhn.fhir.parser.IParser;
+
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
 import java.util.logging.Logger;
 import javax.servlet.http.HttpServletRequest;
 
+import org.hl7.fhir.r4.model.Coding;
+import org.hl7.fhir.r4.model.Questionnaire;
+import org.hl7.fhir.r4.model.QuestionnaireResponse;
+import org.hl7.fhir.r4.model.Resource;
+import org.hl7.fhir.r4.model.ResourceType;
+import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
+import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
+import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
+
 @CrossOrigin
 @RestController
 @RequestMapping("/Questionnaire")
@@ -31,10 +48,67 @@ public ResponseEntity<String> retrieveNextQuestion(HttpServletRequest request, H
         return getNextQuestionOperation(entity.getBody(), request);
     }
 
-    private ResponseEntity<String> getNextQuestionOperation(String body,  HttpServletRequest request) {
-        logger.info("POST /Questionnaire/$next-question fhir+" );
-        return ResponseEntity.status(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
-        .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
-        .body("Request received");
+    private ResponseEntity<String> getNextQuestionOperation(String body, HttpServletRequest request) {
+        logger.info("POST /Questionnaire/$next-question fhir+");
+
+        FhirContext ctx = new FhirComponents().getFhirContext();
+        IParser parser = ctx.newJsonParser();
+
+        IDomainResource domainResource = (IDomainResource) parser.parseResource(QuestionnaireResponse.class, body);
+        if (!domainResource.fhirType().equalsIgnoreCase("QuestionnaireResponse")) {
+            logger.warning("unsupported resource type: ");
+            HttpStatus status = HttpStatus.BAD_REQUEST;
+            MediaType contentType = MediaType.TEXT_PLAIN;
+            return ResponseEntity.status(status).contentType(contentType).body("Bad Request");
+        } else {
+            logger.info(" ---- Resource received " + domainResource.toString());
+            QuestionnaireResponse inputResource = (QuestionnaireResponse) domainResource;
+            String fragmentId = inputResource.getQuestionnaire();
+            List<Resource> containedResource = inputResource.getContained();
+            Questionnaire inQuestionnaire = null;
+            for (int i = 0; i < containedResource.size(); i++) {
+                Resource item = containedResource.get(i);
+                if (item.getResourceType().equals(ResourceType.Questionnaire)) {
+                    Questionnaire inputQuestionnaire = (Questionnaire) item;
+                    if (inputQuestionnaire.getId().equals(fragmentId)) {
+                        inQuestionnaire = inputQuestionnaire;
+                        break;
+                    }
+                }
+            }
+
+            if (inQuestionnaire != null) {
+                // TODO retrieve the questions and set it in the contained Questionnaire
+                logger.info("--- Get next question for questionnaire " + inQuestionnaire.getId());
+                logger.info("---- Get meta profile " + inQuestionnaire.getMeta().getProfile().get(0).getValue());
+
+                Questionnaire.QuestionnaireItemComponent orderReason = inQuestionnaire.addItem().setLinkId("1");
+                orderReason.setText("order Reason");
+                orderReason.setType(QuestionnaireItemType.CHOICE);
+                orderReason.addAnswerOption(new QuestionnaireItemAnswerOptionComponent()
+                        .setValue(new Coding().setCode("Initial or original order for certification")));
+                orderReason.addAnswerOption(new QuestionnaireItemAnswerOptionComponent()
+                        .setValue(new Coding().setCode("Change in statue")));
+                orderReason.addAnswerOption(new QuestionnaireItemAnswerOptionComponent()
+                        .setValue(new Coding().setCode("Revision or change in equipment")));
+                orderReason.addAnswerOption(
+                        new QuestionnaireItemAnswerOptionComponent().setValue(new Coding().setCode("Replacement")));
+
+                List<Resource> newContainedList = new ArrayList<>();
+                newContainedList.add(inQuestionnaire);
+                // inputResource.setContained(newContainedList);
+                inputResource.addContained(inQuestionnaire);
+                String formattedResourceString = ctx.newJsonParser().encodeResourceToString(inputResource);
+
+                return ResponseEntity.status(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
+                        .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
+                        .body(formattedResourceString);
+            } else {
+                return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON)
+                        .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
+                        .body("Invalid input questionnaire");
+            }
+
+        }
     }
 }

From a17a1cc4ee1c206d1b548e76009771e14320db11 Mon Sep 17 00:00:00 2001
From: KeeyanGhoreshi <keeyan.ghoreshi@uconn.edu>
Date: Thu, 14 Oct 2021 17:11:54 -0400
Subject: [PATCH 03/30] Update gradle to newest version

---
 gradle/wrapper/gradle-wrapper.properties |  2 +-
 operations/build.gradle                  |  8 ++--
 resources/build.gradle                   | 26 +++++------
 server/build.gradle                      | 56 ++++++++++++------------
 4 files changed, 47 insertions(+), 45 deletions(-)

diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a4b442974..ffed3a254 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/operations/build.gradle b/operations/build.gradle
index 7f536fdf0..600a57e9e 100644
--- a/operations/build.gradle
+++ b/operations/build.gradle
@@ -1,7 +1,7 @@
-apply plugin: 'maven'
+apply plugin: 'maven-publish'
 
 dependencies {
-    compile project(':resources')
-    compile 'ca.uhn.hapi.fhir:hapi-fhir-base:5.3.0'
-    compile 'ca.uhn.hapi.fhir:hapi-fhir-structures-r4:5.3.0'
+    implementation project(':resources')
+    implementation 'ca.uhn.hapi.fhir:hapi-fhir-base:5.3.0'
+    implementation 'ca.uhn.hapi.fhir:hapi-fhir-structures-r4:5.3.0'
 }
diff --git a/resources/build.gradle b/resources/build.gradle
index c43254ba9..692387e0c 100644
--- a/resources/build.gradle
+++ b/resources/build.gradle
@@ -1,22 +1,22 @@
-apply plugin: 'maven'
+apply plugin: 'maven-publish'
 
 dependencies {
-    compile group: 'com.helger', name: 'ph-schematron', version: '4.1.0'
-    compile 'com.googlecode.json-simple:json-simple:1.1.1'
+    implementation group: 'com.helger', name: 'ph-schematron', version: '4.1.0'
+    implementation 'com.googlecode.json-simple:json-simple:1.1.1'
 
-    compile 'ca.uhn.hapi.fhir:hapi-fhir-base:5.3.0'
-    compile 'ca.uhn.hapi.fhir:hapi-fhir-structures-r4:5.3.0'
-    compile group: 'ca.uhn.hapi.fhir', name: 'hapi-fhir-validation-resources-r4', version: '5.3.0'
-    compile 'ca.uhn.hapi.fhir:hapi-fhir-validation:5.3.0'
+    implementation 'ca.uhn.hapi.fhir:hapi-fhir-base:5.3.0'
+    implementation 'ca.uhn.hapi.fhir:hapi-fhir-structures-r4:5.3.0'
+    implementation group: 'ca.uhn.hapi.fhir', name: 'hapi-fhir-validation-resources-r4', version: '5.3.0'
+    implementation 'ca.uhn.hapi.fhir:hapi-fhir-validation:5.3.0'
 
-    compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.1'
-    compile group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.12.1'
-    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.1'
+    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.12.1'
+    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: '2.12.1'
+    implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.1'
 
-    compile group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'
-    compile group: 'org.springframework', name: 'spring-context', version: '5.0.8.RELEASE'
+    implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'
+    implementation group: 'org.springframework', name: 'spring-context', version: '5.0.8.RELEASE'
 
-    compile('ca.uhn.hapi.fhir:hapi-fhir-jpaserver-base:3.6.0'){
+    implementation('ca.uhn.hapi.fhir:hapi-fhir-jpaserver-base:3.6.0'){
             exclude group: 'org.thymeleaf'
     }
 }
diff --git a/server/build.gradle b/server/build.gradle
index a4ba77c2f..72e0916d0 100644
--- a/server/build.gradle
+++ b/server/build.gradle
@@ -5,7 +5,6 @@ buildscript {
     repositories {
         mavenCentral()
         maven { url 'https://repo.spring.io/snapshot' }
-        maven { url 'http://repo.jenkins-ci.org/releases/' }
 
     }
     dependencies {
@@ -13,7 +12,7 @@ buildscript {
     }
 }
 
-apply plugin: 'maven'
+apply plugin: 'maven-publish'
 //apply plugin: 'war'
 apply plugin: 'java'
 apply plugin: 'org.springframework.boot'
@@ -23,48 +22,51 @@ apply plugin: 'io.spring.dependency-management'
 
 dependencies {
 
-    compile project(':resources')
-    compile project(':operations')
+    implementation project(':resources')
+    implementation project(':operations')
+    implementation 'com.googlecode.json-simple:json-simple:1.1.1'
+    implementation 'com.google.code.gson:gson:2.8.8'
 
-    compile('org.springframework.boot:spring-boot-starter-actuator')
-    compile('org.springframework.boot:spring-boot-starter-data-jpa')
-    compile('org.springframework.boot:spring-boot-starter-data-rest')
-    compile('org.springframework.boot:spring-boot-starter-thymeleaf')
-    compile('org.springframework.boot:spring-boot-starter-security')
+    implementation('org.springframework.boot:spring-boot-starter-actuator')
+    implementation('org.springframework.boot:spring-boot-starter-data-jpa')
+    implementation('org.springframework.boot:spring-boot-starter-data-rest')
+    implementation('org.springframework.boot:spring-boot-starter-thymeleaf')
+    implementation('org.springframework.boot:spring-boot-starter-security')
+    implementation('org.springframework.boot:spring-boot-starter-validation')
 
-    compile('com.h2database:h2')
+    implementation('com.h2database:h2')
 
-    compile("io.jsonwebtoken:jjwt:0.7.0")
+    implementation("io.jsonwebtoken:jjwt:0.7.0")
 
-//    compile 'javax.servlet:javax.servlet-api:3.1.0'
+//    implementation 'javax.servlet:javax.servlet-api:3.1.0'
 
-    compile 'commons-beanutils:commons-beanutils:1.9.3'
+    implementation 'commons-beanutils:commons-beanutils:1.9.3'
 
-    testCompile('org.springframework.boot:spring-boot-starter-test')
-    testCompile "com.github.tomakehurst:wiremock-standalone:2.18.0"
-    testCompile('org.springframework.boot:spring-boot-starter-test')
+    testImplementation('org.springframework.boot:spring-boot-starter-test')
+    testImplementation "com.github.tomakehurst:wiremock-standalone:2.18.0"
+    testImplementation('org.springframework.boot:spring-boot-starter-test')
 
-    compile 'ca.uhn.hapi.fhir:hapi-fhir-base:5.3.0'
-    compile 'ca.uhn.hapi.fhir:hapi-fhir-structures-r4:5.3.0'
+    implementation 'ca.uhn.hapi.fhir:hapi-fhir-base:5.3.0'
+    implementation 'ca.uhn.hapi.fhir:hapi-fhir-structures-r4:5.3.0'
 
-    compile 'com.jayway.jsonpath:json-path:2.4.0'
-    compile 'joda-time:joda-time:2.10.5'
+    implementation 'com.jayway.jsonpath:json-path:2.4.0'
+    implementation 'joda-time:joda-time:2.10.5'
 
     //cql stuff
-    compile (group: 'org.opencds.cqf.cql', name: 'engine', version: '1.5.1') {
+    implementation (group: 'org.opencds.cqf.cql', name: 'engine', version: '1.5.1') {
         exclude group: 'org.slf4j', module: 'slf4j-log4j12'
     }
-    compile (group: 'org.opencds.cqf.cql', name: 'engine.fhir', version: '1.5.1') {
+    implementation (group: 'org.opencds.cqf.cql', name: 'engine.fhir', version: '1.5.1') {
         exclude group: 'org.slf4j', module: 'slf4j-log4j12'
     }
 
-    //Use locally compiled cql libs (engine and fhir)
-    compile fileTree(dir: 'libs', include: ['*.jar'])
-    compile group: 'info.cqframework', name: 'cql-to-elm', version: '1.5.1'
+    //Use locally implementationd cql libs (engine and fhir)
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation group: 'info.cqframework', name: 'cql-to-elm', version: '1.5.1'
 
-    compile 'org.zeroturnaround:zt-zip:1.13'
+    implementation 'org.zeroturnaround:zt-zip:1.13'
 
-    compile group: 'org.kohsuke', name:'github-api', version:'1.77'
+    implementation group: 'org.kohsuke', name:'github-api', version:'1.77'
 }
 
 task buildReact(type:Exec) {

From 608741ca524e59788782097bad9c9cf27d7a0be1 Mon Sep 17 00:00:00 2001
From: Liu <yuliu@mitre.org>
Date: Mon, 18 Oct 2021 16:57:28 -0400
Subject: [PATCH 04/30] fix merge issue

---
 .../cdshooks/services/crd/CdsService.java     | 39 ++++++++++---------
 1 file changed, 20 insertions(+), 19 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
index 17c0e9a0d..f86fc096f 100755
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
@@ -170,25 +170,26 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
 
         if (results.getCoverageRequirements().getApplies()) {
 
-          if ((coverageRequirements.isDocumentationRequired() || coverageRequirements.isPriorAuthRequired())
-              && (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireOrderUri())
-                  || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireFaceToFaceUri())
-                  || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireLabUri())
-                  || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireProgressNoteUri())
-                  || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePARequestUri())
-                  || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePlanOfCareUri())
-                  || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireDispenseUri())
-                  || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireAdditionalUri()))) {
-            List<Link> smartAppLinks = createQuestionnaireLinks(request, applicationBaseUrl, lookupResult, results);
-            response.addCard(CardBuilder.transform(results, smartAppLinks));
-
-            // add a card for an alternative therapy if there is one
-            if (results.getAlternativeTherapy().getApplies()) {
-              try {
-                response.addCard(CardBuilder.alternativeTherapyCard(results.getAlternativeTherapy(),
-                    results.getRequest(), fhirComponents));
-              } catch (RuntimeException e) {
-                logger.warn("Failed to process alternative therapy: " + e.getMessage());
+          if (coverageRequirements.isDocumentationRequired() || coverageRequirements.isPriorAuthRequired()) {
+            if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireOrderUri())
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireFaceToFaceUri())
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireLabUri())
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireProgressNoteUri())
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePARequestUri())
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePlanOfCareUri())
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireDispenseUri())
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireAdditionalUri())) {
+              List<Link> smartAppLinks = createQuestionnaireLinks(request, applicationBaseUrl, lookupResult, results);
+              response.addCard(CardBuilder.transform(results, smartAppLinks));
+
+              // add a card for an alternative therapy if there is one
+              if (results.getAlternativeTherapy().getApplies()) {
+                try {
+                  response.addCard(CardBuilder.alternativeTherapyCard(results.getAlternativeTherapy(),
+                      results.getRequest(), fhirComponents));
+                } catch (RuntimeException e) {
+                  logger.warn("Failed to process alternative therapy: " + e.getMessage());
+                }
               }
             } else {
               logger.warn("Unspecified Questionnaire URI; summary card sent to client");

From eb61b07de99d6ae67205602f7cc1ac212e9a50c9 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Fri, 22 Oct 2021 13:35:17 -0400
Subject: [PATCH 05/30] Working version of next-question (assuming there is
 only one parent question)

---
 .../controllers/QuestionnaireController.java  | 211 ++++++++++++++----
 ...Questions-HomeOxygenTherapyAdditional.json |  89 ++++++++
 2 files changed, 257 insertions(+), 43 deletions(-)
 create mode 100644 server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index d00ff015a..f8e600bfa 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -1,15 +1,7 @@
 package org.hl7.davinci.endpoint.controllers;
 
-import org.hl7.davinci.FhirResourceInfo;
 import org.hl7.davinci.endpoint.Application;
-import org.hl7.davinci.endpoint.Utils;
-import org.hl7.davinci.endpoint.database.FhirResource;
-import org.hl7.davinci.endpoint.database.FhirResourceRepository;
-import org.hl7.davinci.endpoint.files.FileResource;
-import org.hl7.davinci.endpoint.files.FileStore;
-import org.hl7.fhir.instance.model.api.IBaseResource;
 import org.hl7.fhir.instance.model.api.IDomainResource;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
@@ -19,41 +11,146 @@
 
 import ca.uhn.fhir.context.FhirContext;
 import org.hl7.davinci.r4.FhirComponents;
+
+import ca.uhn.fhir.parser.DataFormatException;
 import ca.uhn.fhir.parser.IParser;
 
-import java.io.IOException;
-import java.util.ArrayList;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Optional;
+import java.util.Map;
 import java.util.logging.Logger;
 import javax.servlet.http.HttpServletRequest;
 
-import org.hl7.fhir.r4.model.Coding;
 import org.hl7.fhir.r4.model.Questionnaire;
 import org.hl7.fhir.r4.model.QuestionnaireResponse;
 import org.hl7.fhir.r4.model.Resource;
 import org.hl7.fhir.r4.model.ResourceType;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
-import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
+import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
+import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
+
+// --- ORDER OF RESPONSE-REQUEST OPERATIONS
+// (REQUEST) External user sends the initial QuestionnaireResponse JSON that contains which questionnaire it would like to trigger as n element the "contained" field.
+// (RESPONSE) QuestionnaireController adds the first question with its answerResponse options (with its linkId and text) to the JSON in QuestionnaireResponse.contained.item[] and sends it back.
+// (REQUEST) External user adds their answer to the question to the JSON in QuestionnaireResponse.item[] and sends it back.
+// (RESPONSE) QuestionnaireController takes that response and adds the next indicated question to the JSON in QuestionnaireResponse.contained.item[] and sends it back.
+// Repeat intil QuestionnaireController reaches a leaf-node, then it sets the status to "completed" from "in-progress"
+// Ultimately, The QuestionnaireController responses add ONLY to the QuestionnaireResponse.contained.item[]. The external requester adds answers to QuestionnaireResponse.item[] and includes the associated linkid and text.
 
 @CrossOrigin
 @RestController
 @RequestMapping("/Questionnaire")
 public class QuestionnaireController {
+
+    /**
+     * A class that demos a tree to define next questions based on responses.
+     */
+    private class AdaptiveQuestionnaireTree {
+        private NextQuestionNode root;
+    
+        /**
+         * Constructor.
+         * @param inputQuestionnaire    The input questionnaire from the CDS-Library.
+         */
+        public AdaptiveQuestionnaireTree(Questionnaire inputQuestionnaire) {
+            // Build child nodes. (Does not yet include building out children's child nodes. Assumes only one question with followup questions.)
+            Map<String, QuestionnaireItemComponent> childMap = new HashMap<String, QuestionnaireItemComponent>();
+            // Map of child question IDs for the first question that map to their associated possible response.
+            Map<String, String> childIdsToResponses = new HashMap<String, String>();
+            // This loop iterates over the possible answer options of the first item in the inputQuestionnaire, which is assumed to be the only parent question.
+            for(QuestionnaireItemAnswerOptionComponent answerOption : inputQuestionnaire.getItemFirstRep().getAnswerOption()) {
+                // The Id of this answer response's next question.
+                String answerNextQuestionId = answerOption.getModifierExtensionFirstRep().getUrl();
+                // The response that indicates this answer to the question.
+                String possibleAnswerResponse = answerOption.getValueCoding().getCode();
+                System.out.println("LLLL:" + answerNextQuestionId);
+                System.out.println("AAAA:" + possibleAnswerResponse);
+                // Add the key-value pair of next question id to its assocated answer response.
+                childIdsToResponses.put(answerNextQuestionId, possibleAnswerResponse);
+            }
+
+            // Extract the children and add them to their parent question nodes.
+            for(QuestionnaireItemComponent childQuestion : inputQuestionnaire.getItem()){
+                String linkId = childQuestion.getLinkId();
+                System.out.println("SSSSS:" + linkId);
+                System.out.println("QQQQQ:" + childIdsToResponses.keySet());
+                if(childIdsToResponses.containsKey(linkId)){
+                    // This question is a child of the parent question. Add it to the parent's map of children as a key-value pair of response-childQuestion.
+                    childMap.put(childIdsToResponses.get(linkId), childQuestion);
+                }
+            }
+            // Create the root node with its question and children.
+            root = new NextQuestionNode(inputQuestionnaire, childMap);
+        }
+
+        /**
+         * Returns the next question based on the response to the current question.
+         * @param response  The response given to this question.
+         * @return
+         */
+        public QuestionnaireItemComponent getNextQuestionForResponse(String response){
+            if(!root.children.containsKey(response)){
+                throw new NullPointerException("Not a valid response for this question: \'" + response + "\''. Possible responses for this question: \'" + root.children.keySet() + "\''.");
+            }
+            return root.children.get(response);//.data;
+        }
+
+        /**
+         * Returns whether this is a leaf node (needs work, but is fine for demo purposes).
+         * @param response
+         * @return
+         */
+        public boolean isLeafNode(String response) {
+            return !this.getNextQuestionForResponse(response).hasAnswerOption();
+        }
+    
+        /**
+         * Inner class that describes a node of the tree.
+         */
+        private class NextQuestionNode {
+            private Questionnaire data; // To be used to contain future answer options for multiple subquestions?
+            private Map<String, QuestionnaireItemComponent> children;
+
+            public NextQuestionNode(Questionnaire data, Map<String, QuestionnaireItemComponent> children) {
+                this.data = data;
+                this.children = children;
+            }
+        }
+    }
+
+    // Logger.
     private static Logger logger = Logger.getLogger(Application.class.getName());
+    // Tree that tracks the questions.
+    private AdaptiveQuestionnaireTree questionnaireTree;
 
+    /**
+     * 
+     * @param request
+     * @param entity
+     * @return
+     */
     @PostMapping(value = "/$next-question", consumes = { MediaType.APPLICATION_JSON_VALUE, "application/fhir+json" })
     public ResponseEntity<String> retrieveNextQuestion(HttpServletRequest request, HttpEntity<String> entity) {
         return getNextQuestionOperation(entity.getBody(), request);
     }
 
+    /**
+     * 
+     * @param body
+     * @param request
+     * @return
+     */
     private ResponseEntity<String> getNextQuestionOperation(String body, HttpServletRequest request) {
         logger.info("POST /Questionnaire/$next-question fhir+");
 
         FhirContext ctx = new FhirComponents().getFhirContext();
         IParser parser = ctx.newJsonParser();
 
+        // Parses the body.
         IDomainResource domainResource = (IDomainResource) parser.parseResource(QuestionnaireResponse.class, body);
         if (!domainResource.fhirType().equalsIgnoreCase("QuestionnaireResponse")) {
             logger.warning("unsupported resource type: ");
@@ -62,44 +159,72 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
             return ResponseEntity.status(status).contentType(contentType).body("Bad Request");
         } else {
             logger.info(" ---- Resource received " + domainResource.toString());
-            QuestionnaireResponse inputResource = (QuestionnaireResponse) domainResource;
-            String fragmentId = inputResource.getQuestionnaire();
-            List<Resource> containedResource = inputResource.getContained();
-            Questionnaire inQuestionnaire = null;
+            QuestionnaireResponse inputQuestionnaireResponse = (QuestionnaireResponse) domainResource;
+            String fragmentId = inputQuestionnaireResponse.getQuestionnaire();
+            List<Resource> containedResource = inputQuestionnaireResponse.getContained();
+            Questionnaire inputQuestionnaireFromRequest = null;
             for (int i = 0; i < containedResource.size(); i++) {
                 Resource item = containedResource.get(i);
                 if (item.getResourceType().equals(ResourceType.Questionnaire)) {
-                    Questionnaire inputQuestionnaire = (Questionnaire) item;
-                    if (inputQuestionnaire.getId().equals(fragmentId)) {
-                        inQuestionnaire = inputQuestionnaire;
+                    Questionnaire checkInputQuestionnaire = (Questionnaire) item;
+                    if (checkInputQuestionnaire.getId().equals(fragmentId)) {
+                        inputQuestionnaireFromRequest = checkInputQuestionnaire;
                         break;
                     }
                 }
             }
 
-            if (inQuestionnaire != null) {
-                // TODO retrieve the questions and set it in the contained Questionnaire
-                logger.info("--- Get next question for questionnaire " + inQuestionnaire.getId());
-                logger.info("---- Get meta profile " + inQuestionnaire.getMeta().getProfile().get(0).getValue());
-
-                Questionnaire.QuestionnaireItemComponent orderReason = inQuestionnaire.addItem().setLinkId("1");
-                orderReason.setText("order Reason");
-                orderReason.setType(QuestionnaireItemType.CHOICE);
-                orderReason.addAnswerOption(new QuestionnaireItemAnswerOptionComponent()
-                        .setValue(new Coding().setCode("Initial or original order for certification")));
-                orderReason.addAnswerOption(new QuestionnaireItemAnswerOptionComponent()
-                        .setValue(new Coding().setCode("Change in statue")));
-                orderReason.addAnswerOption(new QuestionnaireItemAnswerOptionComponent()
-                        .setValue(new Coding().setCode("Revision or change in equipment")));
-                orderReason.addAnswerOption(
-                        new QuestionnaireItemAnswerOptionComponent().setValue(new Coding().setCode("Replacement")));
-
-                List<Resource> newContainedList = new ArrayList<>();
-                newContainedList.add(inQuestionnaire);
-                // inputResource.setContained(newContainedList);
-                inputResource.addContained(inQuestionnaire);
-                String formattedResourceString = ctx.newJsonParser().encodeResourceToString(inputResource);
+            if (inputQuestionnaireFromRequest != null) {
+
+                if(questionnaireTree == null){
+
+                    // Import the requested CDS-Library Questionnaire (Couldn't get CDS to work with it, just reading it in it locally for now. In future will need to be pulled from CDS.)
+                    Questionnaire cdsQuestionnaire = null;
+                    try {
+                        cdsQuestionnaire = (Questionnaire) parser.parseResource(Questionnaire.class, new FileReader(new File("/Users/rscalfani/Documents/code/drlsroot/CRD/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json")));
+                        logger.info("--- Imported Questionnaire " + cdsQuestionnaire.getId());
+                    } catch (DataFormatException e) {
+                        e.printStackTrace();
+                    } catch (FileNotFoundException e) {
+                        e.printStackTrace();
+                    }
+                    if(cdsQuestionnaire == null) {
+                        throw new RuntimeException("Requested CDS Questionnaire XXX was not imported and may not exist.");
+                    }
+                    // Pull the first question from the CDS Questionnaire (because we're assuming that there is only one parent question).
+                    QuestionnaireItemComponent currentQuestionItem = cdsQuestionnaire.getItemFirstRep();
+
+                    // Add the first Question item to the contained Questionnaire in the response/request QuestionnaireResponse JSON as part of the response.
+                    inputQuestionnaireFromRequest.addItem(currentQuestionItem);
+
+                    // Build the tree and don't expect any answers since we only just received the required questions.
+                    questionnaireTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
+                    System.out.println(inputQuestionnaireFromRequest.getItem());
+                    logger.info("--- Built Questionnaire Tree for " + inputQuestionnaireFromRequest.getId());
+
+                } else {
+                    // Get the first answer component object from the recieved resource.
+                    QuestionnaireResponseItemAnswerComponent answerComponent = inputQuestionnaireResponse.getItem().get(0).getAnswer().get(0);
+                    // Pull the string response the person gave.
+                    String response = answerComponent.getValueCoding().getCode();
+                    // Pull the resulting next question that the recieved response points to from the tree.
+                    QuestionnaireItemComponent result = questionnaireTree.getNextQuestionForResponse(response);
+                    // Add the next question to the QuestionnaireResponse.contained[0].item[].
+                    Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
+                    containedQuestionnaire.addItem(result);
+                    logger.info("--- Added next question for questionnaire " + inputQuestionnaireFromRequest.getId() + " for response " + response);
+                    // If this question is a leaf node and is the final question, set status to "completed"
+                    if(this.questionnaireTree.isLeafNode(response)){
+                        inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
+                        logger.info("--- Question leaf node reached, setting status to \"completed\".");
+                    }
+                }
 
+                logger.info("--- Get next question for questionnaire " + inputQuestionnaireFromRequest.getId());
+                logger.info("---- Get meta profile " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
+                
+                // Build and send the response.
+                String formattedResourceString = ctx.newJsonParser().encodeResourceToString(inputQuestionnaireResponse);
                 return ResponseEntity.status(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
                         .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
                         .body(formattedResourceString);
@@ -111,4 +236,4 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
 
         }
     }
-}
+}
\ No newline at end of file
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json b/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json
new file mode 100644
index 000000000..6c206db7c
--- /dev/null
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json
@@ -0,0 +1,89 @@
+{
+    "resourceType": "Questionnaire",
+    "id": "HomeOxygenTherapyAdditional",
+    "name": "HomeOxygenTherapyAdditional",
+    "description": "Questions for Home Oxygen Therapy Order Reason",
+    "meta": {
+        "profile": [
+            "http://hl7.org/fhir/StructureDefinition/cqf-questionnaire",
+            "http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-questionnaire-r4"
+        ]
+    },
+    "title": "Home Oxygen Therapy Additional Adaptive Form Questionnaire",
+    "status": "draft",
+    "subjectType": [
+        "Patient"
+    ],
+    "date": "2019-03-26",
+    "publisher": "Da Vinci DTR",
+    "item": [
+        {
+            "linkId": "1",
+            "text": "Order Reason",
+            "type": "choice",
+            "required": true,
+            "answerOption": [
+                {
+                    "modifierExtension": [
+                        {
+                            "url": "1.1"
+                        }
+                    ],
+                    "valueCoding": {
+                        "code": "Initial or original order for certification"
+                    }
+                },
+                {
+                    "modifierExtension": [
+                        {
+                            "url": "1.2"
+                        }
+                    ],
+                    "valueCoding": {
+                        "code": "Change in status"
+                    }
+                },
+                {
+                    "modifierExtension": [
+                        {
+                            "url": "1.3"
+                        }
+                    ],
+                    "valueCoding": {
+                        "code": "Revision or change in equipment"
+                    }
+                },
+                {
+                    "modifierExtension": [
+                        {
+                            "url": "1.4"
+                        }
+                    ],
+                    "valueCoding": {
+                        "code": "Replacement"
+                    }
+                }
+            ]
+        },
+        {
+            "linkId": "1.1",
+            "type": "display",
+            "text": "Submit the order form"
+        },
+        {
+            "linkId": "1.4",
+            "type": "display",
+            "text": "Replacement Reasoning, etc."
+        },
+        {
+            "linkId": "1.2",
+            "type": "display",
+            "text": "Status Reasoning, Patient relocated, etc."
+        },
+        {
+            "linkId": "1.3",
+            "type": "display",
+            "text": "Revision/Equipment change Reasoning, etc."
+        }
+    ]
+}
\ No newline at end of file

From d94c7cfc955cb7c7b99c0414020fac9d1328c907 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Fri, 22 Oct 2021 17:24:07 -0400
Subject: [PATCH 06/30] next-question tree can now have infiinite depth of
 questions and answer options.

---
 .../controllers/QuestionnaireController.java  | 163 ++++++++++++------
 ...Questions-HomeOxygenTherapyAdditional.json | 128 +++++++++++---
 2 files changed, 222 insertions(+), 69 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index f8e600bfa..f9795e339 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -22,6 +22,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
 import javax.servlet.http.HttpServletRequest;
 
 import org.hl7.fhir.r4.model.Questionnaire;
@@ -31,6 +33,7 @@
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
+import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
 
 // --- ORDER OF RESPONSE-REQUEST OPERATIONS
@@ -50,74 +53,132 @@ public class QuestionnaireController {
      * A class that demos a tree to define next questions based on responses.
      */
     private class AdaptiveQuestionnaireTree {
-        private NextQuestionNode root;
+        
+        // The current question (defiend within the node).
+        private AdaptiveQuestionnaireNode root;
     
         /**
-         * Constructor.
+         * Initial constructor that generates the beginning of the tree.
          * @param inputQuestionnaire    The input questionnaire from the CDS-Library.
          */
         public AdaptiveQuestionnaireTree(Questionnaire inputQuestionnaire) {
-            // Build child nodes. (Does not yet include building out children's child nodes. Assumes only one question with followup questions.)
-            Map<String, QuestionnaireItemComponent> childMap = new HashMap<String, QuestionnaireItemComponent>();
-            // Map of child question IDs for the first question that map to their associated possible response.
-            Map<String, String> childIdsToResponses = new HashMap<String, String>();
-            // This loop iterates over the possible answer options of the first item in the inputQuestionnaire, which is assumed to be the only parent question.
-            for(QuestionnaireItemAnswerOptionComponent answerOption : inputQuestionnaire.getItemFirstRep().getAnswerOption()) {
-                // The Id of this answer response's next question.
-                String answerNextQuestionId = answerOption.getModifierExtensionFirstRep().getUrl();
-                // The response that indicates this answer to the question.
-                String possibleAnswerResponse = answerOption.getValueCoding().getCode();
-                System.out.println("LLLL:" + answerNextQuestionId);
-                System.out.println("AAAA:" + possibleAnswerResponse);
-                // Add the key-value pair of next question id to its assocated answer response.
-                childIdsToResponses.put(answerNextQuestionId, possibleAnswerResponse);
-            }
 
-            // Extract the children and add them to their parent question nodes.
-            for(QuestionnaireItemComponent childQuestion : inputQuestionnaire.getItem()){
-                String linkId = childQuestion.getLinkId();
-                System.out.println("SSSSS:" + linkId);
-                System.out.println("QQQQQ:" + childIdsToResponses.keySet());
-                if(childIdsToResponses.containsKey(linkId)){
-                    // This question is a child of the parent question. Add it to the parent's map of children as a key-value pair of response-childQuestion.
-                    childMap.put(childIdsToResponses.get(linkId), childQuestion);
-                }
+            // Because of the nested structure of the input JSON, there can be only one super-parent questionitem in the inputQuestionnaire.
+            if(inputQuestionnaire.getItem().size() != 1){
+                throw new RuntimeException("An input Adaptive next-question questionnaire can have only one super-parent question.item, found " + inputQuestionnaire.getItem().size() + ".");
             }
-            // Create the root node with its question and children.
-            root = new NextQuestionNode(inputQuestionnaire, childMap);
+
+            // Top level parent question item. This is also the first question.
+            QuestionnaireItemComponent topQuestion = inputQuestionnaire.getItemFirstRep();
+
+            // Start the root building.
+            this.root = new AdaptiveQuestionnaireNode(topQuestion);
         }
 
         /**
-         * Returns the next question based on the response to the current question.
+         * Returns the next question based on the response to the current question. Also sets the next question based on that response.
          * @param response  The response given to this question.
          * @return
          */
         public QuestionnaireItemComponent getNextQuestionForResponse(String response){
             if(!root.children.containsKey(response)){
-                throw new NullPointerException("Not a valid response for this question: \'" + response + "\''. Possible responses for this question: \'" + root.children.keySet() + "\''.");
+                throw new RuntimeException("Not a valid response for question: \'" + this.root.questionItem.getText() + "\' with response \'" + response + "\'. Possible responses for this question: \'" + root.children.keySet() + "\'.");
             }
-            return root.children.get(response);//.data;
+            // Pull the current question.
+            QuestionnaireItemComponent currentQuestionnaireItem = this.root.getChildForResponse(response).questionItem;
+            // Set the new next question.
+            this.root = this.root.getChildForResponse(response);
+            // Return the prior current question.
+            return currentQuestionnaireItem;
         }
 
         /**
-         * Returns whether this is a leaf node (needs work, but is fine for demo purposes).
+         * Returns whether this has reached a leaf node.
          * @param response
          * @return
          */
-        public boolean isLeafNode(String response) {
-            return !this.getNextQuestionForResponse(response).hasAnswerOption();
+        public boolean reachedLeafNode() {
+            return this.root.isLeafNode();
+        }
+
+        /**
+         * Returns the linkid of the current question.
+         * @return
+         */
+        public String getCurrentQuestionId() {
+            return this.root.getQuestionId();
         }
     
         /**
          * Inner class that describes a node of the tree.
          */
-        private class NextQuestionNode {
-            private Questionnaire data; // To be used to contain future answer options for multiple subquestions?
-            private Map<String, QuestionnaireItemComponent> children;
+        private class AdaptiveQuestionnaireNode {
+            // Contains the current question of the node.
+            private QuestionnaireItemComponent questionItem;
+            // Map of (answerResponse->childQuestionItemNode) (The child could have answer options within it or be a leaf node. It does have a question item component though).
+            private Map<String, AdaptiveQuestionnaireNode> children;
+
+            public AdaptiveQuestionnaireNode(QuestionnaireItemComponent questionItem) {
+                this.questionItem = questionItem;
+
+                // The number of answer options should always equal the number of subquestion items.
+                if((this.questionItem.getAnswerOption().size() != this.questionItem.getItem().size())){
+                    throw new RuntimeException("There should be the same number of answer options as sub-items. Answer options: " + this.questionItem.getAnswerOption().size() + ", Sub-tems: " + this.questionItem.getItem().size());
+                }  
+
+                Map<String, String> childIdsToResponses = new HashMap<String, String>();
+                // This loop iterates over the possible answer options of this questionitem and links the linkId to its possible responses.
+                for(QuestionnaireItemAnswerOptionComponent answerOption : questionItem.getAnswerOption()) {
+                    // The Id of this answer response's next question.
+                    String answerNextQuestionId = answerOption.getModifierExtensionFirstRep().getUrl();
+                    // The response that indicates this answer to the question.
+                    String possibleAnswerResponse = answerOption.getValueCoding().getCode();
+                    // Check for issues.
+                    if(answerNextQuestionId == null || possibleAnswerResponse == null){
+                        throw new RuntimeException("Malformed Adaptive Questionnaire. Missing a questionID or answer response.");
+                    }
+                    // Add the key-value pair of next question id to its assocated answer response.
+                    childIdsToResponses.put(answerNextQuestionId, possibleAnswerResponse);
+                }
+
+                // Create the map of answerResponses->subQuestionItems
+                this.children = new HashMap<String, AdaptiveQuestionnaireNode>();
+                List<QuestionnaireItemComponent> subQuestionItems = questionItem.getItem();
+                for(QuestionnaireItemComponent subQuestionItem : subQuestionItems){
+                    // SubQuestion linkId.
+                    String subQuestionLinkId = subQuestionItem.getLinkId();
+                    // SubQuestion's associated response.
+                    String subQuestionResponse = childIdsToResponses.get(subQuestionLinkId);
+                    // Create a new node for this subQuestion.
+                    AdaptiveQuestionnaireNode subQuestionNode = new AdaptiveQuestionnaireNode(subQuestionItem);
+                    this.children.put(subQuestionResponse, subQuestionNode); // Should not be ID, should be response.
+                }
+            }
+
+            /**
+             * Returns whether this questionniare is a leaf node.
+             * @return
+             */
+            private boolean isLeafNode() {
+                return this.children.size() < 1;
+                // return !this.questionItem.hasAnswerOption();
+            }
+
+            /**
+             * Returns the child associated with this node for the given response.
+             * @param response
+             * @return
+             */
+            private AdaptiveQuestionnaireNode getChildForResponse(String response) {
+                return this.children.get(response);
+            }
 
-            public NextQuestionNode(Questionnaire data, Map<String, QuestionnaireItemComponent> children) {
-                this.data = data;
-                this.children = children;
+            /**
+             * Returns the question linkid for this node question.
+             * @return
+             */
+            public String getQuestionId() {
+                return this.questionItem.getLinkId();
             }
         }
     }
@@ -191,11 +252,11 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     if(cdsQuestionnaire == null) {
                         throw new RuntimeException("Requested CDS Questionnaire XXX was not imported and may not exist.");
                     }
-                    // Pull the first question from the CDS Questionnaire (because we're assuming that there is only one parent question).
-                    QuestionnaireItemComponent currentQuestionItem = cdsQuestionnaire.getItemFirstRep();
+                    // Pull the first question from the CDS Questionnaire because it should be the top-level question.
+                    QuestionnaireItemComponent topQuestionItem = cdsQuestionnaire.getItemFirstRep();
 
                     // Add the first Question item to the contained Questionnaire in the response/request QuestionnaireResponse JSON as part of the response.
-                    inputQuestionnaireFromRequest.addItem(currentQuestionItem);
+                    inputQuestionnaireFromRequest.addItem(topQuestionItem);
 
                     // Build the tree and don't expect any answers since we only just received the required questions.
                     questionnaireTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
@@ -203,18 +264,22 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     logger.info("--- Built Questionnaire Tree for " + inputQuestionnaireFromRequest.getId());
 
                 } else {
-                    // Get the first answer component object from the recieved resource.
-                    QuestionnaireResponseItemAnswerComponent answerComponent = inputQuestionnaireResponse.getItem().get(0).getAnswer().get(0);
+                    // Previous question Id
+                    String previousQuestionId = this.questionnaireTree.getCurrentQuestionId();
+                    // Get the answer component of the item with the previous question id from the recieved resource.
+                    List<QuestionnaireResponseItemComponent> allQuestions = inputQuestionnaireResponse.getItem();
+                    allQuestions = allQuestions.stream().filter((QuestionnaireResponseItemComponent item) -> item.getLinkId().equals(previousQuestionId)).collect(Collectors.toList());
+                    QuestionnaireResponseItemAnswerComponent answerComponent = allQuestions.get(0).getAnswerFirstRep();
                     // Pull the string response the person gave.
                     String response = answerComponent.getValueCoding().getCode();
                     // Pull the resulting next question that the recieved response points to from the tree.
-                    QuestionnaireItemComponent result = questionnaireTree.getNextQuestionForResponse(response);
+                    QuestionnaireItemComponent nextQuestionResult = questionnaireTree.getNextQuestionForResponse(response);
                     // Add the next question to the QuestionnaireResponse.contained[0].item[].
                     Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
-                    containedQuestionnaire.addItem(result);
-                    logger.info("--- Added next question for questionnaire " + inputQuestionnaireFromRequest.getId() + " for response " + response);
+                    containedQuestionnaire.addItem(nextQuestionResult);
+                    logger.info("--- Added next question for questionnaire \'" + inputQuestionnaireFromRequest.getId() + "\' for response \'" + response + "\''.");
                     // If this question is a leaf node and is the final question, set status to "completed"
-                    if(this.questionnaireTree.isLeafNode(response)){
+                    if(this.questionnaireTree.reachedLeafNode()){
                         inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
                         logger.info("--- Question leaf node reached, setting status to \"completed\".");
                     }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json b/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json
index 6c206db7c..a954b82b5 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json
@@ -63,27 +63,115 @@
                         "code": "Replacement"
                     }
                 }
+            ],
+            "item": [
+                {
+                    "linkId": "1.1",
+                    "type": "display",
+                    "text": "Submit the order form"
+                },
+
+                {
+                    "linkId": "1.2",
+                    "type": "display",
+                    "text": "Status Reasoning, Patient relocated, etc."
+                },
+                {
+                    "linkId": "1.3",
+                    "type": "display",
+                    "text": "Revision/Equipment change Reasoning, etc."
+                },
+                {
+                    "linkId": "1.4",
+                    "text": "Replacement Reason",
+                    "type": "choice",
+                    "required": true,
+                    "answerOption": [
+                        {
+                            "valueCoding": {
+                                "code": "Lost or stolen"
+                            },
+                            "modifierExtension": [
+                                {
+                                    "url": "1.4.1"
+                                }
+                            ]
+                        },
+                        {
+                            "valueCoding": {
+                                "code": "End of lifetime"
+                            },
+                            "modifierExtension": [
+                                {
+                                    "url": "1.4.2"
+                                }
+                            ]
+                        },
+                        {
+                            "valueCoding": {
+                                "code": "Repair exceeds 60% of cost"
+                            },
+                            "modifierExtension": [
+                                {
+                                    "url": "1.4.3"
+                                }
+                            ]
+                        }
+                    ],
+                    "item": [
+                        {
+                            "linkId": "1.4.1",
+                            "type": "display",
+                            "text": "Your prior auth number is A1234 (lost/stolen response)"
+                        },
+                        {
+                            "linkId": "1.4.3",
+                            "text": "Your prior auth number is B1234 (repair cost response). How much will it cost?",
+                            "type": "choice",
+                            "required": true,
+                            "answerOption": [
+                                {
+                                    "valueCoding": {
+                                        "code": "Greater than $500"
+                                    },
+                                    "modifierExtension": [
+                                        {
+                                            "url": "1.4.3.1"
+                                        }
+                                    ]
+                                },
+                                {
+                                    "valueCoding": {
+                                        "code": "Less than $500"
+                                    },
+                                    "modifierExtension": [
+                                        {
+                                            "url": "1.4.3.2"
+                                        }
+                                    ]
+                                }
+                            ],
+                            "item": [
+                                {
+                                    "linkId": "1.4.3.1",
+                                    "type": "display",
+                                    "text": "Your response idicated that it will cost more than $500."
+                                },
+                                {
+                                    "linkId": "1.4.3.2",
+                                    "type": "display",
+                                    "text": "Your response indicated that it will cost less than $500."
+                                }
+                            ]
+                        },
+                        {
+                            "linkId": "1.4.2",
+                            "type": "display",
+                            "text": "Your prior auth number is B1234 (EOL response)"
+                        }
+                    ]
+                }
             ]
-        },
-        {
-            "linkId": "1.1",
-            "type": "display",
-            "text": "Submit the order form"
-        },
-        {
-            "linkId": "1.4",
-            "type": "display",
-            "text": "Replacement Reasoning, etc."
-        },
-        {
-            "linkId": "1.2",
-            "type": "display",
-            "text": "Status Reasoning, Patient relocated, etc."
-        },
-        {
-            "linkId": "1.3",
-            "type": "display",
-            "text": "Revision/Equipment change Reasoning, etc."
         }
     ]
 }
\ No newline at end of file

From c17006820a5cce757066cef6bd6286bc24832c53 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Mon, 25 Oct 2021 10:26:47 -0400
Subject: [PATCH 07/30] Changed the questionnaire tree to use a static map to
 track multuple questionnaire options.

---
 .../controllers/QuestionnaireController.java  | 25 +++++++++++--------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index f9795e339..308beb0c7 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -50,7 +50,7 @@
 public class QuestionnaireController {
 
     /**
-     * A class that demos a tree to define next questions based on responses.
+     * An inner class that demos a tree to define next questions based on responses.
      */
     private class AdaptiveQuestionnaireTree {
         
@@ -113,6 +113,7 @@ public String getCurrentQuestionId() {
          * Inner class that describes a node of the tree.
          */
         private class AdaptiveQuestionnaireNode {
+
             // Contains the current question of the node.
             private QuestionnaireItemComponent questionItem;
             // Map of (answerResponse->childQuestionItemNode) (The child could have answer options within it or be a leaf node. It does have a question item component though).
@@ -185,8 +186,8 @@ public String getQuestionId() {
 
     // Logger.
     private static Logger logger = Logger.getLogger(Application.class.getName());
-    // Tree that tracks the questions.
-    private AdaptiveQuestionnaireTree questionnaireTree;
+    // Trees that track the current and next questions. Is key-value mappng of: Map<Questionnaire ID -> AdaptiveQuestionnaireTree>
+    private static final Map<String, AdaptiveQuestionnaireTree> questionnaireTrees = new HashMap<String, AdaptiveQuestionnaireTree>();
 
     /**
      * 
@@ -237,7 +238,7 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
 
             if (inputQuestionnaireFromRequest != null) {
 
-                if(questionnaireTree == null){
+                if(!questionnaireTrees.containsKey(inputQuestionnaireFromRequest.getId())){
 
                     // Import the requested CDS-Library Questionnaire (Couldn't get CDS to work with it, just reading it in it locally for now. In future will need to be pulled from CDS.)
                     Questionnaire cdsQuestionnaire = null;
@@ -259,13 +260,15 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     inputQuestionnaireFromRequest.addItem(topQuestionItem);
 
                     // Build the tree and don't expect any answers since we only just received the required questions.
-                    questionnaireTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
-                    System.out.println(inputQuestionnaireFromRequest.getItem());
-                    logger.info("--- Built Questionnaire Tree for " + inputQuestionnaireFromRequest.getId());
+                    AdaptiveQuestionnaireTree newTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
+                    questionnaireTrees.put(inputQuestionnaireFromRequest.getId(), newTree);
 
+                    logger.info("--- Built Questionnaire Tree for " + inputQuestionnaireFromRequest.getId());
                 } else {
+                    // Pull in the current tree for the requested questionnaire id.
+                    AdaptiveQuestionnaireTree currentTree = questionnaireTrees.get(inputQuestionnaireFromRequest.getId());
                     // Previous question Id
-                    String previousQuestionId = this.questionnaireTree.getCurrentQuestionId();
+                    String previousQuestionId = currentTree.getCurrentQuestionId();
                     // Get the answer component of the item with the previous question id from the recieved resource.
                     List<QuestionnaireResponseItemComponent> allQuestions = inputQuestionnaireResponse.getItem();
                     allQuestions = allQuestions.stream().filter((QuestionnaireResponseItemComponent item) -> item.getLinkId().equals(previousQuestionId)).collect(Collectors.toList());
@@ -273,15 +276,15 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     // Pull the string response the person gave.
                     String response = answerComponent.getValueCoding().getCode();
                     // Pull the resulting next question that the recieved response points to from the tree.
-                    QuestionnaireItemComponent nextQuestionResult = questionnaireTree.getNextQuestionForResponse(response);
+                    QuestionnaireItemComponent nextQuestionResult = currentTree.getNextQuestionForResponse(response);
                     // Add the next question to the QuestionnaireResponse.contained[0].item[].
                     Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
                     containedQuestionnaire.addItem(nextQuestionResult);
                     logger.info("--- Added next question for questionnaire \'" + inputQuestionnaireFromRequest.getId() + "\' for response \'" + response + "\''.");
                     // If this question is a leaf node and is the final question, set status to "completed"
-                    if(this.questionnaireTree.reachedLeafNode()){
+                    if (currentTree.reachedLeafNode()) {
                         inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
-                        logger.info("--- Question leaf node reached, setting status to \"completed\".");
+                        logger.info("--- Questionnaire leaf node reached, setting status to \"completed\".");
                     }
                 }
 

From 763ec63d357ee79c6204061f77748214060e1176 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Mon, 25 Oct 2021 11:09:35 -0400
Subject: [PATCH 08/30] Removed children display in response JSON for question
 items

---
 .../controllers/QuestionnaireController.java  | 39 ++++++++++++++-----
 1 file changed, 30 insertions(+), 9 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index 308beb0c7..b55888d58 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -114,7 +114,7 @@ public String getCurrentQuestionId() {
          */
         private class AdaptiveQuestionnaireNode {
 
-            // Contains the current question of the node.
+            // Contains the current question item of the node.
             private QuestionnaireItemComponent questionItem;
             // Map of (answerResponse->childQuestionItemNode) (The child could have answer options within it or be a leaf node. It does have a question item component though).
             private Map<String, AdaptiveQuestionnaireNode> children;
@@ -152,7 +152,7 @@ public AdaptiveQuestionnaireNode(QuestionnaireItemComponent questionItem) {
                     String subQuestionResponse = childIdsToResponses.get(subQuestionLinkId);
                     // Create a new node for this subQuestion.
                     AdaptiveQuestionnaireNode subQuestionNode = new AdaptiveQuestionnaireNode(subQuestionItem);
-                    this.children.put(subQuestionResponse, subQuestionNode); // Should not be ID, should be response.
+                    this.children.put(subQuestionResponse, subQuestionNode);
                 }
             }
 
@@ -238,7 +238,7 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
 
             if (inputQuestionnaireFromRequest != null) {
 
-                if(!questionnaireTrees.containsKey(inputQuestionnaireFromRequest.getId())){
+                if(!questionnaireTrees.containsKey(inputQuestionnaireFromRequest.getId() /** || item.isempty() */)){
 
                     // Import the requested CDS-Library Questionnaire (Couldn't get CDS to work with it, just reading it in it locally for now. In future will need to be pulled from CDS.)
                     Questionnaire cdsQuestionnaire = null;
@@ -253,9 +253,9 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     if(cdsQuestionnaire == null) {
                         throw new RuntimeException("Requested CDS Questionnaire XXX was not imported and may not exist.");
                     }
-                    // Pull the first question from the CDS Questionnaire because it should be the top-level question.
+                    // Pull the first question from the CDS Questionnaire because it should be the top-level question (and the only item in the list).
                     QuestionnaireItemComponent topQuestionItem = cdsQuestionnaire.getItemFirstRep();
-
+                    topQuestionItem = removeChildrenFromQuestionItem(topQuestionItem);
                     // Add the first Question item to the contained Questionnaire in the response/request QuestionnaireResponse JSON as part of the response.
                     inputQuestionnaireFromRequest.addItem(topQuestionItem);
 
@@ -275,11 +275,13 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     QuestionnaireResponseItemAnswerComponent answerComponent = allQuestions.get(0).getAnswerFirstRep();
                     // Pull the string response the person gave.
                     String response = answerComponent.getValueCoding().getCode();
-                    // Pull the resulting next question that the recieved response points to from the tree.
-                    QuestionnaireItemComponent nextQuestionResult = currentTree.getNextQuestionForResponse(response);
+                    // Pull the resulting next question that the recieved response points to from the tree without including its children.
+                    QuestionnaireItemComponent nextQuestionItemResult = currentTree.getNextQuestionForResponse(response);
+                    nextQuestionItemResult = removeChildrenFromQuestionItem(nextQuestionItemResult);
+
                     // Add the next question to the QuestionnaireResponse.contained[0].item[].
                     Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
-                    containedQuestionnaire.addItem(nextQuestionResult);
+                    containedQuestionnaire.addItem(nextQuestionItemResult);
                     logger.info("--- Added next question for questionnaire \'" + inputQuestionnaireFromRequest.getId() + "\' for response \'" + response + "\''.");
                     // If this question is a leaf node and is the final question, set status to "completed"
                     if (currentTree.reachedLeafNode()) {
@@ -299,9 +301,28 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
             } else {
                 return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON)
                         .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
-                        .body("Invalid input questionnaire");
+                        .body("Invalid input questionnaire does not exist");
             }
 
         }
     }
+
+    /**
+     * Returns a new question item that is indentical to the input qusetion item except without the children.
+     * @param inputQuestionItem
+     * @return
+     */
+    private static QuestionnaireItemComponent removeChildrenFromQuestionItem(QuestionnaireItemComponent inputQuestionItem){
+        QuestionnaireItemComponent questionItemNoChildren = new QuestionnaireItemComponent();
+        questionItemNoChildren.setLinkId(inputQuestionItem.getLinkId());
+        questionItemNoChildren.setText(inputQuestionItem.getText());
+        questionItemNoChildren.setType(inputQuestionItem.getType());
+        questionItemNoChildren.setRequired(inputQuestionItem.getRequired());
+        questionItemNoChildren.setAnswerOption(inputQuestionItem.getAnswerOption());
+
+        // Can't just remove the children because then that would alter the original object in the tree.
+        // questionItemNoChildren.setItem(null);
+
+        return questionItemNoChildren;
+    }
 }
\ No newline at end of file

From c033a5e8bbcda4ef91064242a019d1a3d82208e5 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Mon, 25 Oct 2021 11:20:12 -0400
Subject: [PATCH 09/30] Made it so that sending a response with no answer items
 will reset that questionnaire's next-question path to the initial form.

---
 .../controllers/QuestionnaireController.java  | 77 ++++++++++++++++---
 1 file changed, 66 insertions(+), 11 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index b55888d58..ead2570f1 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -236,9 +236,16 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 }
             }
 
+            String questionnaireId = inputQuestionnaireFromRequest.getId();
+
             if (inputQuestionnaireFromRequest != null) {
 
-                if(!questionnaireTrees.containsKey(inputQuestionnaireFromRequest.getId() /** || item.isempty() */)){
+                // If there are no item answer responses in the sent JSON, reset the tree so that we can restart the question process.
+                if(inputQuestionnaireFromRequest.getItem().size() < 1) {
+                    questionnaireTrees.remove(questionnaireId);
+                }
+
+                if(!questionnaireTrees.containsKey(questionnaireId)){
 
                     // Import the requested CDS-Library Questionnaire (Couldn't get CDS to work with it, just reading it in it locally for now. In future will need to be pulled from CDS.)
                     Questionnaire cdsQuestionnaire = null;
@@ -251,7 +258,7 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                         e.printStackTrace();
                     }
                     if(cdsQuestionnaire == null) {
-                        throw new RuntimeException("Requested CDS Questionnaire XXX was not imported and may not exist.");
+                        throw new RuntimeException("Requested CDS Questionnaire \'" + questionnaireId + "\' was not imported and may not exist.");
                     }
                     // Pull the first question from the CDS Questionnaire because it should be the top-level question (and the only item in the list).
                     QuestionnaireItemComponent topQuestionItem = cdsQuestionnaire.getItemFirstRep();
@@ -261,12 +268,12 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
 
                     // Build the tree and don't expect any answers since we only just received the required questions.
                     AdaptiveQuestionnaireTree newTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
-                    questionnaireTrees.put(inputQuestionnaireFromRequest.getId(), newTree);
+                    questionnaireTrees.put(questionnaireId, newTree);
 
-                    logger.info("--- Built Questionnaire Tree for " + inputQuestionnaireFromRequest.getId());
+                    logger.info("--- Built Questionnaire Tree for " + questionnaireId);
                 } else {
                     // Pull in the current tree for the requested questionnaire id.
-                    AdaptiveQuestionnaireTree currentTree = questionnaireTrees.get(inputQuestionnaireFromRequest.getId());
+                    AdaptiveQuestionnaireTree currentTree = questionnaireTrees.get(questionnaireId);
                     // Previous question Id
                     String previousQuestionId = currentTree.getCurrentQuestionId();
                     // Get the answer component of the item with the previous question id from the recieved resource.
@@ -282,15 +289,15 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     // Add the next question to the QuestionnaireResponse.contained[0].item[].
                     Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
                     containedQuestionnaire.addItem(nextQuestionItemResult);
-                    logger.info("--- Added next question for questionnaire \'" + inputQuestionnaireFromRequest.getId() + "\' for response \'" + response + "\''.");
-                    // If this question is a leaf node and is the final question, set status to "completed"
+                    logger.info("--- Added next question for questionnaire \'" + questionnaireId + "\' for response \'" + response + "\'.");
+                    // If this question is a leaf node and is the final question, set the status to "completed"
                     if (currentTree.reachedLeafNode()) {
                         inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
                         logger.info("--- Questionnaire leaf node reached, setting status to \"completed\".");
                     }
                 }
 
-                logger.info("--- Get next question for questionnaire " + inputQuestionnaireFromRequest.getId());
+                logger.info("--- Get next question for questionnaire " + questionnaireId);
                 logger.info("---- Get meta profile " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
                 
                 // Build and send the response.
@@ -319,10 +326,58 @@ private static QuestionnaireItemComponent removeChildrenFromQuestionItem(Questio
         questionItemNoChildren.setType(inputQuestionItem.getType());
         questionItemNoChildren.setRequired(inputQuestionItem.getRequired());
         questionItemNoChildren.setAnswerOption(inputQuestionItem.getAnswerOption());
-
         // Can't just remove the children because then that would alter the original object in the tree.
         // questionItemNoChildren.setItem(null);
-
         return questionItemNoChildren;
     }
-}
\ No newline at end of file
+}
+
+
+// --- QuestionniareResponse inital request format:
+// {
+//     "resourceType": "QuestionnaireResponse",
+//     "meta": {
+//         "profile": [
+//             "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaireresponse-adapt"
+//         ]
+//     },
+//     "contained": [
+//         {
+//             "resourceType": "Questionnaire",
+//             "id": "HomeOxygenTherapyAdditional",
+//             "meta": {
+//                 "profile": [
+//                     "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-adapt"
+//                 ]
+//             },
+//             "url": "http://example.com/questionnaire/adaptive-form#HomeOxygenTherapyAdditional",
+//             "item": []
+//         }
+//     ],
+//     "extension": [
+//         {
+//             "url": "http://hl7.org/fhir/StructureDefinition/contained-id",
+//             "valueReference": {
+//                 "reference": "#HomeOxygenTherapyAdditional"
+//             }
+//         }
+//     ],
+//     "questionnaire": "#HomeOxygenTherapyAdditional",
+//     "status": "in-progress",
+//     "item": []
+// }
+
+// --- Answer item format:
+// "item": [
+//     {
+//         "linkId": "1",
+//         "text": "Order Reason",
+//         "answer": [
+//           {
+//             "valueCoding": {
+//               "code": "Replacement"
+//             }
+//           }
+//         ]
+//     }
+// ]
\ No newline at end of file

From 3f169c554a374e9ab6b1c8b0b8ff4e42eef8560f Mon Sep 17 00:00:00 2001
From: Patrick LaRocque <plarocque@mitre.org>
Date: Mon, 25 Oct 2021 14:19:24 -0400
Subject: [PATCH 10/30] Retrieve the Questions file from the FileStore.

---
 .../controllers/QuestionnaireController.java      | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index ead2570f1..7e7bd00e8 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -1,7 +1,10 @@
 package org.hl7.davinci.endpoint.controllers;
 
 import org.hl7.davinci.endpoint.Application;
+import org.hl7.davinci.endpoint.files.FileResource;
+import org.hl7.davinci.endpoint.files.FileStore;
 import org.hl7.fhir.instance.model.api.IDomainResource;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpEntity;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
@@ -18,6 +21,7 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -49,6 +53,10 @@
 @RequestMapping("/Questionnaire")
 public class QuestionnaireController {
 
+
+    @Autowired
+    private FileStore fileStore;
+
     /**
      * An inner class that demos a tree to define next questions based on responses.
      */
@@ -250,12 +258,17 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     // Import the requested CDS-Library Questionnaire (Couldn't get CDS to work with it, just reading it in it locally for now. In future will need to be pulled from CDS.)
                     Questionnaire cdsQuestionnaire = null;
                     try {
-                        cdsQuestionnaire = (Questionnaire) parser.parseResource(Questionnaire.class, new FileReader(new File("/Users/rscalfani/Documents/code/drlsroot/CRD/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json")));
+                        //TODO: need to determine topic, filename, and fhir version without having them hard coded
+                        // File is pulled from the file store
+                        FileResource fileResource = fileStore.getFile("HomeOxygenTherapy", "Questions-HomeOxygenTherapyAdditional.json", "R4", false);
+                        cdsQuestionnaire = (Questionnaire) parser.parseResource(fileResource.getResource().getInputStream());
                         logger.info("--- Imported Questionnaire " + cdsQuestionnaire.getId());
                     } catch (DataFormatException e) {
                         e.printStackTrace();
                     } catch (FileNotFoundException e) {
                         e.printStackTrace();
+                    } catch (IOException e) {
+                        e.printStackTrace();
                     }
                     if(cdsQuestionnaire == null) {
                         throw new RuntimeException("Requested CDS Questionnaire \'" + questionnaireId + "\' was not imported and may not exist.");

From 85a93c06e2cbab69fed603c6bb14f50f6be91e4a Mon Sep 17 00:00:00 2001
From: Patrick LaRocque <plarocque@mitre.org>
Date: Mon, 25 Oct 2021 16:41:39 -0400
Subject: [PATCH 11/30] Fix Java 8 build error.

---
 .../davinci/endpoint/cdshooks/services/crd/CdsService.java | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
index 327fc1399..3c4051ba4 100755
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
@@ -1,5 +1,6 @@
 package org.hl7.davinci.endpoint.cdshooks.services.crd;
 
+import java.io.UnsupportedEncodingException;
 import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URL;
@@ -351,7 +352,11 @@ private Link smartLinkBuilder(String patientId, String fhirBase, URL application
     appContext = appContext + "&filepath=" + applicationBaseUrl + "/";
     if (myConfig.getUrlEncodeAppContext()) {
       logger.info("CdsService::smartLinkBuilder: URL encoding appcontext");
-      appContext = URLEncoder.encode(appContext, StandardCharsets.UTF_8).toString();
+      try {
+        appContext = URLEncoder.encode(appContext, StandardCharsets.UTF_8.name()).toString();
+      } catch (UnsupportedEncodingException e) {
+        logger.error("CdsService::smartLinkBuilder: failed to encode URL: " + e.getMessage());
+      }
     }
 
     logger.info("smarLinkBuilder: appContext: " + appContext);

From c2049d1897007033ff241235eb2e6b40ad201999 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Thu, 28 Oct 2021 16:49:25 -0400
Subject: [PATCH 12/30] Cleanup.

---
 .../controllers/QuestionnaireController.java  | 82 ++++---------------
 1 file changed, 15 insertions(+), 67 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index 7e7bd00e8..7fd1f0a63 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -18,9 +18,7 @@
 import ca.uhn.fhir.parser.DataFormatException;
 import ca.uhn.fhir.parser.IParser;
 
-import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
@@ -127,13 +125,17 @@ private class AdaptiveQuestionnaireNode {
             // Map of (answerResponse->childQuestionItemNode) (The child could have answer options within it or be a leaf node. It does have a question item component though).
             private Map<String, AdaptiveQuestionnaireNode> children;
 
+            /**
+             * Constructor
+             * @param questionItem
+             */
             public AdaptiveQuestionnaireNode(QuestionnaireItemComponent questionItem) {
                 this.questionItem = questionItem;
 
                 // The number of answer options should always equal the number of subquestion items.
                 if((this.questionItem.getAnswerOption().size() != this.questionItem.getItem().size())){
-                    throw new RuntimeException("There should be the same number of answer options as sub-items. Answer options: " + this.questionItem.getAnswerOption().size() + ", Sub-tems: " + this.questionItem.getItem().size());
-                }  
+                    throw new RuntimeException("There should be the same number of answer options as sub-items. Answer options: " + this.questionItem.getAnswerOption().size() + ", sub-items: " + this.questionItem.getItem().size());
+                }
 
                 Map<String, String> childIdsToResponses = new HashMap<String, String>();
                 // This loop iterates over the possible answer options of this questionitem and links the linkId to its possible responses.
@@ -170,7 +172,6 @@ public AdaptiveQuestionnaireNode(QuestionnaireItemComponent questionItem) {
              */
             private boolean isLeafNode() {
                 return this.children.size() < 1;
-                // return !this.questionItem.hasAnswerOption();
             }
 
             /**
@@ -209,7 +210,7 @@ public ResponseEntity<String> retrieveNextQuestion(HttpServletRequest request, H
     }
 
     /**
-     * 
+     *
      * @param body
      * @param request
      * @return
@@ -254,8 +255,8 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 }
 
                 if(!questionnaireTrees.containsKey(questionnaireId)){
-
-                    // Import the requested CDS-Library Questionnaire (Couldn't get CDS to work with it, just reading it in it locally for now. In future will need to be pulled from CDS.)
+                    // If there is not already a tree that matches the requested questionnaire id, build it.
+                    // Import the requested CDS-Library Questionnaire.
                     Questionnaire cdsQuestionnaire = null;
                     try {
                         //TODO: need to determine topic, filename, and fhir version without having them hard coded
@@ -282,14 +283,14 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     // Build the tree and don't expect any answers since we only just received the required questions.
                     AdaptiveQuestionnaireTree newTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
                     questionnaireTrees.put(questionnaireId, newTree);
-
                     logger.info("--- Built Questionnaire Tree for " + questionnaireId);
                 } else {
-                    // Pull in the current tree for the requested questionnaire id.
+                    // If there is already a tree with the requested questionnaire id, execute next-question on it with the new request.
+                    // Pull the current tree for the requested questionnaire id.
                     AdaptiveQuestionnaireTree currentTree = questionnaireTrees.get(questionnaireId);
-                    // Previous question Id
+                    // Get the previous question Id.
                     String previousQuestionId = currentTree.getCurrentQuestionId();
-                    // Get the answer component of the item with the previous question id from the recieved resource.
+                    // Get the request's answer component of the item with the previous question id.
                     List<QuestionnaireResponseItemComponent> allQuestions = inputQuestionnaireResponse.getItem();
                     allQuestions = allQuestions.stream().filter((QuestionnaireResponseItemComponent item) -> item.getLinkId().equals(previousQuestionId)).collect(Collectors.toList());
                     QuestionnaireResponseItemAnswerComponent answerComponent = allQuestions.get(0).getAnswerFirstRep();
@@ -298,11 +299,11 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     // Pull the resulting next question that the recieved response points to from the tree without including its children.
                     QuestionnaireItemComponent nextQuestionItemResult = currentTree.getNextQuestionForResponse(response);
                     nextQuestionItemResult = removeChildrenFromQuestionItem(nextQuestionItemResult);
-
                     // Add the next question to the QuestionnaireResponse.contained[0].item[].
                     Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
                     containedQuestionnaire.addItem(nextQuestionItemResult);
                     logger.info("--- Added next question for questionnaire \'" + questionnaireId + "\' for response \'" + response + "\'.");
+
                     // If this question is a leaf node and is the final question, set the status to "completed"
                     if (currentTree.reachedLeafNode()) {
                         inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
@@ -310,7 +311,6 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     }
                 }
 
-                logger.info("--- Get next question for questionnaire " + questionnaireId);
                 logger.info("---- Get meta profile " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
                 
                 // Build and send the response.
@@ -339,58 +339,6 @@ private static QuestionnaireItemComponent removeChildrenFromQuestionItem(Questio
         questionItemNoChildren.setType(inputQuestionItem.getType());
         questionItemNoChildren.setRequired(inputQuestionItem.getRequired());
         questionItemNoChildren.setAnswerOption(inputQuestionItem.getAnswerOption());
-        // Can't just remove the children because then that would alter the original object in the tree.
-        // questionItemNoChildren.setItem(null);
         return questionItemNoChildren;
     }
-}
-
-
-// --- QuestionniareResponse inital request format:
-// {
-//     "resourceType": "QuestionnaireResponse",
-//     "meta": {
-//         "profile": [
-//             "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaireresponse-adapt"
-//         ]
-//     },
-//     "contained": [
-//         {
-//             "resourceType": "Questionnaire",
-//             "id": "HomeOxygenTherapyAdditional",
-//             "meta": {
-//                 "profile": [
-//                     "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-adapt"
-//                 ]
-//             },
-//             "url": "http://example.com/questionnaire/adaptive-form#HomeOxygenTherapyAdditional",
-//             "item": []
-//         }
-//     ],
-//     "extension": [
-//         {
-//             "url": "http://hl7.org/fhir/StructureDefinition/contained-id",
-//             "valueReference": {
-//                 "reference": "#HomeOxygenTherapyAdditional"
-//             }
-//         }
-//     ],
-//     "questionnaire": "#HomeOxygenTherapyAdditional",
-//     "status": "in-progress",
-//     "item": []
-// }
-
-// --- Answer item format:
-// "item": [
-//     {
-//         "linkId": "1",
-//         "text": "Order Reason",
-//         "answer": [
-//           {
-//             "valueCoding": {
-//               "code": "Replacement"
-//             }
-//           }
-//         ]
-//     }
-// ]
\ No newline at end of file
+}
\ No newline at end of file

From bade0a0dbc2f03d3cbf85d02407a09684a3e6c52 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Mon, 1 Nov 2021 17:24:29 -0400
Subject: [PATCH 13/30] Added funcionality for an adaptive questionnaire to
 return multiple questions for a request.

---
 .../controllers/QuestionnaireController.java  | 228 +++++++++++-------
 1 file changed, 146 insertions(+), 82 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index 7fd1f0a63..c1ad0a5c2 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -20,6 +20,7 @@
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -51,7 +52,6 @@
 @RequestMapping("/Questionnaire")
 public class QuestionnaireController {
 
-
     @Autowired
     private FileStore fileStore;
 
@@ -69,16 +69,11 @@ private class AdaptiveQuestionnaireTree {
          */
         public AdaptiveQuestionnaireTree(Questionnaire inputQuestionnaire) {
 
-            // Because of the nested structure of the input JSON, there can be only one super-parent questionitem in the inputQuestionnaire.
-            if(inputQuestionnaire.getItem().size() != 1){
-                throw new RuntimeException("An input Adaptive next-question questionnaire can have only one super-parent question.item, found " + inputQuestionnaire.getItem().size() + ".");
-            }
-
-            // Top level parent question item. This is also the first question.
-            QuestionnaireItemComponent topQuestion = inputQuestionnaire.getItemFirstRep();
+            // Top level parent question item, the first question page.
+            QuestionnaireItemComponent topLevelQuestion = inputQuestionnaire.getItemFirstRep();
 
             // Start the root building.
-            this.root = new AdaptiveQuestionnaireNode(topQuestion);
+            this.root = new AdaptiveQuestionnaireNode(topLevelQuestion);
         }
 
         /**
@@ -86,16 +81,20 @@ public AdaptiveQuestionnaireTree(Questionnaire inputQuestionnaire) {
          * @param response  The response given to this question.
          * @return
          */
-        public QuestionnaireItemComponent getNextQuestionForResponse(String response){
-            if(!root.children.containsKey(response)){
-                throw new RuntimeException("Not a valid response for question: \'" + this.root.questionItem.getText() + "\' with response \'" + response + "\'. Possible responses for this question: \'" + root.children.keySet() + "\'.");
+        public List<QuestionnaireItemComponent> getNextQuestionsFromResponse(String response){
+
+            if(!root.hasResponse(response)){
+                throw new RuntimeException("Not a valid response for question: \'" + this.root.determinantQuestionItem.getText() + "\' with response \'" + response + "\'. Possible responses for this question: \'" + root.children.keySet() + "\'.");
             }
-            // Pull the current question.
-            QuestionnaireItemComponent currentQuestionnaireItem = this.root.getChildForResponse(response).questionItem;
+            AdaptiveQuestionnaireNode nextQuestionSetNode = this.root.getChildForResponse(response);
+            // Pull the current question set.
+            List<QuestionnaireItemComponent> currentQuestionnaireItems = nextQuestionSetNode.getQuestionSet();
             // Set the new next question.
-            this.root = this.root.getChildForResponse(response);
+            this.root = nextQuestionSetNode;
             // Return the prior current question.
-            return currentQuestionnaireItem;
+
+            currentQuestionnaireItems.get(0).getModifierExtensionFirstRep().getValue();
+            return currentQuestionnaireItems;
         }
 
         /**
@@ -111,8 +110,8 @@ public boolean reachedLeafNode() {
          * Returns the linkid of the current question.
          * @return
          */
-        public String getCurrentQuestionId() {
-            return this.root.getQuestionId();
+        public String getCurrentDeterminantQuestionId() {
+            return this.root.getDeterminantQuestionId();
         }
     
         /**
@@ -120,49 +119,124 @@ public String getCurrentQuestionId() {
          */
         private class AdaptiveQuestionnaireNode {
 
-            // Contains the current question item of the node.
-            private QuestionnaireItemComponent questionItem;
+            // Contains the list of additional questions that should be displayed with this question.
+            private List<QuestionnaireItemComponent> supplementalQuestions;
+            // Contains the current question item that dictates the next question of the node.
+            private QuestionnaireItemComponent determinantQuestionItem;
             // Map of (answerResponse->childQuestionItemNode) (The child could have answer options within it or be a leaf node. It does have a question item component though).
             private Map<String, AdaptiveQuestionnaireNode> children;
 
             /**
              * Constructor
-             * @param questionItem
+             * @param determinantQuestionItem
              */
-            public AdaptiveQuestionnaireNode(QuestionnaireItemComponent questionItem) {
-                this.questionItem = questionItem;
-
-                // The number of answer options should always equal the number of subquestion items.
-                if((this.questionItem.getAnswerOption().size() != this.questionItem.getItem().size())){
-                    throw new RuntimeException("There should be the same number of answer options as sub-items. Answer options: " + this.questionItem.getAnswerOption().size() + ", sub-items: " + this.questionItem.getItem().size());
+            public AdaptiveQuestionnaireNode(QuestionnaireItemComponent determinantQuestion) {
+
+                this.determinantQuestionItem = determinantQuestion;
+                // Get the child and supplemental question items of this question.
+                List<QuestionnaireItemComponent> subQuestions = determinantQuestion.getItem();
+                // Extract the supplemental questions which do not have a child link-id branch from the determinant questions.
+                List<String> nonSupplementLinkIds = determinantQuestionItem.getAnswerOption().stream().map(answerOption -> answerOption.getModifierExtensionFirstRep().getUrl()).collect(Collectors.toList());
+                List<QuestionnaireItemComponent> childQuestions = this.extractChildQuestions(subQuestions, nonSupplementLinkIds);
+                // Extract the remaining questions as supplemental questions.
+                this.supplementalQuestions = this.extractSupplementalQuestions(subQuestions, nonSupplementLinkIds);
+
+                // The number of answer options of the determinant question should always equal the number of child question items.
+                if((this.determinantQuestionItem.getAnswerOption().size() != childQuestions.size())){
+                    throw new RuntimeException("There should be the same number of answer options as sub-items. Answer options: " + this.determinantQuestionItem.getAnswerOption().size() + ", sub-items: " + childQuestions.size());
                 }
 
-                Map<String, String> childIdsToResponses = new HashMap<String, String>();
-                // This loop iterates over the possible answer options of this questionitem and links the linkId to its possible responses.
-                for(QuestionnaireItemAnswerOptionComponent answerOption : questionItem.getAnswerOption()) {
-                    // The Id of this answer response's next question.
-                    String answerNextQuestionId = answerOption.getModifierExtensionFirstRep().getUrl();
-                    // The response that indicates this answer to the question.
-                    String possibleAnswerResponse = answerOption.getValueCoding().getCode();
-                    // Check for issues.
-                    if(answerNextQuestionId == null || possibleAnswerResponse == null){
-                        throw new RuntimeException("Malformed Adaptive Questionnaire. Missing a questionID or answer response.");
+                // If the determinant question item does not have any answer options, then this is a leaf node and should not generate any children.
+                if(determinantQuestionItem.hasAnswerOption()) {
+                    Map<String, String> childIdsToResponses = new HashMap<String, String>();
+                    // This loop iterates over the possible answer options of this questionitem and links the linkId to its possible responses.
+                    for(QuestionnaireItemAnswerOptionComponent answerOption : determinantQuestionItem.getAnswerOption()) {
+                        // The Id of this answer response's next question.
+                        String answerNextQuestionId = answerOption.getModifierExtensionFirstRep().getUrl();
+                        // The response that indicates this answer to the question.
+                        String possibleAnswerResponse = answerOption.getValueCoding().getCode();
+                        // Check for issues.
+                        if(answerNextQuestionId == null || possibleAnswerResponse == null){
+                            throw new RuntimeException("Malformed Adaptive Questionnaire. Missing a questionID or answer response.");
+                        }
+                        // Add the key-value pair of next question id to its assocated answer response.
+                        childIdsToResponses.put(answerNextQuestionId, possibleAnswerResponse);
+                    }
+
+                    // Create the map of answerResponses->subQuestionItems
+                    this.children = new HashMap<String, AdaptiveQuestionnaireNode>();
+                    List<QuestionnaireItemComponent> subQuestionItems = determinantQuestionItem.getItem();
+                    for(QuestionnaireItemComponent subQuestionItem : subQuestionItems){
+                        // SubQuestion linkId.
+                        String subQuestionLinkId = subQuestionItem.getLinkId();
+                        // SubQuestion's associated response.
+                        String subQuestionResponse = childIdsToResponses.get(subQuestionLinkId);
+                        // Create a new node for this subQuestion.
+                        AdaptiveQuestionnaireNode subQuestionNode = new AdaptiveQuestionnaireNode(subQuestionItem);
+                        this.children.put(subQuestionResponse, subQuestionNode);
                     }
-                    // Add the key-value pair of next question id to its assocated answer response.
-                    childIdsToResponses.put(answerNextQuestionId, possibleAnswerResponse);
                 }
+            }
+
+            /**
+             * Returns the question items in the given list that do not have the linkids of the given list of strings.
+             * @param questionItems
+             * @param nonSupplementQuestions
+             * @return
+             */
+            private List<QuestionnaireItemComponent> extractSupplementalQuestions(
+                    List<QuestionnaireItemComponent> questionItems, List<String> nonSupplementLinkIds) {
+                return questionItems.stream().filter(questionItem -> !nonSupplementLinkIds.contains(questionItem.getLinkId())).collect(Collectors.toList());
+            }
+
+            /**
+             * Returns the question items in the given list that do have the linkids of the given list of strings.
+             * @param questionItems
+             * @param nonSupplementQuestions
+             * @return
+             */
+            private List<QuestionnaireItemComponent> extractChildQuestions(
+                    List<QuestionnaireItemComponent> questionItems, List<String> nonSupplementLinkIds) {
+                return questionItems.stream().filter(questionItem -> nonSupplementLinkIds.contains(questionItem.getLinkId())).collect(Collectors.toList());
+            }
+
+            /**
+             * Returns the set of questions associated with the node. Incldues all questions in the set, determinant and non-determinant.
+             * @return
+             */
+            public List<QuestionnaireItemComponent> getQuestionSet() {
+                QuestionnaireItemComponent determinantQuestionNoChildren = this.removeChildrenFromQuestionItem(this.determinantQuestionItem);
+                List<QuestionnaireItemComponent> questionSet = new ArrayList<QuestionnaireItemComponent>();
+                questionSet.add(determinantQuestionNoChildren);
+                questionSet.addAll(this.supplementalQuestions);
+                return questionSet;
+            }
 
-                // Create the map of answerResponses->subQuestionItems
-                this.children = new HashMap<String, AdaptiveQuestionnaireNode>();
-                List<QuestionnaireItemComponent> subQuestionItems = questionItem.getItem();
-                for(QuestionnaireItemComponent subQuestionItem : subQuestionItems){
-                    // SubQuestion linkId.
-                    String subQuestionLinkId = subQuestionItem.getLinkId();
-                    // SubQuestion's associated response.
-                    String subQuestionResponse = childIdsToResponses.get(subQuestionLinkId);
-                    // Create a new node for this subQuestion.
-                    AdaptiveQuestionnaireNode subQuestionNode = new AdaptiveQuestionnaireNode(subQuestionItem);
-                    this.children.put(subQuestionResponse, subQuestionNode);
+            /**
+             * Returns a new question item that is indentical to the input qusetion item except without the children.
+             * @param inputQuestionItem
+             * @return
+             */
+            private QuestionnaireItemComponent removeChildrenFromQuestionItem(QuestionnaireItemComponent inputQuestionItem){
+                QuestionnaireItemComponent questionItemNoChildren = new QuestionnaireItemComponent();
+                questionItemNoChildren.setLinkId(inputQuestionItem.getLinkId());
+                questionItemNoChildren.setText(inputQuestionItem.getText());
+                questionItemNoChildren.setType(inputQuestionItem.getType());
+                questionItemNoChildren.setRequired(inputQuestionItem.getRequired());
+                questionItemNoChildren.setAnswerOption(inputQuestionItem.getAnswerOption());
+                return questionItemNoChildren;
+            }
+
+            /**
+             * Returns whether the determinant question contains the requested response.
+             * @param response
+             * @return
+             */
+            public boolean hasResponse(String response) {
+                try {
+                    return this.children.containsKey(response);
+                } catch (NullPointerException e) {
+                    throw new NullPointerException("Null pointer thrown for children " + this.children + " with response " + response);
                 }
             }
 
@@ -171,7 +245,7 @@ public AdaptiveQuestionnaireNode(QuestionnaireItemComponent questionItem) {
              * @return
              */
             private boolean isLeafNode() {
-                return this.children.size() < 1;
+                return this.children == null || this.children.size() < 1;
             }
 
             /**
@@ -187,10 +261,14 @@ private AdaptiveQuestionnaireNode getChildForResponse(String response) {
              * Returns the question linkid for this node question.
              * @return
              */
-            public String getQuestionId() {
-                return this.questionItem.getLinkId();
+            public String getDeterminantQuestionId() {
+                return this.determinantQuestionItem.getLinkId();
             }
         }
+
+        public List<QuestionnaireItemComponent> getCurrentQuestionSet() {
+            return this.root.getQuestionSet();
+        }
     }
 
     // Logger.
@@ -199,7 +277,7 @@ public String getQuestionId() {
     private static final Map<String, AdaptiveQuestionnaireTree> questionnaireTrees = new HashMap<String, AdaptiveQuestionnaireTree>();
 
     /**
-     * 
+     * Retrieves the next question based on the request.
      * @param request
      * @param entity
      * @return
@@ -210,7 +288,7 @@ public ResponseEntity<String> retrieveNextQuestion(HttpServletRequest request, H
     }
 
     /**
-     *
+     * Returns the next question based on the request.
      * @param body
      * @param request
      * @return
@@ -274,35 +352,36 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     if(cdsQuestionnaire == null) {
                         throw new RuntimeException("Requested CDS Questionnaire \'" + questionnaireId + "\' was not imported and may not exist.");
                     }
-                    // Pull the first question from the CDS Questionnaire because it should be the top-level question (and the only item in the list).
-                    QuestionnaireItemComponent topQuestionItem = cdsQuestionnaire.getItemFirstRep();
-                    topQuestionItem = removeChildrenFromQuestionItem(topQuestionItem);
-                    // Add the first Question item to the contained Questionnaire in the response/request QuestionnaireResponse JSON as part of the response.
-                    inputQuestionnaireFromRequest.addItem(topQuestionItem);
 
                     // Build the tree and don't expect any answers since we only just received the required questions.
                     AdaptiveQuestionnaireTree newTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
                     questionnaireTrees.put(questionnaireId, newTree);
                     logger.info("--- Built Questionnaire Tree for " + questionnaireId);
+
+                    List<QuestionnaireItemComponent> questionSet = newTree.getCurrentQuestionSet();
+                    final Questionnaire tempInputQuestionnaireFromRequest = inputQuestionnaireFromRequest;
+                    questionSet.forEach(questionItem -> tempInputQuestionnaireFromRequest.addItem(questionItem));
                 } else {
                     // If there is already a tree with the requested questionnaire id, execute next-question on it with the new request.
                     // Pull the current tree for the requested questionnaire id.
                     AdaptiveQuestionnaireTree currentTree = questionnaireTrees.get(questionnaireId);
                     // Get the previous question Id.
-                    String previousQuestionId = currentTree.getCurrentQuestionId();
+                    String previousQuestionId = currentTree.getCurrentDeterminantQuestionId();
                     // Get the request's answer component of the item with the previous question id.
                     List<QuestionnaireResponseItemComponent> allQuestions = inputQuestionnaireResponse.getItem();
-                    allQuestions = allQuestions.stream().filter((QuestionnaireResponseItemComponent item) -> item.getLinkId().equals(previousQuestionId)).collect(Collectors.toList());
+                    allQuestions = allQuestions.stream().filter(item -> item.getLinkId().equals(previousQuestionId)).collect(Collectors.toList());
+                    if(allQuestions.size() < 1) {
+                        throw new RuntimeException("No given answers in the request references the current question asked. Current Question ID: " + previousQuestionId + ".");
+                    }
                     QuestionnaireResponseItemAnswerComponent answerComponent = allQuestions.get(0).getAnswerFirstRep();
                     // Pull the string response the person gave.
                     String response = answerComponent.getValueCoding().getCode();
                     // Pull the resulting next question that the recieved response points to from the tree without including its children.
-                    QuestionnaireItemComponent nextQuestionItemResult = currentTree.getNextQuestionForResponse(response);
-                    nextQuestionItemResult = removeChildrenFromQuestionItem(nextQuestionItemResult);
-                    // Add the next question to the QuestionnaireResponse.contained[0].item[].
+                    List<QuestionnaireItemComponent> nextQuestionsItemResult = currentTree.getNextQuestionsFromResponse(response);
+                    // Add the next question set to the QuestionnaireResponse.contained[0].item[].
                     Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
-                    containedQuestionnaire.addItem(nextQuestionItemResult);
-                    logger.info("--- Added next question for questionnaire \'" + questionnaireId + "\' for response \'" + response + "\'.");
+                    nextQuestionsItemResult.forEach(questionItem -> containedQuestionnaire.addItem(questionItem));
+                    logger.info("--- Added next question set for questionnaire \'" + questionnaireId + "\' for response \'" + response + "\'.");
 
                     // If this question is a leaf node and is the final question, set the status to "completed"
                     if (currentTree.reachedLeafNode()) {
@@ -326,19 +405,4 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
 
         }
     }
-
-    /**
-     * Returns a new question item that is indentical to the input qusetion item except without the children.
-     * @param inputQuestionItem
-     * @return
-     */
-    private static QuestionnaireItemComponent removeChildrenFromQuestionItem(QuestionnaireItemComponent inputQuestionItem){
-        QuestionnaireItemComponent questionItemNoChildren = new QuestionnaireItemComponent();
-        questionItemNoChildren.setLinkId(inputQuestionItem.getLinkId());
-        questionItemNoChildren.setText(inputQuestionItem.getText());
-        questionItemNoChildren.setType(inputQuestionItem.getType());
-        questionItemNoChildren.setRequired(inputQuestionItem.getRequired());
-        questionItemNoChildren.setAnswerOption(inputQuestionItem.getAnswerOption());
-        return questionItemNoChildren;
-    }
 }
\ No newline at end of file

From d9d40414e87c638c714c42666ab69db24baeea5e Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Wed, 10 Nov 2021 14:00:40 -0500
Subject: [PATCH 14/30] Added multiple questions to be sent based on current
 next-question. Updated CDS-Library extraction.

---
 .../controllers/QuestionnaireController.java  | 43 +++++++++++++------
 .../endpoint/files/CommonFileStore.java       |  8 +++-
 .../files/SubQuestionnaireProcessor.java      |  4 ++
 3 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index c1ad0a5c2..aa238744d 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -17,9 +17,11 @@
 
 import ca.uhn.fhir.parser.DataFormatException;
 import ca.uhn.fhir.parser.IParser;
+import org.hl7.davinci.endpoint.Utils;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -339,8 +341,12 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     try {
                         //TODO: need to determine topic, filename, and fhir version without having them hard coded
                         // File is pulled from the file store
-                        FileResource fileResource = fileStore.getFile("HomeOxygenTherapy", "Questions-HomeOxygenTherapyAdditional.json", "R4", false);
-                        cdsQuestionnaire = (Questionnaire) parser.parseResource(fileResource.getResource().getInputStream());
+                        String baseUrl = Utils.getApplicationBaseUrl(request).toString() + "/";
+                        FileResource fileResource = fileStore.getFhirResourceById("r4", "questionnaire", "HomeOxygenTherapyAdditional", baseUrl);
+                        org.springframework.core.io.Resource resource = fileResource.getResource();
+                        InputStream resourceStream = resource.getInputStream();
+                        cdsQuestionnaire = (Questionnaire) parser.parseResource(resourceStream);
+
                         logger.info("--- Imported Questionnaire " + cdsQuestionnaire.getId());
                     } catch (DataFormatException e) {
                         e.printStackTrace();
@@ -358,9 +364,9 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     questionnaireTrees.put(questionnaireId, newTree);
                     logger.info("--- Built Questionnaire Tree for " + questionnaireId);
 
+                    // Add the set of questions to the response.
                     List<QuestionnaireItemComponent> questionSet = newTree.getCurrentQuestionSet();
-                    final Questionnaire tempInputQuestionnaireFromRequest = inputQuestionnaireFromRequest;
-                    questionSet.forEach(questionItem -> tempInputQuestionnaireFromRequest.addItem(questionItem));
+                    addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, questionSet);
                 } else {
                     // If there is already a tree with the requested questionnaire id, execute next-question on it with the new request.
                     // Pull the current tree for the requested questionnaire id.
@@ -368,19 +374,18 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     // Get the previous question Id.
                     String previousQuestionId = currentTree.getCurrentDeterminantQuestionId();
                     // Get the request's answer component of the item with the previous question id.
-                    List<QuestionnaireResponseItemComponent> allQuestions = inputQuestionnaireResponse.getItem();
-                    allQuestions = allQuestions.stream().filter(item -> item.getLinkId().equals(previousQuestionId)).collect(Collectors.toList());
-                    if(allQuestions.size() < 1) {
+                    List<QuestionnaireResponseItemComponent> allResponses = inputQuestionnaireResponse.getItem();
+                    List<QuestionnaireResponseItemComponent> currentResponses = allResponses.stream().filter(item -> item.getLinkId().equals(previousQuestionId)).collect(Collectors.toList());
+                    if(currentResponses.size() < 1) {
                         throw new RuntimeException("No given answers in the request references the current question asked. Current Question ID: " + previousQuestionId + ".");
                     }
-                    QuestionnaireResponseItemAnswerComponent answerComponent = allQuestions.get(0).getAnswerFirstRep();
-                    // Pull the string response the person gave.
+                    // Pull the string response given by the current response.
+                    QuestionnaireResponseItemAnswerComponent answerComponent = currentResponses.get(0).getAnswerFirstRep();
                     String response = answerComponent.getValueCoding().getCode();
                     // Pull the resulting next question that the recieved response points to from the tree without including its children.
-                    List<QuestionnaireItemComponent> nextQuestionsItemResult = currentTree.getNextQuestionsFromResponse(response);
-                    // Add the next question set to the QuestionnaireResponse.contained[0].item[].
-                    Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
-                    nextQuestionsItemResult.forEach(questionItem -> containedQuestionnaire.addItem(questionItem));
+                    List<QuestionnaireItemComponent> nextQuestionSetResult = currentTree.getNextQuestionsFromResponse(response);
+                    // Add the next set of questions to the response.
+                    addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, nextQuestionSetResult);
                     logger.info("--- Added next question set for questionnaire \'" + questionnaireId + "\' for response \'" + response + "\'.");
 
                     // If this question is a leaf node and is the final question, set the status to "completed"
@@ -402,7 +407,17 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                         .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
                         .body("Invalid input questionnaire does not exist");
             }
-
         }
     }
+
+    /**
+     * Adds the given set of questions to the contained questionniare in the questionnaire response.
+     * @param inputQuestionnaireResponse
+     * @param questionSet
+     */
+    private void addQuestionSetToQuestionnaireResponse(QuestionnaireResponse inputQuestionnaireResponse, List<QuestionnaireItemComponent> questionSet) {
+        // Add the next question set to the QuestionnaireResponse.contained[0].item[].
+        Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
+        questionSet.forEach(questionItem -> containedQuestionnaire.addItem(questionItem));
+    }
 }
\ No newline at end of file
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java b/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
index a10381674..f4497e39b 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
@@ -128,9 +128,15 @@ public FileResource getFhirResourceById(String fhirVersion, String resourceType,
     logger.info("CommonFileStore::getFhirResourceById(): " + fhirVersion + "/" + resourceType + "/" + id);
 
     FhirResourceCriteria criteria = new FhirResourceCriteria();
-    criteria.setFhirVersion(fhirVersion).setResourceType(resourceType).setId(id);
+    criteria.setFhirVersion(fhirVersion).setResourceType(resourceType).setId(id).setName(id);
     List<FhirResource> fhirResourceList = fhirResources.findById(criteria);
     FileResource resource = readFhirResourceFromFiles(fhirResourceList, fhirVersion, baseUrl);
+    System.out.println("Resource Pulled: " + resource + resource.getFilename());
+    System.out.println("Fhir Resource List: " + fhirResourceList);
+    System.out.println("Criteria ID: " + criteria);
+    System.out.println("BaseUrl: " + baseUrl);
+    System.out.println("--------------------------");
+
 
     if ((resource != null) && fhirVersion.equalsIgnoreCase("r4")) {
 
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/files/SubQuestionnaireProcessor.java b/server/src/main/java/org/hl7/davinci/endpoint/files/SubQuestionnaireProcessor.java
index f4fcf781b..953509983 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/files/SubQuestionnaireProcessor.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/files/SubQuestionnaireProcessor.java
@@ -76,6 +76,10 @@ private void processItemList(List<QuestionnaireItemComponent> itemList, FileStor
     for (int i = 0; i < itemList.size();) {
       List<QuestionnaireItemComponent> returnedItemList = 
         processItem(itemList.get(i), fileStore, baseUrl, containedList, extensionList);
+
+      // if(itemList.get(i).getItem().size() > 0){
+      //   this.processItemList(itemList.get(i).getItem(), fileStore, baseUrl, containedList, extensionList);
+      // }
       
       if (returnedItemList.size() == 0) {
         continue;

From 79b23b8ca81a76326f785ca406adf9769cbc74be Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Tue, 30 Nov 2021 16:48:19 -0500
Subject: [PATCH 15/30] Updated CDS-Library resource call for adform.

---
 .../controllers/QuestionnaireController.java  | 28 ++++++++++++++-----
 .../endpoint/files/CommonFileStore.java       |  2 +-
 2 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index aa238744d..179bc1bdc 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -340,12 +340,25 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     Questionnaire cdsQuestionnaire = null;
                     try {
                         //TODO: need to determine topic, filename, and fhir version without having them hard coded
-                        // File is pulled from the file store
-                        String baseUrl = Utils.getApplicationBaseUrl(request).toString() + "/";
-                        FileResource fileResource = fileStore.getFhirResourceById("r4", "questionnaire", "HomeOxygenTherapyAdditional", baseUrl);
-                        org.springframework.core.io.Resource resource = fileResource.getResource();
-                        InputStream resourceStream = resource.getInputStream();
-                        cdsQuestionnaire = (Questionnaire) parser.parseResource(resourceStream);
+                        boolean pullFromResources = false;
+                        if(pullFromResources){
+                            // Resource is pulled from the file store as a resource.
+                            String baseUrl = Utils.getApplicationBaseUrl(request).toString() + "/";
+                            FileResource fileResource = fileStore.getFhirResourceById("r4", "questionnaire", "HomeOxygenTherapyAdditional", baseUrl);
+                            org.springframework.core.io.Resource resource = fileResource.getResource();
+                            InputStream resourceStream = resource.getInputStream();
+                            cdsQuestionnaire = (Questionnaire) parser.parseResource(resourceStream);
+                        } else {
+                            // File is pulled from the file store as a file.
+                            FileResource fileResource = fileStore.getFile("HomeOxygenTherapy", "Questions-HomeOxygenTherapyAdditionalAdaptive.json", "R4", false);
+                            if(fileResource == null) {
+                                throw new RuntimeException("File resource pulled from the filestore is null.");
+                            }
+                            if(fileResource.getResource() == null) {
+                                throw new RuntimeException("File resource pulled from the filestore has a null getResource().");
+                            }
+                            cdsQuestionnaire = (Questionnaire) parser.parseResource(fileResource.getResource().getInputStream());
+                        }
 
                         logger.info("--- Imported Questionnaire " + cdsQuestionnaire.getId());
                     } catch (DataFormatException e) {
@@ -396,7 +409,8 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 }
 
                 logger.info("---- Get meta profile " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
-                
+                logger.info("---- Sent response back " + inputQuestionnaireFromRequest.getId());
+
                 // Build and send the response.
                 String formattedResourceString = ctx.newJsonParser().encodeResourceToString(inputQuestionnaireResponse);
                 return ResponseEntity.status(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java b/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
index f4497e39b..0f47acb0a 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
@@ -131,7 +131,7 @@ public FileResource getFhirResourceById(String fhirVersion, String resourceType,
     criteria.setFhirVersion(fhirVersion).setResourceType(resourceType).setId(id).setName(id);
     List<FhirResource> fhirResourceList = fhirResources.findById(criteria);
     FileResource resource = readFhirResourceFromFiles(fhirResourceList, fhirVersion, baseUrl);
-    System.out.println("Resource Pulled: " + resource + resource.getFilename());
+    System.out.println("Resource Pulled: " + resource + "-" + resource.getFilename());
     System.out.println("Fhir Resource List: " + fhirResourceList);
     System.out.println("Criteria ID: " + criteria);
     System.out.println("BaseUrl: " + baseUrl);

From 19c0c193814d7de7b52aa32e8b6eb4970bf38631 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Wed, 1 Dec 2021 12:41:11 -0500
Subject: [PATCH 16/30] Updated adform tree to rerun with each request so that
 any input can produce the correct results.

---
 .../controllers/QuestionnaireController.java  | 156 +++++++-----------
 1 file changed, 56 insertions(+), 100 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index 179bc1bdc..8ef7a66c7 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -40,6 +40,7 @@
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemComponent;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseStatus;
+import org.hl7.fhir.r4.model.Reference;
 
 // --- ORDER OF RESPONSE-REQUEST OPERATIONS
 // (REQUEST) External user sends the initial QuestionnaireResponse JSON that contains which questionnaire it would like to trigger as n element the "contained" field.
@@ -62,7 +63,7 @@ public class QuestionnaireController {
      */
     private class AdaptiveQuestionnaireTree {
         
-        // The current question (defiend within the node).
+        // The current question (defined within the node).
         private AdaptiveQuestionnaireNode root;
     
         /**
@@ -80,41 +81,24 @@ public AdaptiveQuestionnaireTree(Questionnaire inputQuestionnaire) {
 
         /**
          * Returns the next question based on the response to the current question. Also sets the next question based on that response.
-         * @param response  The response given to this question.
+         * @param allAnswerItems  The set of answer items given to this tree.
          * @return
          */
-        public List<QuestionnaireItemComponent> getNextQuestionsFromResponse(String response){
-
-            if(!root.hasResponse(response)){
-                throw new RuntimeException("Not a valid response for question: \'" + this.root.determinantQuestionItem.getText() + "\' with response \'" + response + "\'. Possible responses for this question: \'" + root.children.keySet() + "\'.");
+        public List<QuestionnaireItemComponent> getNextQuestionsForAnswers(List<QuestionnaireResponseItemComponent> allResponseItems) {
+            if(allResponseItems == null) {
+                throw new NullPointerException("Input answer items is null.");
             }
-            AdaptiveQuestionnaireNode nextQuestionSetNode = this.root.getChildForResponse(response);
-            // Pull the current question set.
-            List<QuestionnaireItemComponent> currentQuestionnaireItems = nextQuestionSetNode.getQuestionSet();
-            // Set the new next question.
-            this.root = nextQuestionSetNode;
-            // Return the prior current question.
-
-            currentQuestionnaireItems.get(0).getModifierExtensionFirstRep().getValue();
-            return currentQuestionnaireItems;
+            return this.root.getNextQuestionForAnswers(allResponseItems);
         }
 
         /**
-         * Returns whether this has reached a leaf node.
+         * TODO Returns whether this has reached a leaf node. TODO - NEEDS TO BE UPDATED
          * @param response
          * @return
          */
         public boolean reachedLeafNode() {
             return this.root.isLeafNode();
         }
-
-        /**
-         * Returns the linkid of the current question.
-         * @return
-         */
-        public String getCurrentDeterminantQuestionId() {
-            return this.root.getDeterminantQuestionId();
-        }
     
         /**
          * Inner class that describes a node of the tree.
@@ -180,6 +164,30 @@ public AdaptiveQuestionnaireNode(QuestionnaireItemComponent determinantQuestion)
                 }
             }
 
+            /**
+             * Returns the next question based on the set of provided answers.
+             * @param allResponseItems
+             * @return
+             */
+            public List<QuestionnaireItemComponent> getNextQuestionForAnswers(List<QuestionnaireResponseItemComponent> allResponseItems) {
+
+                // Extract the current question being answered from the list if answer items.
+                String currentQuestionId = this.determinantQuestionItem.getLinkId();
+                List<QuestionnaireResponseItemComponent> currentQuestionResponses = allResponseItems.stream().filter(answerItem -> answerItem.getLinkId().equals(currentQuestionId)).collect(Collectors.toList());
+                if(currentQuestionResponses.size() != 1) {
+                    // If there are no more answer items to check, we've reached the end of the recursion.
+                    return this.getQuestionSet();
+                }
+
+                QuestionnaireResponseItemComponent currentQuestionResponse = currentQuestionResponses.get(0);
+                QuestionnaireResponseItemAnswerComponent currentQuestionAnswer = currentQuestionResponse.getAnswerFirstRep();
+
+                // With the currrent question answer in hand, extract the next question.
+                AdaptiveQuestionnaireNode nextNode = this.children.get(currentQuestionAnswer.getValueCoding().getCode());
+                
+                return nextNode.getNextQuestionForAnswers(allResponseItems.stream().filter(responseItem -> !responseItem.equals(currentQuestionResponse)).collect(Collectors.toList()));
+            }
+
             /**
              * Returns the question items in the given list that do not have the linkids of the given list of strings.
              * @param questionItems
@@ -230,47 +238,15 @@ private QuestionnaireItemComponent removeChildrenFromQuestionItem(QuestionnaireI
             }
 
             /**
-             * Returns whether the determinant question contains the requested response.
-             * @param response
-             * @return
-             */
-            public boolean hasResponse(String response) {
-                try {
-                    return this.children.containsKey(response);
-                } catch (NullPointerException e) {
-                    throw new NullPointerException("Null pointer thrown for children " + this.children + " with response " + response);
-                }
-            }
-
-            /**
-             * Returns whether this questionniare is a leaf node.
+             * TODO Returns whether this questionniare is a leaf node. TODO - NEEDS TO BE UPDATED.
              * @return
              */
             private boolean isLeafNode() {
                 return this.children == null || this.children.size() < 1;
             }
 
-            /**
-             * Returns the child associated with this node for the given response.
-             * @param response
-             * @return
-             */
-            private AdaptiveQuestionnaireNode getChildForResponse(String response) {
-                return this.children.get(response);
-            }
-
-            /**
-             * Returns the question linkid for this node question.
-             * @return
-             */
-            public String getDeterminantQuestionId() {
-                return this.determinantQuestionItem.getLinkId();
-            }
         }
 
-        public List<QuestionnaireItemComponent> getCurrentQuestionSet() {
-            return this.root.getQuestionSet();
-        }
     }
 
     // Logger.
@@ -325,15 +301,11 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 }
             }
 
-            String questionnaireId = inputQuestionnaireFromRequest.getId();
+            String questionnaireId = ((Reference) inputQuestionnaireResponse.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/contained-id").getValue()).getReference();
+            System.out.println("Input Questionnaire: " + questionnaireId);
 
             if (inputQuestionnaireFromRequest != null) {
 
-                // If there are no item answer responses in the sent JSON, reset the tree so that we can restart the question process.
-                if(inputQuestionnaireFromRequest.getItem().size() < 1) {
-                    questionnaireTrees.remove(questionnaireId);
-                }
-
                 if(!questionnaireTrees.containsKey(questionnaireId)){
                     // If there is not already a tree that matches the requested questionnaire id, build it.
                     // Import the requested CDS-Library Questionnaire.
@@ -368,48 +340,32 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                     } catch (IOException e) {
                         e.printStackTrace();
                     }
-                    if(cdsQuestionnaire == null) {
-                        throw new RuntimeException("Requested CDS Questionnaire \'" + questionnaireId + "\' was not imported and may not exist.");
-                    }
 
-                    // Build the tree and don't expect any answers since we only just received the required questions.
+                    // Build the tree.
                     AdaptiveQuestionnaireTree newTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
                     questionnaireTrees.put(questionnaireId, newTree);
                     logger.info("--- Built Questionnaire Tree for " + questionnaireId);
+                }
 
-                    // Add the set of questions to the response.
-                    List<QuestionnaireItemComponent> questionSet = newTree.getCurrentQuestionSet();
-                    addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, questionSet);
-                } else {
-                    // If there is already a tree with the requested questionnaire id, execute next-question on it with the new request.
-                    // Pull the current tree for the requested questionnaire id.
-                    AdaptiveQuestionnaireTree currentTree = questionnaireTrees.get(questionnaireId);
-                    // Get the previous question Id.
-                    String previousQuestionId = currentTree.getCurrentDeterminantQuestionId();
-                    // Get the request's answer component of the item with the previous question id.
-                    List<QuestionnaireResponseItemComponent> allResponses = inputQuestionnaireResponse.getItem();
-                    List<QuestionnaireResponseItemComponent> currentResponses = allResponses.stream().filter(item -> item.getLinkId().equals(previousQuestionId)).collect(Collectors.toList());
-                    if(currentResponses.size() < 1) {
-                        throw new RuntimeException("No given answers in the request references the current question asked. Current Question ID: " + previousQuestionId + ".");
-                    }
-                    // Pull the string response given by the current response.
-                    QuestionnaireResponseItemAnswerComponent answerComponent = currentResponses.get(0).getAnswerFirstRep();
-                    String response = answerComponent.getValueCoding().getCode();
-                    // Pull the resulting next question that the recieved response points to from the tree without including its children.
-                    List<QuestionnaireItemComponent> nextQuestionSetResult = currentTree.getNextQuestionsFromResponse(response);
-                    // Add the next set of questions to the response.
-                    addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, nextQuestionSetResult);
-                    logger.info("--- Added next question set for questionnaire \'" + questionnaireId + "\' for response \'" + response + "\'.");
-
-                    // If this question is a leaf node and is the final question, set the status to "completed"
-                    if (currentTree.reachedLeafNode()) {
-                        inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
-                        logger.info("--- Questionnaire leaf node reached, setting status to \"completed\".");
-                    }
+                // Pull the tree for the requested questionnaire id.
+                AdaptiveQuestionnaireTree currentTree = questionnaireTrees.get(questionnaireId);
+                // Get the request's set of answer responses.
+                List<QuestionnaireResponseItemComponent> allResponses = inputQuestionnaireResponse.getItem();
+                // Pull the resulting next question that the recieved responses and answers point to from the tree without including its children.
+                List<QuestionnaireItemComponent> nextQuestionSetResults = currentTree.getNextQuestionsForAnswers(allResponses);
+                // Add the next set of questions to the response.
+                QuestionnaireController.addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, nextQuestionSetResults);
+
+                logger.info("--- Added next question set for questionnaire \'" + questionnaireId + "\' for response \'" + allResponses + "\'.");
+
+                // If this question is a leaf node and is the final question, set the status to "completed"
+                if (currentTree.reachedLeafNode()) {
+                    inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
+                    logger.info("--- Questionnaire leaf node reached, setting status to \"completed\".");
                 }
 
-                logger.info("---- Get meta profile " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
-                logger.info("---- Sent response back " + inputQuestionnaireFromRequest.getId());
+                logger.info("---- Get meta profile: " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
+                logger.info("---- Sending response: " + inputQuestionnaireFromRequest.getId());
 
                 // Build and send the response.
                 String formattedResourceString = ctx.newJsonParser().encodeResourceToString(inputQuestionnaireResponse);
@@ -419,7 +375,7 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
             } else {
                 return ResponseEntity.status(HttpStatus.BAD_REQUEST).contentType(MediaType.APPLICATION_JSON)
                         .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
-                        .body("Invalid input questionnaire does not exist");
+                        .body("Input questionnaire from the request does not exist.");
             }
         }
     }
@@ -429,7 +385,7 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
      * @param inputQuestionnaireResponse
      * @param questionSet
      */
-    private void addQuestionSetToQuestionnaireResponse(QuestionnaireResponse inputQuestionnaireResponse, List<QuestionnaireItemComponent> questionSet) {
+    private static void addQuestionSetToQuestionnaireResponse(QuestionnaireResponse inputQuestionnaireResponse, List<QuestionnaireItemComponent> questionSet) {
         // Add the next question set to the QuestionnaireResponse.contained[0].item[].
         Questionnaire containedQuestionnaire = (Questionnaire) inputQuestionnaireResponse.getContained().get(0);
         questionSet.forEach(questionItem -> containedQuestionnaire.addItem(questionItem));

From 321c40723a2d2e2c85f1492f028059fd8026d120 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Wed, 1 Dec 2021 12:48:51 -0500
Subject: [PATCH 17/30] Cleanup, added TODO comments with missing
 functionality.

---
 .../controllers/QuestionnaireController.java  | 22 ++++++++-----------
 1 file changed, 9 insertions(+), 13 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index 8ef7a66c7..a07052cf3 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -90,15 +90,6 @@ public List<QuestionnaireItemComponent> getNextQuestionsForAnswers(List<Question
             }
             return this.root.getNextQuestionForAnswers(allResponseItems);
         }
-
-        /**
-         * TODO Returns whether this has reached a leaf node. TODO - NEEDS TO BE UPDATED
-         * @param response
-         * @return
-         */
-        public boolean reachedLeafNode() {
-            return this.root.isLeafNode();
-        }
     
         /**
          * Inner class that describes a node of the tree.
@@ -143,7 +134,7 @@ public AdaptiveQuestionnaireNode(QuestionnaireItemComponent determinantQuestion)
                         String possibleAnswerResponse = answerOption.getValueCoding().getCode();
                         // Check for issues.
                         if(answerNextQuestionId == null || possibleAnswerResponse == null){
-                            throw new RuntimeException("Malformed Adaptive Questionnaire. Missing a questionID or answer response.");
+                            throw new RuntimeException("Malformed Adaptive Questionnaire. Missing a question ID or answer response.");
                         }
                         // Add the key-value pair of next question id to its assocated answer response.
                         childIdsToResponses.put(answerNextQuestionId, possibleAnswerResponse);
@@ -177,15 +168,22 @@ public List<QuestionnaireItemComponent> getNextQuestionForAnswers(List<Questionn
                 if(currentQuestionResponses.size() != 1) {
                     // If there are no more answer items to check, we've reached the end of the recursion.
                     return this.getQuestionSet();
+                    // TODO - this could cause an unexpected end-of-questionnaire issue if incorrect responses are given.
                 }
 
                 QuestionnaireResponseItemComponent currentQuestionResponse = currentQuestionResponses.get(0);
                 QuestionnaireResponseItemAnswerComponent currentQuestionAnswer = currentQuestionResponse.getAnswerFirstRep();
 
+                if(this.isLeafNode()){
+                    // TODO: How to notify controller that this is a leaf node?
+                }
+
                 // With the currrent question answer in hand, extract the next question.
                 AdaptiveQuestionnaireNode nextNode = this.children.get(currentQuestionAnswer.getValueCoding().getCode());
                 
-                return nextNode.getNextQuestionForAnswers(allResponseItems.stream().filter(responseItem -> !responseItem.equals(currentQuestionResponse)).collect(Collectors.toList()));
+                // Has to be done this way without removing the previous answer response so that we don't alter the original list object.
+                List<QuestionnaireResponseItemComponent> nextResponseItems = allResponseItems.stream().filter(responseItem -> !responseItem.equals(currentQuestionResponse)).collect(Collectors.toList());
+                return nextNode.getNextQuestionForAnswers(nextResponseItems);
             }
 
             /**
@@ -244,9 +242,7 @@ private QuestionnaireItemComponent removeChildrenFromQuestionItem(QuestionnaireI
             private boolean isLeafNode() {
                 return this.children == null || this.children.size() < 1;
             }
-
         }
-
     }
 
     // Logger.

From fa760d45ad108e2248f712ec8f67dc2ba675e3a4 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Wed, 1 Dec 2021 13:27:37 -0500
Subject: [PATCH 18/30] Commented out leaf node code

---
 .../endpoint/controllers/QuestionnaireController.java     | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index a07052cf3..af062fc0c 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -355,10 +355,10 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 logger.info("--- Added next question set for questionnaire \'" + questionnaireId + "\' for response \'" + allResponses + "\'.");
 
                 // If this question is a leaf node and is the final question, set the status to "completed"
-                if (currentTree.reachedLeafNode()) {
-                    inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
-                    logger.info("--- Questionnaire leaf node reached, setting status to \"completed\".");
-                }
+                // if (currentTree.reachedLeafNode()) {
+                //     inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
+                //     logger.info("--- Questionnaire leaf node reached, setting status to \"completed\".");
+                // }
 
                 logger.info("---- Get meta profile: " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
                 logger.info("---- Sending response: " + inputQuestionnaireFromRequest.getId());

From fe21db8975e25856a5338bb2844c72e5c9acf7d7 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Wed, 1 Dec 2021 15:20:17 -0500
Subject: [PATCH 19/30] Added support for string-based answers, added leaf node
 functionality. Refactored CDS import.

---
 .../controllers/QuestionnaireController.java  | 127 +++++++++++-------
 1 file changed, 77 insertions(+), 50 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index af062fc0c..7aad16276 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -1,5 +1,6 @@
 package org.hl7.davinci.endpoint.controllers;
 
+import org.aspectj.weaver.patterns.TypePatternQuestions.Question;
 import org.hl7.davinci.endpoint.Application;
 import org.hl7.davinci.endpoint.files.FileResource;
 import org.hl7.davinci.endpoint.files.FileStore;
@@ -31,10 +32,13 @@
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.hl7.fhir.r4.model.Coding;
 import org.hl7.fhir.r4.model.Questionnaire;
 import org.hl7.fhir.r4.model.QuestionnaireResponse;
 import org.hl7.fhir.r4.model.Resource;
 import org.hl7.fhir.r4.model.ResourceType;
+import org.hl7.fhir.r4.model.StringType;
+import org.hl7.fhir.r4.model.Type;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
@@ -63,7 +67,7 @@ public class QuestionnaireController {
      */
     private class AdaptiveQuestionnaireTree {
         
-        // The current question (defined within the node).
+        // The initial question node of the tree.
         private AdaptiveQuestionnaireNode root;
     
         /**
@@ -81,14 +85,15 @@ public AdaptiveQuestionnaireTree(Questionnaire inputQuestionnaire) {
 
         /**
          * Returns the next question based on the response to the current question. Also sets the next question based on that response.
+         * @param inputQuestionnaireResponse
          * @param allAnswerItems  The set of answer items given to this tree.
          * @return
          */
-        public List<QuestionnaireItemComponent> getNextQuestionsForAnswers(List<QuestionnaireResponseItemComponent> allResponseItems) {
+        public List<QuestionnaireItemComponent> getNextQuestionsForAnswers(List<QuestionnaireResponseItemComponent> allResponseItems, QuestionnaireResponse inputQuestionnaireResponse) {
             if(allResponseItems == null) {
                 throw new NullPointerException("Input answer items is null.");
             }
-            return this.root.getNextQuestionForAnswers(allResponseItems);
+            return this.root.getNextQuestionForAnswers(allResponseItems, inputQuestionnaireResponse);
         }
     
         /**
@@ -158,9 +163,10 @@ public AdaptiveQuestionnaireNode(QuestionnaireItemComponent determinantQuestion)
             /**
              * Returns the next question based on the set of provided answers.
              * @param allResponseItems
+             * @param inputQuestionnaireResponse
              * @return
              */
-            public List<QuestionnaireItemComponent> getNextQuestionForAnswers(List<QuestionnaireResponseItemComponent> allResponseItems) {
+            public List<QuestionnaireItemComponent> getNextQuestionForAnswers(List<QuestionnaireResponseItemComponent> allResponseItems, QuestionnaireResponse inputQuestionnaireResponse) {
 
                 // Extract the current question being answered from the list if answer items.
                 String currentQuestionId = this.determinantQuestionItem.getLinkId();
@@ -174,16 +180,32 @@ public List<QuestionnaireItemComponent> getNextQuestionForAnswers(List<Questionn
                 QuestionnaireResponseItemComponent currentQuestionResponse = currentQuestionResponses.get(0);
                 QuestionnaireResponseItemAnswerComponent currentQuestionAnswer = currentQuestionResponse.getAnswerFirstRep();
 
-                if(this.isLeafNode()){
-                    // TODO: How to notify controller that this is a leaf node?
-                }
 
                 // With the currrent question answer in hand, extract the next question.
-                AdaptiveQuestionnaireNode nextNode = this.children.get(currentQuestionAnswer.getValueCoding().getCode());
+                Type value = currentQuestionAnswer.getValue();
+                String response;
+                // Not a fan of this, but not sure how else to determine the value based on different possible input types.
+                if(value instanceof StringType){
+                    response = currentQuestionAnswer.getValueStringType().asStringValue();
+                } else if(value instanceof Coding){
+                    response = currentQuestionAnswer.getValueCoding().getCode();
+                } else {
+                    throw new RuntimeException("Answer does not match one of the possible input types.");
+                }
+                if(!children.containsKey(response)){
+                    throw new NullPointerException("Response does not match with a possible next question.");
+                }
+                AdaptiveQuestionnaireNode nextNode = this.children.get(response);
+
+                if(nextNode.isLeafNode()){
+                    // Since the next node is a leaf node, set the questionnaire response status to complete.
+                    inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
+                    return this.getQuestionSet();
+                }
                 
                 // Has to be done this way without removing the previous answer response so that we don't alter the original list object.
                 List<QuestionnaireResponseItemComponent> nextResponseItems = allResponseItems.stream().filter(responseItem -> !responseItem.equals(currentQuestionResponse)).collect(Collectors.toList());
-                return nextNode.getNextQuestionForAnswers(nextResponseItems);
+                return nextNode.getNextQuestionForAnswers(nextResponseItems, inputQuestionnaireResponse);
             }
 
             /**
@@ -236,7 +258,7 @@ private QuestionnaireItemComponent removeChildrenFromQuestionItem(QuestionnaireI
             }
 
             /**
-             * TODO Returns whether this questionniare is a leaf node. TODO - NEEDS TO BE UPDATED.
+             * Returns whether this questionniare is a leaf node.
              * @return
              */
             private boolean isLeafNode() {
@@ -305,38 +327,8 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 if(!questionnaireTrees.containsKey(questionnaireId)){
                     // If there is not already a tree that matches the requested questionnaire id, build it.
                     // Import the requested CDS-Library Questionnaire.
-                    Questionnaire cdsQuestionnaire = null;
-                    try {
-                        //TODO: need to determine topic, filename, and fhir version without having them hard coded
-                        boolean pullFromResources = false;
-                        if(pullFromResources){
-                            // Resource is pulled from the file store as a resource.
-                            String baseUrl = Utils.getApplicationBaseUrl(request).toString() + "/";
-                            FileResource fileResource = fileStore.getFhirResourceById("r4", "questionnaire", "HomeOxygenTherapyAdditional", baseUrl);
-                            org.springframework.core.io.Resource resource = fileResource.getResource();
-                            InputStream resourceStream = resource.getInputStream();
-                            cdsQuestionnaire = (Questionnaire) parser.parseResource(resourceStream);
-                        } else {
-                            // File is pulled from the file store as a file.
-                            FileResource fileResource = fileStore.getFile("HomeOxygenTherapy", "Questions-HomeOxygenTherapyAdditionalAdaptive.json", "R4", false);
-                            if(fileResource == null) {
-                                throw new RuntimeException("File resource pulled from the filestore is null.");
-                            }
-                            if(fileResource.getResource() == null) {
-                                throw new RuntimeException("File resource pulled from the filestore has a null getResource().");
-                            }
-                            cdsQuestionnaire = (Questionnaire) parser.parseResource(fileResource.getResource().getInputStream());
-                        }
-
-                        logger.info("--- Imported Questionnaire " + cdsQuestionnaire.getId());
-                    } catch (DataFormatException e) {
-                        e.printStackTrace();
-                    } catch (FileNotFoundException e) {
-                        e.printStackTrace();
-                    } catch (IOException e) {
-                        e.printStackTrace();
-                    }
-
+                    Questionnaire cdsQuestionnaire = QuestionnaireController.importCdsQuestionnaire(request, parser, fileStore, "Questions-HomeOxygenTherapyAdditionalAdaptive.json");
+                    
                     // Build the tree.
                     AdaptiveQuestionnaireTree newTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
                     questionnaireTrees.put(questionnaireId, newTree);
@@ -348,18 +340,11 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 // Get the request's set of answer responses.
                 List<QuestionnaireResponseItemComponent> allResponses = inputQuestionnaireResponse.getItem();
                 // Pull the resulting next question that the recieved responses and answers point to from the tree without including its children.
-                List<QuestionnaireItemComponent> nextQuestionSetResults = currentTree.getNextQuestionsForAnswers(allResponses);
+                List<QuestionnaireItemComponent> nextQuestionSetResults = currentTree.getNextQuestionsForAnswers(allResponses, inputQuestionnaireResponse);
                 // Add the next set of questions to the response.
                 QuestionnaireController.addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, nextQuestionSetResults);
 
                 logger.info("--- Added next question set for questionnaire \'" + questionnaireId + "\' for response \'" + allResponses + "\'.");
-
-                // If this question is a leaf node and is the final question, set the status to "completed"
-                // if (currentTree.reachedLeafNode()) {
-                //     inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
-                //     logger.info("--- Questionnaire leaf node reached, setting status to \"completed\".");
-                // }
-
                 logger.info("---- Get meta profile: " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
                 logger.info("---- Sending response: " + inputQuestionnaireFromRequest.getId());
 
@@ -376,6 +361,48 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
         }
     }
 
+    /**
+     * Imports the requested questionnaire from the CDS-Library.
+     * @param fileStore2
+     * @param parser
+     * @param request
+     * @return
+     */
+    private static Questionnaire importCdsQuestionnaire(HttpServletRequest request, IParser parser, FileStore fileStore, String questionnaireId) {
+        Questionnaire cdsQuestionnaire = null;
+        try {
+            //TODO: need to determine topic, filename, and fhir version without having them hard coded
+            boolean pullFromResources = false;
+            if(pullFromResources){
+                // Resource is pulled from the file store as a resource.
+                String baseUrl = Utils.getApplicationBaseUrl(request).toString() + "/";
+                FileResource fileResource = fileStore.getFhirResourceById("r4", "questionnaire", questionnaireId, baseUrl);
+                org.springframework.core.io.Resource resource = fileResource.getResource();
+                InputStream resourceStream = resource.getInputStream();
+                cdsQuestionnaire = (Questionnaire) parser.parseResource(resourceStream);
+            } else {
+                // File is pulled from the file store as a file.
+                FileResource fileResource = fileStore.getFile("HomeOxygenTherapy", questionnaireId, "R4", false);
+                if(fileResource == null) {
+                    throw new RuntimeException("File resource pulled from the filestore is null.");
+                }
+                if(fileResource.getResource() == null) {
+                    throw new RuntimeException("File resource pulled from the filestore has a null getResource().");
+                }
+                cdsQuestionnaire = (Questionnaire) parser.parseResource(fileResource.getResource().getInputStream());
+            }
+
+            logger.info("--- Imported Questionnaire " + cdsQuestionnaire.getId());
+        } catch (DataFormatException e) {
+            e.printStackTrace();
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return cdsQuestionnaire;
+    }
+
     /**
      * Adds the given set of questions to the contained questionniare in the questionnaire response.
      * @param inputQuestionnaireResponse

From 6d5de01a030075b8803d8bb5a027250de44bfdcb Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Wed, 1 Dec 2021 17:03:15 -0500
Subject: [PATCH 20/30] Fixed an issue where duplicate questions would be sent
 with the final question in a branch.

---
 .../controllers/QuestionnaireController.java  | 27 +++++++++++++------
 .../endpoint/files/CommonFileStore.java       |  5 ----
 2 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index 7aad16276..e20d29396 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -25,6 +25,7 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.logging.Logger;
@@ -75,10 +76,8 @@ private class AdaptiveQuestionnaireTree {
          * @param inputQuestionnaire    The input questionnaire from the CDS-Library.
          */
         public AdaptiveQuestionnaireTree(Questionnaire inputQuestionnaire) {
-
-            // Top level parent question item, the first question page.
+            // Top level parent question item; the first set of questions.
             QuestionnaireItemComponent topLevelQuestion = inputQuestionnaire.getItemFirstRep();
-
             // Start the root building.
             this.root = new AdaptiveQuestionnaireNode(topLevelQuestion);
         }
@@ -92,6 +91,8 @@ public AdaptiveQuestionnaireTree(Questionnaire inputQuestionnaire) {
         public List<QuestionnaireItemComponent> getNextQuestionsForAnswers(List<QuestionnaireResponseItemComponent> allResponseItems, QuestionnaireResponse inputQuestionnaireResponse) {
             if(allResponseItems == null) {
                 throw new NullPointerException("Input answer items is null.");
+            } else if ((new HashSet(allResponseItems.stream().map(item -> item.getLinkId()).collect(Collectors.toList()))).size() != allResponseItems.size()){
+                throw new RuntimeException("Detected duplicate answers to the same question.");
             }
             return this.root.getNextQuestionForAnswers(allResponseItems, inputQuestionnaireResponse);
         }
@@ -173,8 +174,8 @@ public List<QuestionnaireItemComponent> getNextQuestionForAnswers(List<Questionn
                 List<QuestionnaireResponseItemComponent> currentQuestionResponses = allResponseItems.stream().filter(answerItem -> answerItem.getLinkId().equals(currentQuestionId)).collect(Collectors.toList());
                 if(currentQuestionResponses.size() != 1) {
                     // If there are no more answer items to check, we've reached the end of the recursion.
-                    return this.getQuestionSet();
                     // TODO - this could cause an unexpected end-of-questionnaire issue if incorrect responses are given.
+                    return this.getQuestionSet();
                 }
 
                 QuestionnaireResponseItemComponent currentQuestionResponse = currentQuestionResponses.get(0);
@@ -200,7 +201,7 @@ public List<QuestionnaireItemComponent> getNextQuestionForAnswers(List<Questionn
                 if(nextNode.isLeafNode()){
                     // Since the next node is a leaf node, set the questionnaire response status to complete.
                     inputQuestionnaireResponse.setStatus(QuestionnaireResponseStatus.COMPLETED);
-                    return this.getQuestionSet();
+                    return nextNode.getQuestionSet();
                 }
                 
                 // Has to be done this way without removing the previous answer response so that we don't alter the original list object.
@@ -239,6 +240,7 @@ public List<QuestionnaireItemComponent> getQuestionSet() {
                 List<QuestionnaireItemComponent> questionSet = new ArrayList<QuestionnaireItemComponent>();
                 questionSet.add(determinantQuestionNoChildren);
                 questionSet.addAll(this.supplementalQuestions);
+                logger.info("--- Question Set: " + questionSet.stream().map(item -> item.getLinkId()).collect(Collectors.toList()));
                 return questionSet;
             }
 
@@ -319,6 +321,12 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 }
             }
 
+            logger.info("--- Received questionnaire response: " + ctx.newJsonParser().encodeResourceToString(inputQuestionnaireResponse));
+            // Check that there are no duplicates in the recieved set of questions.
+            if ((new HashSet(((Questionnaire) inputQuestionnaireResponse.getContained().get(0)).getItem().stream().map(item -> item.getLinkId()).collect(Collectors.toList()))).size() != ((Questionnaire) inputQuestionnaireResponse.getContained().get(0)).getItem().size()){
+                throw new RuntimeException("Received a set of questions with duplicates.");
+            }
+
             String questionnaireId = ((Reference) inputQuestionnaireResponse.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/contained-id").getValue()).getReference();
             System.out.println("Input Questionnaire: " + questionnaireId);
 
@@ -343,13 +351,16 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
                 List<QuestionnaireItemComponent> nextQuestionSetResults = currentTree.getNextQuestionsForAnswers(allResponses, inputQuestionnaireResponse);
                 // Add the next set of questions to the response.
                 QuestionnaireController.addQuestionSetToQuestionnaireResponse(inputQuestionnaireResponse, nextQuestionSetResults);
+                // Check that there no duplicates in the set of questions.
+                if ((new HashSet(((Questionnaire) inputQuestionnaireResponse.getContained().get(0)).getItem().stream().map(item -> item.getLinkId()).collect(Collectors.toList()))).size() != ((Questionnaire) inputQuestionnaireResponse.getContained().get(0)).getItem().size()){
+                    throw new RuntimeException("Attempted to send a set of questions with duplicates. Question IDs are: " + (((Questionnaire) inputQuestionnaireResponse.getContained().get(0)).getItem().stream().map(item -> item.getLinkId()).collect(Collectors.toList())));
+                }
 
-                logger.info("--- Added next question set for questionnaire \'" + questionnaireId + "\' for response \'" + allResponses + "\'.");
-                logger.info("---- Get meta profile: " + inputQuestionnaireFromRequest.getMeta().getProfile().get(0).getValue());
-                logger.info("---- Sending response: " + inputQuestionnaireFromRequest.getId());
+                logger.info("--- Added next question set for questionnaire \'" + questionnaireId + "\' for responses \'" + allResponses + "\'.");
 
                 // Build and send the response.
                 String formattedResourceString = ctx.newJsonParser().encodeResourceToString(inputQuestionnaireResponse);
+                logger.info("--- Sending questionnaire response: " + formattedResourceString);
                 return ResponseEntity.status(HttpStatus.ACCEPTED).contentType(MediaType.APPLICATION_JSON)
                         .header(HttpHeaders.CONTENT_TYPE, "application/fhir+json" + "; charset=utf-8")
                         .body(formattedResourceString);
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java b/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
index 0f47acb0a..406b734b2 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
@@ -132,11 +132,6 @@ public FileResource getFhirResourceById(String fhirVersion, String resourceType,
     List<FhirResource> fhirResourceList = fhirResources.findById(criteria);
     FileResource resource = readFhirResourceFromFiles(fhirResourceList, fhirVersion, baseUrl);
     System.out.println("Resource Pulled: " + resource + "-" + resource.getFilename());
-    System.out.println("Fhir Resource List: " + fhirResourceList);
-    System.out.println("Criteria ID: " + criteria);
-    System.out.println("BaseUrl: " + baseUrl);
-    System.out.println("--------------------------");
-
 
     if ((resource != null) && fhirVersion.equalsIgnoreCase("r4")) {
 

From 9041344cff702f6d82c62164d0a2c9cc909f7e76 Mon Sep 17 00:00:00 2001
From: KeeyanGhoreshi <keeyan.ghoreshi@uconn.edu>
Date: Thu, 2 Dec 2021 13:45:05 -0500
Subject: [PATCH 21/30] Update gradle to newest version

---
 server/build.gradle | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/server/build.gradle b/server/build.gradle
index 72e0916d0..1a2c2c79b 100644
--- a/server/build.gradle
+++ b/server/build.gradle
@@ -38,7 +38,6 @@ dependencies {
 
     implementation("io.jsonwebtoken:jjwt:0.7.0")
 
-//    implementation 'javax.servlet:javax.servlet-api:3.1.0'
 
     implementation 'commons-beanutils:commons-beanutils:1.9.3'
 
@@ -60,7 +59,7 @@ dependencies {
         exclude group: 'org.slf4j', module: 'slf4j-log4j12'
     }
 
-    //Use locally implementationd cql libs (engine and fhir)
+    //Use locally compiled cql libs (engine and fhir)
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation group: 'info.cqframework', name: 'cql-to-elm', version: '1.5.1'
 

From 2aa86d108dc4ace3bb6f35f5fe9e8a7f377e84ad Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Fri, 10 Dec 2021 09:31:09 -0500
Subject: [PATCH 22/30] Cleaned up code by removing extranseous file, comments,
 updated question value type determination.

---
 .../controllers/QuestionnaireController.java  |   9 +-
 ...Questions-HomeOxygenTherapyAdditional.json | 177 ------------------
 .../files/SubQuestionnaireProcessor.java      |   7 -
 3 files changed, 2 insertions(+), 191 deletions(-)
 delete mode 100644 server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index e20d29396..d434c7666 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -1,6 +1,5 @@
 package org.hl7.davinci.endpoint.controllers;
 
-import org.aspectj.weaver.patterns.TypePatternQuestions.Question;
 import org.hl7.davinci.endpoint.Application;
 import org.hl7.davinci.endpoint.files.FileResource;
 import org.hl7.davinci.endpoint.files.FileStore;
@@ -181,14 +180,11 @@ public List<QuestionnaireItemComponent> getNextQuestionForAnswers(List<Questionn
                 QuestionnaireResponseItemComponent currentQuestionResponse = currentQuestionResponses.get(0);
                 QuestionnaireResponseItemAnswerComponent currentQuestionAnswer = currentQuestionResponse.getAnswerFirstRep();
 
-
                 // With the currrent question answer in hand, extract the next question.
-                Type value = currentQuestionAnswer.getValue();
                 String response;
-                // Not a fan of this, but not sure how else to determine the value based on different possible input types.
-                if(value instanceof StringType){
+                if (currentQuestionAnswer.hasValueStringType()) {
                     response = currentQuestionAnswer.getValueStringType().asStringValue();
-                } else if(value instanceof Coding){
+                } else if (currentQuestionAnswer.hasValueCoding()) {
                     response = currentQuestionAnswer.getValueCoding().getCode();
                 } else {
                     throw new RuntimeException("Answer does not match one of the possible input types.");
@@ -402,7 +398,6 @@ private static Questionnaire importCdsQuestionnaire(HttpServletRequest request,
                 }
                 cdsQuestionnaire = (Questionnaire) parser.parseResource(fileResource.getResource().getInputStream());
             }
-
             logger.info("--- Imported Questionnaire " + cdsQuestionnaire.getId());
         } catch (DataFormatException e) {
             e.printStackTrace();
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json b/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json
deleted file mode 100644
index a954b82b5..000000000
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/Questions-HomeOxygenTherapyAdditional.json
+++ /dev/null
@@ -1,177 +0,0 @@
-{
-    "resourceType": "Questionnaire",
-    "id": "HomeOxygenTherapyAdditional",
-    "name": "HomeOxygenTherapyAdditional",
-    "description": "Questions for Home Oxygen Therapy Order Reason",
-    "meta": {
-        "profile": [
-            "http://hl7.org/fhir/StructureDefinition/cqf-questionnaire",
-            "http://hl7.org/fhir/us/davinci-dtr/StructureDefinition/dtr-questionnaire-r4"
-        ]
-    },
-    "title": "Home Oxygen Therapy Additional Adaptive Form Questionnaire",
-    "status": "draft",
-    "subjectType": [
-        "Patient"
-    ],
-    "date": "2019-03-26",
-    "publisher": "Da Vinci DTR",
-    "item": [
-        {
-            "linkId": "1",
-            "text": "Order Reason",
-            "type": "choice",
-            "required": true,
-            "answerOption": [
-                {
-                    "modifierExtension": [
-                        {
-                            "url": "1.1"
-                        }
-                    ],
-                    "valueCoding": {
-                        "code": "Initial or original order for certification"
-                    }
-                },
-                {
-                    "modifierExtension": [
-                        {
-                            "url": "1.2"
-                        }
-                    ],
-                    "valueCoding": {
-                        "code": "Change in status"
-                    }
-                },
-                {
-                    "modifierExtension": [
-                        {
-                            "url": "1.3"
-                        }
-                    ],
-                    "valueCoding": {
-                        "code": "Revision or change in equipment"
-                    }
-                },
-                {
-                    "modifierExtension": [
-                        {
-                            "url": "1.4"
-                        }
-                    ],
-                    "valueCoding": {
-                        "code": "Replacement"
-                    }
-                }
-            ],
-            "item": [
-                {
-                    "linkId": "1.1",
-                    "type": "display",
-                    "text": "Submit the order form"
-                },
-
-                {
-                    "linkId": "1.2",
-                    "type": "display",
-                    "text": "Status Reasoning, Patient relocated, etc."
-                },
-                {
-                    "linkId": "1.3",
-                    "type": "display",
-                    "text": "Revision/Equipment change Reasoning, etc."
-                },
-                {
-                    "linkId": "1.4",
-                    "text": "Replacement Reason",
-                    "type": "choice",
-                    "required": true,
-                    "answerOption": [
-                        {
-                            "valueCoding": {
-                                "code": "Lost or stolen"
-                            },
-                            "modifierExtension": [
-                                {
-                                    "url": "1.4.1"
-                                }
-                            ]
-                        },
-                        {
-                            "valueCoding": {
-                                "code": "End of lifetime"
-                            },
-                            "modifierExtension": [
-                                {
-                                    "url": "1.4.2"
-                                }
-                            ]
-                        },
-                        {
-                            "valueCoding": {
-                                "code": "Repair exceeds 60% of cost"
-                            },
-                            "modifierExtension": [
-                                {
-                                    "url": "1.4.3"
-                                }
-                            ]
-                        }
-                    ],
-                    "item": [
-                        {
-                            "linkId": "1.4.1",
-                            "type": "display",
-                            "text": "Your prior auth number is A1234 (lost/stolen response)"
-                        },
-                        {
-                            "linkId": "1.4.3",
-                            "text": "Your prior auth number is B1234 (repair cost response). How much will it cost?",
-                            "type": "choice",
-                            "required": true,
-                            "answerOption": [
-                                {
-                                    "valueCoding": {
-                                        "code": "Greater than $500"
-                                    },
-                                    "modifierExtension": [
-                                        {
-                                            "url": "1.4.3.1"
-                                        }
-                                    ]
-                                },
-                                {
-                                    "valueCoding": {
-                                        "code": "Less than $500"
-                                    },
-                                    "modifierExtension": [
-                                        {
-                                            "url": "1.4.3.2"
-                                        }
-                                    ]
-                                }
-                            ],
-                            "item": [
-                                {
-                                    "linkId": "1.4.3.1",
-                                    "type": "display",
-                                    "text": "Your response idicated that it will cost more than $500."
-                                },
-                                {
-                                    "linkId": "1.4.3.2",
-                                    "type": "display",
-                                    "text": "Your response indicated that it will cost less than $500."
-                                }
-                            ]
-                        },
-                        {
-                            "linkId": "1.4.2",
-                            "type": "display",
-                            "text": "Your prior auth number is B1234 (EOL response)"
-                        }
-                    ]
-                }
-            ]
-        }
-    ]
-}
\ No newline at end of file
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/files/SubQuestionnaireProcessor.java b/server/src/main/java/org/hl7/davinci/endpoint/files/SubQuestionnaireProcessor.java
index 953509983..a5b9adbb6 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/files/SubQuestionnaireProcessor.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/files/SubQuestionnaireProcessor.java
@@ -8,12 +8,9 @@
 import org.hl7.fhir.r4.model.CanonicalType;
 import org.hl7.fhir.r4.model.Extension;
 import org.hl7.fhir.r4.model.Questionnaire;
-import org.hl7.fhir.r4.model.Reference;
-import org.hl7.fhir.r4.model.ValueSet;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.hl7.fhir.r4.model.Resource;
 
 /**
  * Processes FHIR R4 questionnaire to assemble sub-questionnaires into a complete questionnaire.
@@ -76,10 +73,6 @@ private void processItemList(List<QuestionnaireItemComponent> itemList, FileStor
     for (int i = 0; i < itemList.size();) {
       List<QuestionnaireItemComponent> returnedItemList = 
         processItem(itemList.get(i), fileStore, baseUrl, containedList, extensionList);
-
-      // if(itemList.get(i).getItem().size() > 0){
-      //   this.processItemList(itemList.get(i).getItem(), fileStore, baseUrl, containedList, extensionList);
-      // }
       
       if (returnedItemList.size() == 0) {
         continue;

From c0301371284ef912bb36a40d9b61cedcc8cc7729 Mon Sep 17 00:00:00 2001
From: Robi Scalfani <rscalfani@mitre.org>
Date: Fri, 10 Dec 2021 11:32:35 -0500
Subject: [PATCH 23/30] Removed hard-coded home oxygen file names

---
 .../controllers/QuestionnaireController.java  | 41 +++++++------------
 1 file changed, 14 insertions(+), 27 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
index d434c7666..ea583a748 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/QuestionnaireController.java
@@ -17,11 +17,9 @@
 
 import ca.uhn.fhir.parser.DataFormatException;
 import ca.uhn.fhir.parser.IParser;
-import org.hl7.davinci.endpoint.Utils;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -32,13 +30,10 @@
 
 import javax.servlet.http.HttpServletRequest;
 
-import org.hl7.fhir.r4.model.Coding;
 import org.hl7.fhir.r4.model.Questionnaire;
 import org.hl7.fhir.r4.model.QuestionnaireResponse;
 import org.hl7.fhir.r4.model.Resource;
 import org.hl7.fhir.r4.model.ResourceType;
-import org.hl7.fhir.r4.model.StringType;
-import org.hl7.fhir.r4.model.Type;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemAnswerOptionComponent;
 import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent;
 import org.hl7.fhir.r4.model.QuestionnaireResponse.QuestionnaireResponseItemAnswerComponent;
@@ -328,10 +323,10 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
 
             if (inputQuestionnaireFromRequest != null) {
 
-                if(!questionnaireTrees.containsKey(questionnaireId)){
+                if (!questionnaireTrees.containsKey(questionnaireId)) {
                     // If there is not already a tree that matches the requested questionnaire id, build it.
                     // Import the requested CDS-Library Questionnaire.
-                    Questionnaire cdsQuestionnaire = QuestionnaireController.importCdsQuestionnaire(request, parser, fileStore, "Questions-HomeOxygenTherapyAdditionalAdaptive.json");
+                    Questionnaire cdsQuestionnaire = QuestionnaireController.importCdsAdaptiveQuestionnaire(request, parser, fileStore, questionnaireId);
                     
                     // Build the tree.
                     AdaptiveQuestionnaireTree newTree = new AdaptiveQuestionnaireTree(cdsQuestionnaire);
@@ -375,29 +370,21 @@ private ResponseEntity<String> getNextQuestionOperation(String body, HttpServlet
      * @param request
      * @return
      */
-    private static Questionnaire importCdsQuestionnaire(HttpServletRequest request, IParser parser, FileStore fileStore, String questionnaireId) {
+    private static Questionnaire importCdsAdaptiveQuestionnaire(HttpServletRequest request, IParser parser, FileStore fileStore, String questionnaireId) {
         Questionnaire cdsQuestionnaire = null;
         try {
-            //TODO: need to determine topic, filename, and fhir version without having them hard coded
-            boolean pullFromResources = false;
-            if(pullFromResources){
-                // Resource is pulled from the file store as a resource.
-                String baseUrl = Utils.getApplicationBaseUrl(request).toString() + "/";
-                FileResource fileResource = fileStore.getFhirResourceById("r4", "questionnaire", questionnaireId, baseUrl);
-                org.springframework.core.io.Resource resource = fileResource.getResource();
-                InputStream resourceStream = resource.getInputStream();
-                cdsQuestionnaire = (Questionnaire) parser.parseResource(resourceStream);
-            } else {
-                // File is pulled from the file store as a file.
-                FileResource fileResource = fileStore.getFile("HomeOxygenTherapy", questionnaireId, "R4", false);
-                if(fileResource == null) {
-                    throw new RuntimeException("File resource pulled from the filestore is null.");
-                }
-                if(fileResource.getResource() == null) {
-                    throw new RuntimeException("File resource pulled from the filestore has a null getResource().");
-                }
-                cdsQuestionnaire = (Questionnaire) parser.parseResource(fileResource.getResource().getInputStream());
+            String adaptiveQuestionniareFile = ("Questions-" + questionnaireId + "Adaptive.json").replace("#", ""); // The filename should be the questionnaire ID with these added values.
+            String topic = questionnaireId.replace("Additional", "").replace("#", ""); // The topic should be the questionnaire ID but without the 'Additional' tag.
+            // File is pulled from the file store as a file.
+            logger.info("--- Importing questionniare file: " + adaptiveQuestionniareFile + " from topic: " + topic);
+            FileResource fileResource = fileStore.getFile(topic, adaptiveQuestionniareFile, "R4", false);
+            if(fileResource == null) {
+                throw new RuntimeException("File resource pulled from the filestore is null.");
+            }
+            if(fileResource.getResource() == null) {
+                throw new RuntimeException("File resource pulled from the filestore has a null getResource().");
             }
+            cdsQuestionnaire = (Questionnaire) parser.parseResource(fileResource.getResource().getInputStream());
             logger.info("--- Imported Questionnaire " + cdsQuestionnaire.getId());
         } catch (DataFormatException e) {
             e.printStackTrace();

From 9ccbfb26be98d746a2f8329c95589cdf78ef8bdf Mon Sep 17 00:00:00 2001
From: Patrick LaRocque <41444457+plarocque4@users.noreply.github.com>
Date: Wed, 22 Dec 2021 16:38:10 -0500
Subject: [PATCH 24/30] Preemptive prior auth (#255)

* Support for preemptive prior authorization.

* Add missing items to Card, Suggestion, and action including the selectionBehavior for handling multiple suggestions, the UUIDs and the resourceId in the Action.

* Missed a place where the selectionBehavior needed to be set.
---
 .../src/main/java/org/cdshooks/Action.java    |  14 +-
 .../java/org/cdshooks/ActionSerializer.java   |   2 +
 .../src/main/java/org/cdshooks/Card.java      |  48 ++++++-
 .../org/cdshooks/CoverageRequirements.java    |  25 ++++
 .../main/java/org/cdshooks/Suggestion.java    |  12 +-
 .../java/org/hl7/davinci/r4/Utilities.java    | 122 +++++++++++++---
 .../cdshooks/services/crd/CdsService.java     |  18 ++-
 .../services/crd/r4/FhirRequestProcessor.java |  41 ++++++
 .../services/crd/r4/OrderSignService.java     |  12 ++
 .../endpoint/components/CardBuilder.java      | 134 ++++++++++++++----
 .../database/FhirResourceCriteria.java        |  60 ++++++--
 .../database/FhirResourceRepository.java      |   6 +-
 .../endpoint/files/CommonFileStore.java       |  19 +--
 .../CoverageRequirementRuleCriteria.java      |  11 +-
 14 files changed, 443 insertions(+), 81 deletions(-)

diff --git a/resources/src/main/java/org/cdshooks/Action.java b/resources/src/main/java/org/cdshooks/Action.java
index 84cf4b44e..edac2a4ef 100755
--- a/resources/src/main/java/org/cdshooks/Action.java
+++ b/resources/src/main/java/org/cdshooks/Action.java
@@ -15,6 +15,7 @@ public Action(FhirComponentsT fhirComponents) {
   private TypeEnum type = null;
   private String description = null;
   private IBaseResource resource = null;
+  private String resourceId = null;
 
   private FhirComponentsT fhirComponents;
 
@@ -26,9 +27,7 @@ public void setType(TypeEnum type) {
     this.type = type;
   }
 
-  public String getDescription() {
-    return description;
-  }
+  public String getDescription() { return description; }
 
   public void setDescription(String description) {
     this.description = description;
@@ -36,7 +35,14 @@ public void setDescription(String description) {
 
   public IBaseResource getResource() { return resource; }
 
-  public void setResource(IBaseResource resource) { this.resource = resource; }
+  public void setResource(IBaseResource resource) {
+    this.resource = resource;
+    setResourceId(resource.fhirType() + "/" + resource.getIdElement().getIdPart());
+  }
+
+  public String getResourceId() { return resourceId; }
+
+  public void setResourceId(String resourceId) { this.resourceId = resourceId; }
 
   public FhirComponentsT getFhirComponents() {
     return this.fhirComponents;
diff --git a/resources/src/main/java/org/cdshooks/ActionSerializer.java b/resources/src/main/java/org/cdshooks/ActionSerializer.java
index c22dc6b03..4b2a4448d 100644
--- a/resources/src/main/java/org/cdshooks/ActionSerializer.java
+++ b/resources/src/main/java/org/cdshooks/ActionSerializer.java
@@ -38,6 +38,8 @@ public void serialize(
         new ObjectMapper().readValue(jsonStrNew, HashMap.class);
     jgen.writeObjectField("resource", map);
 
+    jgen.writeStringField("resourceId", value.getResourceId());
+
     jgen.writeEndObject();
   }
 }
diff --git a/resources/src/main/java/org/cdshooks/Card.java b/resources/src/main/java/org/cdshooks/Card.java
index 73d8d2358..a4b125703 100755
--- a/resources/src/main/java/org/cdshooks/Card.java
+++ b/resources/src/main/java/org/cdshooks/Card.java
@@ -5,16 +5,24 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
 
 public class Card {
+  private String uuid = null;
   private String summary = null;
-
   private String detail = null;
   private IndicatorEnum indicator = null;
   private Source source = null;
   private List<Suggestion> suggestions = null;
+  private SelectionBehaviorEnum selectionBehavior = null;
   private List<Link> links = null;
 
+  public Card() { this.uuid = UUID.randomUUID().toString(); }
+
+  public String getUuid() { return uuid; }
+
+  private void setUuid(String uuid) { this.uuid = uuid; }
+
   public String getSummary() {
     return summary;
   }
@@ -68,6 +76,10 @@ public void setSuggestions(List<Suggestion> suggestions) {
     this.suggestions = suggestions;
   }
 
+  public SelectionBehaviorEnum getSelectionBehavior() { return selectionBehavior; }
+
+  public void setSelectionBehavior(SelectionBehaviorEnum selectionBehavior) { this.selectionBehavior = selectionBehavior; }
+
   /**
    * Add a link.
    * @param linksItem The link.
@@ -129,4 +141,38 @@ public String toString() {
       return String.valueOf(value);
     }
   }
+
+  public enum SelectionBehaviorEnum {
+    ANY("any"),
+    AT_MOST_ONE("at-most-one");
+
+    private String value;
+
+    SelectionBehaviorEnum(String value) { this.value = value; }
+
+    /**
+     * Create the enum value from a string. Needed because the values have illegal java chars.
+     * @param value One of the enum values.
+     * @return selectionBehaviorEnum
+     */
+    @JsonCreator
+    public static SelectionBehaviorEnum fromValue(String value) throws IOException {
+      for (SelectionBehaviorEnum selectionBehaviorEnum : SelectionBehaviorEnum.values()) {
+        if (selectionBehaviorEnum.toString().equals(value)) {
+          return selectionBehaviorEnum;
+        }
+      }
+      return null;
+    }
+
+    @JsonValue
+    public String getValue() {
+      return value;
+    }
+
+    @Override
+    public String toString() {
+      return String.valueOf(value);
+    }
+  }
 }
diff --git a/resources/src/main/java/org/cdshooks/CoverageRequirements.java b/resources/src/main/java/org/cdshooks/CoverageRequirements.java
index 135e3b5b6..cea3e3908 100644
--- a/resources/src/main/java/org/cdshooks/CoverageRequirements.java
+++ b/resources/src/main/java/org/cdshooks/CoverageRequirements.java
@@ -1,5 +1,7 @@
 package org.cdshooks;
 
+import java.util.UUID;
+
 public class CoverageRequirements {
   private boolean applies;
   private String summary;
@@ -17,6 +19,9 @@ public class CoverageRequirements {
   private boolean priorAuthRequired;
   private boolean documentationRequired;
 
+  private boolean priorAuthApproved;
+  private String priorAuthId;
+
   public boolean getApplies() { return applies; }
 
   public CoverageRequirements setApplies(boolean applies) {
@@ -139,6 +144,26 @@ public CoverageRequirements setDocumentationRequired(boolean documentationRequir
     return this;
   }
 
+  public boolean isPriorAuthApproved() { return priorAuthApproved; }
+
+  public CoverageRequirements setPriorAuthApproved(boolean priorAuthApproved) {
+    this.priorAuthApproved = priorAuthApproved;
+    return this;
+  }
+
+  public String getPriorAuthId() { return priorAuthId; }
+
+  public CoverageRequirements setPriorAuthId(String priorAuthId) {
+    this.priorAuthId = priorAuthId;
+    return this;
+  }
+
+  public CoverageRequirements generatePriorAuthId() {
+    // Generate a random UUID as the ID. This is the same method that Prior Auth (PAS) uses.
+    this.priorAuthId = UUID.randomUUID().toString();
+    return this;
+  }
+
   public String getQuestionnaireAdditionalUri() {
     return questionnaireAdditionalUri;
   }
diff --git a/resources/src/main/java/org/cdshooks/Suggestion.java b/resources/src/main/java/org/cdshooks/Suggestion.java
index e1c88aca1..0eb4e6d87 100755
--- a/resources/src/main/java/org/cdshooks/Suggestion.java
+++ b/resources/src/main/java/org/cdshooks/Suggestion.java
@@ -7,11 +7,13 @@
 public class Suggestion {
   private String label = null;
 
-  private UUID uuid = null;
+  private String uuid = null;
 
   private List<Action> actions = null;
 
-  private boolean isRecommended = false;
+  private boolean isRecommended = true;
+
+  public Suggestion() { this.uuid = UUID.randomUUID().toString(); }
 
   public String getLabel() {
     return label;
@@ -21,11 +23,9 @@ public void setLabel(String label) {
     this.label = label;
   }
 
-  public UUID getUuid() {
-    return uuid;
-  }
+  public String getUuid() { return uuid; }
 
-  public void setUuid(UUID uuid) {
+  public void setUuid(String uuid) {
     this.uuid = uuid;
   }
 
diff --git a/resources/src/main/java/org/hl7/davinci/r4/Utilities.java b/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
index 6cfd033b4..e1821bc71 100644
--- a/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
+++ b/resources/src/main/java/org/hl7/davinci/r4/Utilities.java
@@ -1,35 +1,16 @@
 package org.hl7.davinci.r4;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
 
 import ca.uhn.fhir.context.FhirContext;
 import ca.uhn.fhir.parser.IParser;
-import org.hl7.davinci.FhirResourceInfo;
-import org.hl7.davinci.PatientInfo;
-import org.hl7.davinci.PractitionerRoleInfo;
-import org.hl7.davinci.RequestIncompleteException;
-import org.hl7.davinci.SharedUtilities;
-import org.hl7.davinci.SuppressParserErrorHandler;
+import org.hl7.davinci.*;
 import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.r4.model.Address;
+import org.hl7.fhir.r4.model.*;
 import org.hl7.fhir.r4.model.Address.AddressType;
 import org.hl7.fhir.r4.model.Address.AddressUse;
-import org.hl7.fhir.r4.model.Bundle;
 import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
-import org.hl7.fhir.r4.model.Coverage;
-import org.hl7.fhir.r4.model.DomainResource;
-import org.hl7.fhir.r4.model.Library;
-import org.hl7.fhir.r4.model.Location;
-import org.hl7.fhir.r4.model.Organization;
-import org.hl7.fhir.r4.model.Patient;
-import org.hl7.fhir.r4.model.PractitionerRole;
-import org.hl7.fhir.r4.model.Questionnaire;
-import org.hl7.fhir.r4.model.QuestionnaireResponse;
-import org.hl7.fhir.r4.model.Reference;
-import org.hl7.fhir.r4.model.Resource;
-import org.hl7.fhir.r4.model.ValueSet;
+import org.hl7.fhir.r4.model.ClaimResponse.AdjudicationComponent;
 
 public class Utilities {
   /**
@@ -248,4 +229,99 @@ public static FhirResourceInfo getFhirResourceInfo(String resourceString) {
     return getFhirResourceInfo(parseFhirData(resourceString));
   }
 
+  public static Reference convertIdToReference(String id, String fhirType) {
+    Reference reference = new Reference();
+    if (id.toUpperCase().startsWith(fhirType.toUpperCase() + "/")) {
+      reference.setReference(id);
+    } else {
+      reference.setReference(fhirType + "/" + id);
+    }
+    return reference;
+  }
+
+  public static ClaimResponse createClaimResponse(String priorAuthId, String patientId, String payerId, String providerId, String applicationFhirPath) {
+    Date now = new Date();
+
+    ClaimResponse claimResponse = new ClaimResponse();
+
+    claimResponse.setId(priorAuthId);
+
+    Meta meta = new Meta();
+    meta.addProfile("http://hl7.org/fhir/us/davinci-pas/StructureDefinition/profile-claimresponse");
+    claimResponse.setMeta(meta);
+
+    claimResponse.addIdentifier(new Identifier().setSystem(applicationFhirPath).setValue(claimResponse.getId()));
+
+    claimResponse.setStatus(ClaimResponse.ClaimResponseStatus.ACTIVE);
+
+    CodeableConcept codeableConcept = new CodeableConcept();
+    Coding coding = new Coding();
+    coding.setSystem("http://terminology.hl7.org/CodeSystem/claim-type");
+    coding.setCode("professional");
+    coding.setDisplay("Professional");
+    codeableConcept.addCoding(coding);
+    claimResponse.setType(codeableConcept);
+
+    claimResponse.setUse(ClaimResponse.Use.PREAUTHORIZATION);
+
+    claimResponse.setPatient(convertIdToReference(patientId, "Patient"));
+
+    claimResponse.setCreated(now);
+
+    claimResponse.setInsurer(convertIdToReference(payerId, "Organization"));
+
+    claimResponse.setOutcome(ClaimResponse.RemittanceOutcome.COMPLETE);
+
+    claimResponse.setDisposition("Granted");
+
+    claimResponse.setPreAuthRef(claimResponse.getId());
+
+    ClaimResponse.ItemComponent itemComponent = new ClaimResponse.ItemComponent();
+
+    Extension reviewActionExtension = new Extension();
+    reviewActionExtension.setUrl("http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-reviewAction");
+    reviewActionExtension.addExtension(
+        new Extension()
+            .setUrl("http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-reviewActionCode")
+            .setValue(new CodeableConcept().addCoding(
+                new Coding()
+                    .setSystem("https://valueset.x12.org/x217/005010/response/2000F/HCR/1/01/00/306")
+                    .setCode("A1"))));
+    reviewActionExtension.addExtension(
+        new Extension()
+            .setUrl("number")
+            .setValue(new StringType(UUID.randomUUID().toString())));
+    itemComponent.addExtension(reviewActionExtension);
+
+    itemComponent.addExtension(
+        new Extension()
+            .setUrl("http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-itemPreAuthIssueDate")
+            .setValue(new DateType().setValue(now)));
+
+    itemComponent.addExtension(
+        new Extension()
+            .setUrl("http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-authorizationNumber")
+            .setValue(new StringType(UUID.randomUUID().toString())));
+
+    Extension providerExtension = new Extension();
+    providerExtension.setUrl("http://hl7.org/fhir/us/davinci-pas/StructureDefinition/extension-itemAuthorizedProvider");
+    providerExtension.addExtension(
+        new Extension()
+            .setUrl("provider")
+            .setValue(new Reference().setReference(convertIdToReference(providerId, "Practitioner").getReference())));
+    itemComponent.addExtension(providerExtension);
+
+    itemComponent.setItemSequence(1);
+
+    itemComponent.addAdjudication(
+        new AdjudicationComponent().setCategory(
+            new CodeableConcept().addCoding(
+                new Coding().setSystem("http://terminology.hl7.org/CodeSystem/adjudication")
+                    .setCode("submitted"))));
+
+    claimResponse.addItem(itemComponent);
+
+    return claimResponse;
+  }
+
 }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
index 3c4051ba4..b5664cac2 100755
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
@@ -20,6 +20,7 @@
 import org.hl7.davinci.endpoint.components.CardBuilder;
 import org.hl7.davinci.endpoint.components.CardBuilder.CqlResultsForCard;
 import org.hl7.davinci.endpoint.components.PrefetchHydrator;
+import org.hl7.davinci.endpoint.database.FhirResourceRepository;
 import org.hl7.davinci.endpoint.database.RequestLog;
 import org.hl7.davinci.endpoint.database.RequestService;
 import org.hl7.davinci.endpoint.files.FileStore;
@@ -76,6 +77,9 @@ public abstract class CdsService<requestTypeT extends CdsRequest<?, ?>> {
   @Autowired
   FileStore fileStore;
 
+  @Autowired
+  private FhirResourceRepository fhirResourceRepository;
+
   private final List<PrefetchTemplateElement> prefetchElements;
 
   protected FhirComponentsT fhirComponents;
@@ -188,7 +192,14 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
 
         if (results.getCoverageRequirements().getApplies()) {
 
-          if (coverageRequirements.isDocumentationRequired() || coverageRequirements.isPriorAuthRequired()) {
+          // if prior auth already approved
+          if (coverageRequirements.isPriorAuthApproved()) {
+            response.addCard(CardBuilder.priorAuthCard(results, results.getRequest(), fhirComponents, coverageRequirements.getPriorAuthId(),
+                request.getContext().getPatientId(), lookupResult.getCriteria().getPayorId(), request.getContext().getUserId(),
+                applicationBaseUrl.toString() + "/fhir/" + fhirComponents.getFhirVersion().toString(),
+                fhirResourceRepository));
+
+          } else if (coverageRequirements.isDocumentationRequired() || coverageRequirements.isPriorAuthRequired()) {
             if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireOrderUri())
                 || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireFaceToFaceUri())
                 || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireLabUri())
@@ -217,7 +228,10 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
             // no prior auth or documentation required
             logger.info("Add the no doc or prior auth required card");
             Card card = CardBuilder.transform(results);
-            card = CardBuilder.createSuggestionsWithNote(card, results, fhirComponents);
+            card.addSuggestionsItem(CardBuilder.createSuggestionWithNote(card, results.getRequest(), fhirComponents,
+                "Save Update To EHR", "Update original " + results.getRequest().fhirType() + " to add note",
+                true));
+            card.setSelectionBehavior(Card.SelectionBehaviorEnum.ANY);
             response.addCard(card);
           }
         }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/FhirRequestProcessor.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/FhirRequestProcessor.java
index 3c92f68b3..912b97d8d 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/FhirRequestProcessor.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/FhirRequestProcessor.java
@@ -1,6 +1,7 @@
 package org.hl7.davinci.endpoint.cdshooks.services.crd.r4;
 
 import org.cdshooks.AlternativeTherapy;
+import org.hl7.fhir.instance.model.api.IBase;
 import org.hl7.fhir.instance.model.api.IBaseResource;
 import org.hl7.fhir.r4.model.*;
 import org.slf4j.Logger;
@@ -123,4 +124,44 @@ public static IBaseResource addNoteToRequest(IBaseResource request, Annotation n
 
     return output;
   }
+
+  public static IBaseResource addSupportingInfoToRequest(IBaseResource request, Reference reference) {
+    IBaseResource output = request;
+
+    switch (request.fhirType()) {
+      case "DeviceRequest":
+        DeviceRequest deviceRequest = ((DeviceRequest) request).copy();
+        deviceRequest.addSupportingInfo(reference);
+        output = deviceRequest;
+        break;
+      case "MedicationRequest":
+        MedicationRequest medicationRequest = ((MedicationRequest) request).copy();
+        medicationRequest.addSupportingInformation(reference);
+        output = medicationRequest;
+        break;
+      case "MedicationDispense":
+        MedicationDispense medicationDispense = ((MedicationDispense) request).copy();
+        medicationDispense.addSupportingInformation(reference);
+        output = medicationDispense;
+        break;
+      case "ServiceRequest":
+        ServiceRequest serviceRequest = ((ServiceRequest) request).copy();
+        serviceRequest.addSupportingInfo(reference);
+        output = serviceRequest;
+        break;
+      case "Appointment":
+        Appointment appointment = ((Appointment) request).copy();
+        appointment.addSupportingInformation(reference);
+        output = appointment;
+        break;
+      case "NutritionOrder":
+      case "SupplyRequest":
+      case "Encounter":
+      default:
+        logger.info("Unsupported fhir R4 resource type (" + request.fhirType() + ") when adding note");
+        throw new RuntimeException("Unsupported fhir R4 resource type " + request.fhirType());
+    }
+
+    return output;
+  }
 }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSignService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSignService.java
index df46cdcd4..8b9c7d2ba 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSignService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSignService.java
@@ -93,6 +93,18 @@ protected CqlResultsForCard executeCqlAndGetRelevantResults(Context context, Str
       logger.info("Prior Auth Required");
       coverageRequirements.setSummary(humanReadableTopic + ": Prior Authorization required.")
           .setDetails("Prior Authorization required, follow the attached link for information.");
+
+      // check if prior auth is automatically approved
+      if (evaluateStatement("APPROVE_PRIORAUTH", context) != null) {
+        coverageRequirements.setPriorAuthApproved((Boolean) evaluateStatement("APPROVE_PRIORAUTH", context));
+        if (coverageRequirements.isPriorAuthApproved()) {
+          coverageRequirements.generatePriorAuthId();
+          logger.info("Prior Auth Approved: " + coverageRequirements.getPriorAuthId());
+          coverageRequirements.setSummary(humanReadableTopic + ": Prior Authorization approved.")
+              .setDetails("Prior Authorization approved, ID is " + coverageRequirements.getPriorAuthId());
+        }
+      }
+
     } else if (coverageRequirements.isDocumentationRequired()) {
       logger.info("Documentation Required");
       coverageRequirements.setSummary(humanReadableTopic + ": Documentation Required.")
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java b/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
index 92a5633aa..e67ff6f42 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/components/CardBuilder.java
@@ -1,18 +1,16 @@
 package org.hl7.davinci.endpoint.components;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 import org.cdshooks.*;
 import org.hl7.davinci.FhirComponentsT;
 import org.hl7.davinci.endpoint.cdshooks.services.crd.r4.FhirRequestProcessor;
+import org.hl7.davinci.endpoint.database.FhirResource;
+import org.hl7.davinci.endpoint.database.FhirResourceRepository;
+import org.hl7.davinci.r4.Utilities;
 import org.hl7.fhir.instance.model.api.IBase;
 import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.r4.model.Annotation;
-import org.hl7.fhir.r4.model.DeviceRequest;
-import org.hl7.fhir.r4.model.StringType;
+import org.hl7.fhir.r4.model.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -82,21 +80,33 @@ public CqlResultsForCard setRequest(IBaseResource request) {
    * @param cqlResults
    * @return card with appropriate information
    */
-  public static Card transform(CqlResultsForCard cqlResults) {
+  public static Card transform(CqlResultsForCard cqlResults, Boolean addLink) {
     Card card = baseCard();
 
-    Link link = new Link();
-    link.setUrl(cqlResults.getCoverageRequirements().getInfoLink());
-    link.setType("absolute");
-    link.setLabel("Documentation Requirements");
+    if (addLink) {
+      Link link = new Link();
+      link.setUrl(cqlResults.getCoverageRequirements().getInfoLink());
+      link.setType("absolute");
+      link.setLabel("Documentation Requirements");
+      card.setLinks(Arrays.asList(link));
+    }
 
-    card.setLinks(Arrays.asList(link));
     card.setSummary(cqlResults.getCoverageRequirements().getSummary());
     card.setDetail(cqlResults.getCoverageRequirements().getDetails());
 
     return card;
   }
 
+  /**
+   * Transforms a result from the database into a card, defaults to adding the link.
+   *
+   * @param cqlResults
+   * @return card with appropriate information
+   */
+  public static Card transform(CqlResultsForCard cqlResults) {
+    return transform(cqlResults, true);
+  }
+
   /**
    * Transforms a result from the database into a card.
    *
@@ -180,6 +190,8 @@ public static Card alternativeTherapyCard(AlternativeTherapy alternativeTherapy,
     suggestionList.add(alternativeTherapySuggestion);
     card.setSuggestions(suggestionList);
 
+    card.setSelectionBehavior(Card.SelectionBehaviorEnum.ANY);
+
     return card;
   }
 
@@ -192,14 +204,90 @@ public static Card drugInteractionCard(DrugInteraction drugInteraction) {
     return card;
   }
 
-  public static Card createSuggestionsWithNote(Card card,
-                                               CqlResultsForCard cqlResults,
-                                               FhirComponentsT fhirComponents) {
-    List<Suggestion> suggestionList = new ArrayList<>();
+  public static Card priorAuthCard(CqlResultsForCard cqlResults,
+                                   IBaseResource request,
+                                   FhirComponentsT fhirComponents,
+                                   String priorAuthId,
+                                   String patientId,
+                                   String payerId,
+                                   String providerId,
+                                   String applicationFhirPath,
+                                   FhirResourceRepository fhirResourceRepository) {
+    logger.info("Build Prior Auth Card");
+
+    Card card = transform(cqlResults, false);
+
+    // create the ClaimResponse
+    ClaimResponse claimResponse = Utilities.createClaimResponse(priorAuthId, patientId, payerId, providerId, applicationFhirPath);
+
+    // build the FhirResource and save to the database
+    FhirResource fhirResource = new FhirResource();
+    fhirResource.setFhirVersion(fhirComponents.getFhirVersion().toString())
+        .setResourceType(claimResponse.fhirType())
+        .setData(fhirComponents.getJsonParser().encodeResourceToString(claimResponse));
+    fhirResource.setId(claimResponse.getId());
+    fhirResource.setName(claimResponse.getId());
+    FhirResource newFhirResource = fhirResourceRepository.save(fhirResource);
+    logger.info("stored: " + newFhirResource.getFhirVersion() + "/" + newFhirResource.getResourceType() + "/" + newFhirResource.getId());
+
+    // create the reference to the ClaimResponse
+    Reference claimResponseReference = new Reference();
+    claimResponseReference.setReference("ClaimResponse/" + claimResponse.getId());
+
+    // add SupportingInfo to the Request
+    IBaseResource outputRequest = FhirRequestProcessor.addSupportingInfoToRequest(request, claimResponseReference);
+
+    // add suggestion with ClaimResponse
+    Suggestion suggestionWithClaimResponse = createSuggestionWithResource(outputRequest, claimResponse, fhirComponents,
+        "Store the prior authorization in the EHR");
+    card.addSuggestionsItem(suggestionWithClaimResponse);
+
+    // add suggestion with annotation
+    Suggestion suggestionWithAnnotation = createSuggestionWithNote(card, outputRequest, fhirComponents,
+        "Store prior authorization as an annotation to the order", "Add authorization to record",
+        false);
+    card.addSuggestionsItem(suggestionWithAnnotation);
+
+    card.setSelectionBehavior(Card.SelectionBehaviorEnum.AT_MOST_ONE);
+
+    return card;
+  }
+
+  public static Suggestion createSuggestionWithResource(IBaseResource request,
+                                                        IBaseResource resource,
+                                                        FhirComponentsT fhirComponents,
+                                                        String label) {
+    Suggestion suggestion = new Suggestion();
+
+    suggestion.setLabel(label);
+    suggestion.setIsRecommended(true);
+
+    // build the create Action
+    Action createAction = new Action(fhirComponents);
+    createAction.setType(Action.TypeEnum.create);
+    createAction.setDescription("Store " + resource.fhirType());
+    createAction.setResource(resource);
+    suggestion.addActionsItem(createAction);
+
+    // build the update Action
+    Action updateAction = new Action(fhirComponents);
+    updateAction.setType(Action.TypeEnum.update);
+    updateAction.setDescription("Update to the resource " + request.fhirType());
+    updateAction.setResource(request);
+    suggestion.addActionsItem(updateAction);
+
+    return suggestion;
+  }
+  public static Suggestion createSuggestionWithNote(Card card,
+                                                    IBaseResource request,
+                                                    FhirComponentsT fhirComponents,
+                                                    String label,
+                                                    String description,
+                                                    boolean isRecommended) {
     Suggestion requestWithNoteSuggestion = new Suggestion();
 
-    requestWithNoteSuggestion.setLabel("Save Update To EHR");
-    requestWithNoteSuggestion.setIsRecommended(true);
+    requestWithNoteSuggestion.setLabel(label);
+    requestWithNoteSuggestion.setIsRecommended(isRecommended);
     List<Action> actionList = new ArrayList<>();
 
     // build the Annotation
@@ -210,19 +298,17 @@ public static Card createSuggestionsWithNote(Card card,
     String text = card.getSummary() + ": " + card.getDetail();
     annotation.setText(text);
     annotation.setTime(new Date()); // set the date and time to now
-    IBaseResource resource = FhirRequestProcessor.addNoteToRequest(cqlResults.getRequest(), annotation);
+    IBaseResource resource = FhirRequestProcessor.addNoteToRequest(request, annotation);
 
     Action updateAction = new Action(fhirComponents);
     updateAction.setType(Action.TypeEnum.update);
-    updateAction.setDescription("Update original " + cqlResults.getRequest().fhirType() + " to add note");
+    updateAction.setDescription(description);
     updateAction.setResource(resource);
 
     actionList.add(updateAction);
 
     requestWithNoteSuggestion.setActions(actionList);
-    suggestionList.add(requestWithNoteSuggestion);
-    card.setSuggestions(suggestionList);
-    return card;
+    return requestWithNoteSuggestion;
   }
 
   /**
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/database/FhirResourceCriteria.java b/server/src/main/java/org/hl7/davinci/endpoint/database/FhirResourceCriteria.java
index ce1ccd6fa..08f8e50af 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/database/FhirResourceCriteria.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/database/FhirResourceCriteria.java
@@ -2,11 +2,11 @@
 
 public class FhirResourceCriteria {
 
-  private String fhirVersion;
-  private String resourceType;
-  private String name;
-  private String id;
-  private String url;
+  private String fhirVersion = null;
+  private String resourceType = null;
+  private String name = null;
+  private String id = null;
+  private String url = null;
 
   public String getFhirVersion() { return fhirVersion; }
 
@@ -44,8 +44,52 @@ public FhirResourceCriteria setUrl(String url) {
   }
 
   public String toString() {
-    return String.format(
-        "fhirVersion=%s, resourceType=%s, name=%s", fhirVersion, resourceType, name
-    );
+    String string = new String();
+    boolean first = true;
+    if (fhirVersion != null) {
+      if (first) {
+        first = false;
+      } else {
+        string = string + ", ";
+      }
+      string = string + "fhirVersion=" + fhirVersion;
+    }
+
+    if (resourceType != null) {
+      if (first) {
+        first = false;
+      } else {
+        string = string + ", ";
+      }
+      string = string + "resourceType=" + resourceType;
+    }
+
+    if (id != null) {
+      if (first) {
+        first = false;
+      } else {
+        string = string + ", ";
+      }
+      string = string + "id=" + id;
+    }
+
+    if (name != null) {
+      if (first) {
+        first = false;
+      } else {
+        string = string + ", ";
+      }
+      string = string + "name=" + name;
+    }
+
+    if (url != null) {
+      if (first) {
+        first = false;
+      } else {
+        string = string + ", ";
+      }
+      string = string + "url=" + url;
+    }
+    return string;
   }
 }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/database/FhirResourceRepository.java b/server/src/main/java/org/hl7/davinci/endpoint/database/FhirResourceRepository.java
index adacc77e9..5d01a8a0f 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/database/FhirResourceRepository.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/database/FhirResourceRepository.java
@@ -17,7 +17,7 @@ public interface FhirResourceRepository extends CrudRepository<FhirResource, Lon
   @Query(
       "SELECT r FROM FhirResource r WHERE "
           + "r.fhirVersion = :#{#criteria.fhirVersion} "
-          + "and r.resourceType = :#{#criteria.resourceType} "
+          + "and LOWER(r.resourceType) = :#{#criteria.resourceType} "
           + "and r.id = :#{#criteria.id}")
   List<FhirResource> findById(
       @Param("criteria") FhirResourceCriteria criteria
@@ -26,7 +26,7 @@ List<FhirResource> findById(
   @Query(
       "SELECT r FROM FhirResource r WHERE "
           + "r.fhirVersion = :#{#criteria.fhirVersion} "
-          + "and r.resourceType = :#{#criteria.resourceType} "
+          + "and LOWER(r.resourceType) = :#{#criteria.resourceType} "
           + "and r.name = :#{#criteria.name}")
   List<FhirResource> findByName(
       @Param("criteria") FhirResourceCriteria criteria
@@ -35,7 +35,7 @@ List<FhirResource> findByName(
   @Query(
       "SELECT r FROM FhirResource r WHERE "
           + "r.fhirVersion = :#{#criteria.fhirVersion} "
-          + "and r.resourceType = :#{#criteria.resourceType} "
+          + "and LOWER(r.resourceType) = :#{#criteria.resourceType} "
           + "and r.url = :#{#criteria.url}")
   List<FhirResource> findByUrl(
       @Param("criteria") FhirResourceCriteria criteria
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java b/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
index 406b734b2..a0b52c5ed 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/files/CommonFileStore.java
@@ -21,6 +21,7 @@
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.util.List;
+import java.util.Locale;
 
 public abstract class CommonFileStore implements FileStore {
 
@@ -101,10 +102,10 @@ protected FileResource readFhirResourceFromFiles(List<FhirResource> fhirResource
   }
 
   public FileResource getFhirResourceByTopic(String fhirVersion, String resourceType, String name, String baseUrl) {
-    logger.info("CommonFileStore::getFhirResourceByTopic(): " + fhirVersion + "/" + resourceType + "/" + name);
-
     FhirResourceCriteria criteria = new FhirResourceCriteria();
-    criteria.setFhirVersion(fhirVersion).setResourceType(resourceType).setName(name);
+    criteria.setFhirVersion(fhirVersion).setResourceType(resourceType.toLowerCase()).setName(name);
+    logger.info("CommonFileStore::getFhirResourceByTopic(): " + criteria.toString());
+
     List<FhirResource> fhirResourceList = fhirResources.findByName(criteria);
     FileResource resource = readFhirResourceFromFiles(fhirResourceList, fhirVersion, baseUrl);
 
@@ -125,10 +126,10 @@ public FileResource getFhirResourceById(String fhirVersion, String resourceType,
 
   public FileResource getFhirResourceById(String fhirVersion, String resourceType, String id, String baseUrl,
       boolean isRoot) {
-    logger.info("CommonFileStore::getFhirResourceById(): " + fhirVersion + "/" + resourceType + "/" + id);
-
     FhirResourceCriteria criteria = new FhirResourceCriteria();
-    criteria.setFhirVersion(fhirVersion).setResourceType(resourceType).setId(id).setName(id);
+    criteria.setFhirVersion(fhirVersion).setResourceType(resourceType.toLowerCase()).setId(id);
+    logger.info("CommonFileStore::getFhirResourceById(): " + criteria.toString());
+
     List<FhirResource> fhirResourceList = fhirResources.findById(criteria);
     FileResource resource = readFhirResourceFromFiles(fhirResourceList, fhirVersion, baseUrl);
     System.out.println("Resource Pulled: " + resource + "-" + resource.getFilename());
@@ -154,10 +155,10 @@ public FileResource getFhirResourceById(String fhirVersion, String resourceType,
   }
 
   public FileResource getFhirResourceByUrl(String fhirVersion, String resourceType, String url, String baseUrl) {
-    logger.info("CommonFileStore::getFhirResourceByUrl(): " + fhirVersion + "/" + resourceType + "/" + url);
-
     FhirResourceCriteria criteria = new FhirResourceCriteria();
-    criteria.setFhirVersion(fhirVersion).setResourceType(resourceType).setUrl(url);
+    criteria.setFhirVersion(fhirVersion).setResourceType(resourceType.toLowerCase()).setUrl(url);
+    logger.info("CommonFileStore::getFhirResourceByUrl(): " + criteria.toString());
+
     List<FhirResource> fhirResourceList = fhirResources.findByUrl(criteria);
     FileResource resource = readFhirResourceFromFiles(fhirResourceList, fhirVersion, baseUrl);
 
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/rules/CoverageRequirementRuleCriteria.java b/server/src/main/java/org/hl7/davinci/endpoint/rules/CoverageRequirementRuleCriteria.java
index e630a7bad..b6728f847 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/rules/CoverageRequirementRuleCriteria.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/rules/CoverageRequirementRuleCriteria.java
@@ -8,6 +8,7 @@
 public class CoverageRequirementRuleCriteria {
 
   private String payor;
+  private String payorId;
   private String code;
   private String codeSystem;
   private String fhirVersion;
@@ -45,6 +46,13 @@ public CoverageRequirementRuleCriteria setPayor(String payor) {
     return this;
   }
 
+  public String getPayorId() { return payorId; }
+
+  public CoverageRequirementRuleCriteria setPayorId(String payorId) {
+    this.payorId = payorId;
+    return this;
+  }
+
   public String getFhirVersion() { return fhirVersion; }
 
   public CoverageRequirementRuleCriteria setFhirVersion(String fhirVersion) {
@@ -71,8 +79,9 @@ public static List<CoverageRequirementRuleCriteria> createQueriesFromR4(List<org
       String codeSystem = coding.getSystem();
       for (org.hl7.fhir.r4.model.Organization payor : payors) {
         String payorName = payor.getName();
+        String payorId = payor.getId();
         CoverageRequirementRuleCriteria criteria = new CoverageRequirementRuleCriteria();
-        criteria.setPayor(payorName).setCodeSystem(codeSystem).setCode(code).setFhirVersion("R4");
+        criteria.setPayor(payorName).setPayorId(payorId).setCodeSystem(codeSystem).setCode(code).setFhirVersion("R4");
         criteriaList.add(criteria);
       }
     }

From 4cb84bc88c67652061644217c521af9b6a3d2fea Mon Sep 17 00:00:00 2001
From: smalho01 <88040167+smalho01@users.noreply.github.com>
Date: Wed, 12 Jan 2022 02:11:16 -0500
Subject: [PATCH 25/30] Update build.gradle

---
 server/build.gradle | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/server/build.gradle b/server/build.gradle
index 33362bfad..2a4c139ef 100644
--- a/server/build.gradle
+++ b/server/build.gradle
@@ -142,7 +142,7 @@ void cloneCdsLibraryScript(branch) {
     println "GIT: clone CDS-Library branch " + branch
     exec {
         workingDir './'
-        commandLine 'git', 'clone', 'https://github.com/mcode/CDS-Library.git', 'CDS-Library'
+        commandLine 'git', 'clone', 'https://github.com/HL7-DaVinci/CDS-Library.git', 'CDS-Library'
     }
     checkoutCdsLibraryScript(branch)
 }

From d2386de6105003cf37688e9542a91515f3dff237 Mon Sep 17 00:00:00 2001
From: smalho01 <88040167+smalho01@users.noreply.github.com>
Date: Wed, 12 Jan 2022 02:11:57 -0500
Subject: [PATCH 26/30] Delete CdsAbstract.java

---
 .../cdshooks/services/crd/CdsAbstract.java    | 161 ------------------
 1 file changed, 161 deletions(-)
 delete mode 100644 server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsAbstract.java

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsAbstract.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsAbstract.java
deleted file mode 100644
index 750e37322..000000000
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsAbstract.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package org.hl7.davinci.endpoint.cdshooks.services.crd;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import org.cdshooks.*;
-import org.hl7.davinci.FhirComponentsT;
-import org.hl7.davinci.PrefetchTemplateElement;
-import org.hl7.davinci.endpoint.config.YamlConfig;
-import org.hl7.davinci.endpoint.rules.CoverageRequirementRuleCriteria;
-import org.hl7.davinci.r4.crdhook.DiscoveryExtension;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
-@Component
-public abstract class CdsAbstract<requestTypeT extends CdsRequest<?, ?>>{
-    static final Logger logger = LoggerFactory.getLogger(CdsAbstract.class);
-
-    /**
-     * The {id} portion of the URL to this service which is available at
-     * {baseUrl}/cds-services/{id}. REQUIRED
-     */
-    public String id;
-
-    /**
-     * The hook this service should be invoked on. REQUIRED
-     */
-    public Hook hook;
-
-    /**
-     * The human-friendly name of this service. RECOMMENDED
-     */
-    public String title;
-
-    /**
-     * The description of this service. REQUIRED
-     */
-    public String description;
-
-    /**
-     * An object containing key/value pairs of FHIR queries that this service is
-     * requesting that the EHR prefetch and provide on each service call. The key is
-     * a string that describes the type of data being requested and the value is a
-     * string representing the FHIR query. OPTIONAL
-     */
-    public Prefetch prefetch;
-
-    private final List<PrefetchTemplateElement> prefetchElements;
-
-    protected FhirComponentsT fhirComponents;
-
-    private final DiscoveryExtension extension;
-
-    @Autowired
-    @JsonIgnore
-    public YamlConfig myConfig;
-
-    public CdsAbstract(String id, Hook hook, String title, String description,
-                      List<PrefetchTemplateElement> prefetchElements, FhirComponentsT fhirComponents,
-                      DiscoveryExtension extension) {
-
-        if (id == null) {
-            throw new NullPointerException("CDSService id cannot be null");
-        }
-        if (hook == null) {
-            throw new NullPointerException("CDSService hook cannot be null");
-        }
-        if (description == null) {
-            throw new NullPointerException("CDSService description cannot be null");
-        }
-        this.id = id;
-        this.hook = hook;
-        this.title = title;
-        this.description = description;
-        this.prefetchElements = prefetchElements;
-        prefetch = new Prefetch();
-        for (PrefetchTemplateElement prefetchElement : prefetchElements) {
-            this.prefetch.put(prefetchElement.getKey(), prefetchElement.getQuery());
-        }
-        this.fhirComponents = fhirComponents;
-        this.extension = extension;
-    }
-
-    public DiscoveryExtension getExtension() { return extension; }
-
-    public List<PrefetchTemplateElement> getPrefetchElements() {
-        return prefetchElements;
-    }
-
-    protected Link smartLinkBuilder(String patientId, String fhirBase, URL applicationBaseUrl, String questionnaireUri,
-                                  String reqResourceId, CoverageRequirementRuleCriteria criteria, boolean priorAuthRequired, String label) {
-        URI configLaunchUri = myConfig.getLaunchUrl();
-        questionnaireUri = applicationBaseUrl + "/fhir/r4/" + questionnaireUri;
-
-        String launchUrl;
-        if (myConfig.getLaunchUrl().isAbsolute()) {
-            launchUrl = myConfig.getLaunchUrl().toString();
-        } else {
-            try {
-                launchUrl = new URL(applicationBaseUrl.getProtocol(), applicationBaseUrl.getHost(),
-                        applicationBaseUrl.getPort(), applicationBaseUrl.getFile() + configLaunchUri.toString(), null).toString();
-            } catch (MalformedURLException e) {
-                String msg = "Error creating smart launch URL";
-                logger.error(msg);
-                throw new RuntimeException(msg);
-            }
-        }
-
-        if (fhirBase != null && fhirBase.endsWith("/")) {
-            fhirBase = fhirBase.substring(0, fhirBase.length() - 1);
-        }
-        if (patientId != null && patientId.startsWith("Patient/")) {
-            patientId = patientId.substring(8);
-        }
-
-        // PARAMS:
-        // template is the uri of the questionnaire
-        // request is the ID of the device request or medrec (not the full URI like the
-        // IG says, since it should be taken from fhirBase
-
-        String filepath = "../../getfile/" + criteria.getQueryString();
-
-        String appContext = "template=" + questionnaireUri + "&request=" + reqResourceId;
-        appContext = appContext + "&fhirpath=" + applicationBaseUrl + "/fhir/";
-
-        appContext = appContext + "&priorauth=" + (priorAuthRequired ? "true" : "false");
-        appContext = appContext + "&filepath=" + applicationBaseUrl + "/";
-        if (myConfig.getUrlEncodeAppContext()) {
-            logger.info("CdsService::smartLinkBuilder: URL encoding appcontext");
-            appContext = URLEncoder.encode(appContext, StandardCharsets.UTF_8).toString();
-        }
-
-        logger.info("smarLinkBuilder: appContext: " + appContext);
-
-        if (myConfig.isAppendParamsToSmartLaunchUrl()) {
-            launchUrl = launchUrl + "?iss=" + fhirBase + "&patientId=" + patientId + "&template=" + questionnaireUri
-                    + "&request=" + reqResourceId;
-        } else {
-            // TODO: The iss should be set by the EHR?
-            launchUrl = launchUrl;
-        }
-
-        Link link = new Link();
-        link.setType("smart");
-        link.setLabel(label);
-        link.setUrl(launchUrl);
-
-        link.setAppContext(appContext);
-
-        return link;
-    }
-
-    public abstract CdsResponse handleRequest(requestTypeT request, URL applicationBaseUrl);
-}

From e40cdd9dd9f99e56bf14f4e89b9a219a3de073a7 Mon Sep 17 00:00:00 2001
From: smalho01 <88040167+smalho01@users.noreply.github.com>
Date: Wed, 12 Jan 2022 02:21:02 -0500
Subject: [PATCH 27/30] Delete CdsServiceRems.java

---
 .../cdshooks/services/crd/CdsServiceRems.java | 86 -------------------
 1 file changed, 86 deletions(-)
 delete mode 100644 server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsServiceRems.java

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsServiceRems.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsServiceRems.java
deleted file mode 100644
index f6e5dbe63..000000000
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsServiceRems.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.hl7.davinci.endpoint.cdshooks.services.crd;
-
-import org.cdshooks.Card;
-import org.cdshooks.CdsRequest;
-import org.cdshooks.CdsResponse;
-import org.cdshooks.Hook;
-import org.hl7.davinci.FhirComponentsT;
-import org.hl7.davinci.PrefetchTemplateElement;
-import org.hl7.davinci.endpoint.components.CardBuilder;
-import org.hl7.davinci.r4.crdhook.DiscoveryExtension;
-import org.hl7.fhir.r4.model.Coding;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-import org.springframework.web.bind.annotation.RequestBody;
-
-import javax.validation.Valid;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-
-@Component
-public abstract class CdsServiceRems<requestTypeT extends CdsRequest<?, ?>> extends CdsAbstract<requestTypeT> {
-    static final Logger logger = LoggerFactory.getLogger(CdsServiceRems.class);
-
-    public List<Coding> remsDrugs = new ArrayList<>();
-
-    public CdsServiceRems(String id, Hook hook, String title, String description,
-                      List<PrefetchTemplateElement> prefetchElements, FhirComponentsT fhirComponents,
-                      DiscoveryExtension extension) {
-        super(id, hook, title, description, prefetchElements, fhirComponents, extension);
-        Coding turalio = new Coding()
-                .setCode("2183126")
-                .setSystem("http://www.nlm.nih.gov/research/umls/rxnorm")
-                .setDisplay("Turalio");
-        Coding iPledge = new Coding()
-                .setCode("6064")
-                .setSystem("http://www.nlm.nih.gov/research/umls/rxnorm")
-                .setDisplay("Isotretinoin");
-        Coding revlimid = new Coding()
-                .setCode("337535")
-                .setSystem("http://www.nlm.nih.gov/research/umls/rxnorm")
-                .setDisplay("Revlimid");
-        Coding abstral = new Coding()
-                .setCode("1053648")
-                .setSystem("http://www.nlm.nih.gov/research/umls/rxnorm")
-                .setDisplay("Abstral");
-        remsDrugs.add(turalio);
-        remsDrugs.add(iPledge);
-        remsDrugs.add(revlimid);
-        remsDrugs.add(abstral);
-    }
-
-    /**
-     * Performs generic operations for incoming requests of any type.
-     *
-     * @param request the generically typed incoming request
-     * @return The response from the server
-     */
-    public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL applicationBaseUrl) {
-        CdsResponse response = new CdsResponse();
-        List<Coding> medications = getMedications(request);
-        for (Coding medication : medications) {
-            Card card = CardBuilder.summaryCard("");
-            if (isRemsDrug(medication)) {
-                card.setSummary(String.format("%s is a REMS drug", medication.getDisplay()));
-            } else {
-                card.setSummary(String.format("%s is not a REMS drug", medication.getDisplay()));
-            }
-            response.addCard(card);
-        }
-        return response;
-    }
-
-    private boolean isRemsDrug(Coding medication) {
-        for( Coding remsDrug : remsDrugs){
-            if (remsDrug.getCode().equals(medication.getCode())){
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public abstract List<Coding> getMedications(requestTypeT request);
-
-}
\ No newline at end of file

From 72e13157b4fbb333efad0f09aa30ac381f7fb832 Mon Sep 17 00:00:00 2001
From: smalho01 <88040167+smalho01@users.noreply.github.com>
Date: Wed, 12 Jan 2022 02:22:38 -0500
Subject: [PATCH 28/30] Delete OrderSelectServiceRems.java

---
 .../crd/r4/OrderSelectServiceRems.java        | 67 -------------------
 1 file changed, 67 deletions(-)
 delete mode 100644 server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectServiceRems.java

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectServiceRems.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectServiceRems.java
deleted file mode 100644
index a5cf2824b..000000000
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/OrderSelectServiceRems.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.hl7.davinci.endpoint.cdshooks.services.crd.r4;
-
-import org.cdshooks.Hook;
-import org.hl7.davinci.PrefetchTemplateElement;
-import org.hl7.davinci.endpoint.Utils;
-import org.hl7.davinci.endpoint.cdshooks.services.crd.CdsServiceRems;
-import org.hl7.davinci.r4.FhirComponents;
-import org.hl7.davinci.r4.Utilities;
-import org.hl7.davinci.r4.crdhook.CrdPrefetch;
-import org.hl7.davinci.r4.crdhook.orderselect.CrdPrefetchTemplateElements;
-import org.hl7.davinci.r4.crdhook.orderselect.OrderSelectRequest;
-import org.hl7.fhir.r4.model.Bundle;
-import org.hl7.fhir.r4.model.Coding;
-import org.hl7.fhir.r4.model.MedicationRequest;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.stereotype.Component;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-
-@Component("r4_OrderSelectServiceRems")
-public class OrderSelectServiceRems extends CdsServiceRems<OrderSelectRequest> {
-
-    public static final String ID = "order-select-rems";
-    public static final String TITLE = "order-select-rems Coverage Requirements Discovery";
-    public static final Hook HOOK = Hook.ORDER_SELECT;
-    public static final String DESCRIPTION =
-            "Get information regarding the coverage requirements for REMS drugs";
-    public static final List<PrefetchTemplateElement> PREFETCH_ELEMENTS = Arrays.asList(
-            CrdPrefetchTemplateElements.MEDICATION_STATEMENT_BUNDLE,
-            CrdPrefetchTemplateElements.MEDICATION_REQUEST_BUNDLE);
-    public static final FhirComponents FHIRCOMPONENTS = new FhirComponents();
-    static final Logger logger = LoggerFactory.getLogger(OrderSelectServiceRems.class);
-
-    public List<Coding> remsDrugs = new ArrayList<>();
-
-    public OrderSelectServiceRems() {
-        super(ID, HOOK, TITLE, DESCRIPTION, PREFETCH_ELEMENTS, FHIRCOMPONENTS, null);
-    }
-
-
-    public List<Coding> getMedications(OrderSelectRequest request) {
-        List<String> selections = Arrays.asList(request.getContext().getSelections());
-        CrdPrefetch prefetch = request.getPrefetch();
-        List<Coding> medications = getSelections(prefetch, selections);
-        return medications;
-    }
-    public List<Coding> getSelections(CrdPrefetch prefetch, List<String> selections) {
-        Bundle medicationRequestBundle = prefetch.getMedicationRequestBundle();
-        List<MedicationRequest> medicationRequestList = Utilities.getResourcesOfTypeFromBundle(MedicationRequest.class, medicationRequestBundle);
-        List<Coding> codings = new ArrayList<>();
-
-        if (!medicationRequestList.isEmpty()) {
-            for (MedicationRequest medicationRequest : medicationRequestList) {
-                if (Utils.idInSelectionsList(medicationRequest.getId(), selections)) {
-                    codings.add(medicationRequest.getMedicationCodeableConcept().getCodingFirstRep()); // assume there is only 1 coding per MR
-                }
-            }
-        }
-
-        return codings;
-    }
-
-}

From b2973158b30efa0514bbbae52e23e6de23f20c8f Mon Sep 17 00:00:00 2001
From: Sahil Malhotra <smalhotra@mitre.org>
Date: Wed, 12 Jan 2022 02:23:44 -0500
Subject: [PATCH 29/30] revert accidental merges from mcode space

---
 .../java/org/hl7/davinci/endpoint/Utils.java  |  24 --
 .../cdshooks/services/crd/CdsService.java     | 227 +++++++++++++++---
 .../services/crd/CdsServiceInformation.java   |   8 +-
 .../services/crd/r4/FhirBundleProcessor.java  |  37 ++-
 .../controllers/r4/CdsHooksController.java    |  30 +--
 5 files changed, 242 insertions(+), 84 deletions(-)

diff --git a/server/src/main/java/org/hl7/davinci/endpoint/Utils.java b/server/src/main/java/org/hl7/davinci/endpoint/Utils.java
index c85e2f452..4d407e93c 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/Utils.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/Utils.java
@@ -2,7 +2,6 @@
 
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 import com.google.common.net.HttpHeaders;
 
@@ -39,27 +38,4 @@ public static URL getApplicationBaseUrl(HttpServletRequest request) {
     }
   }
 
-  public static String stripResourceType(String identifier) {
-    int indexOfDivider = identifier.indexOf('/');
-    if (indexOfDivider+1 == identifier.length()) {
-      // remove the trailing '/'
-      return identifier.substring(0, indexOfDivider);
-    } else {
-      return identifier.substring(indexOfDivider+1);
-    }
-  }
-
-  public static boolean idInSelectionsList(String identifier, List<String> selections) {
-    if (selections.isEmpty()) {
-      return true;
-    } else {
-      for ( String selection : selections) {
-        if (identifier.contains(stripResourceType(selection))) {
-          return true;
-        }
-      }
-      return false;
-    }
-  }
-
 }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
index a2aec34ea..b5664cac2 100755
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsService.java
@@ -1,19 +1,33 @@
 package org.hl7.davinci.endpoint.cdshooks.services.crd;
 
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Date;
+import javax.validation.Valid;
+
 import org.apache.commons.lang.StringUtils;
 import org.cdshooks.*;
 import org.hl7.davinci.FhirComponentsT;
 import org.hl7.davinci.PrefetchTemplateElement;
 import org.hl7.davinci.RequestIncompleteException;
+import org.hl7.davinci.endpoint.config.YamlConfig;
 import org.hl7.davinci.endpoint.components.CardBuilder;
 import org.hl7.davinci.endpoint.components.CardBuilder.CqlResultsForCard;
 import org.hl7.davinci.endpoint.components.PrefetchHydrator;
+import org.hl7.davinci.endpoint.database.FhirResourceRepository;
 import org.hl7.davinci.endpoint.database.RequestLog;
 import org.hl7.davinci.endpoint.database.RequestService;
 import org.hl7.davinci.endpoint.files.FileStore;
+import org.hl7.davinci.endpoint.rules.CoverageRequirementRuleCriteria;
 import org.hl7.davinci.endpoint.rules.CoverageRequirementRuleResult;
-import org.hl7.davinci.r4.crdhook.DiscoveryExtension;
 import org.hl7.davinci.r4.crdhook.orderselect.OrderSelectRequest;
+import org.hl7.davinci.r4.crdhook.DiscoveryExtension;
 import org.opencds.cqf.cql.engine.execution.Context;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,26 +35,99 @@
 import org.springframework.stereotype.Component;
 import org.springframework.web.bind.annotation.RequestBody;
 
-import javax.validation.Valid;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
 @Component
-public abstract class CdsService<requestTypeT extends CdsRequest<?, ?>> extends CdsAbstract<requestTypeT> {
+public abstract class CdsService<requestTypeT extends CdsRequest<?, ?>> {
   static final Logger logger = LoggerFactory.getLogger(CdsService.class);
 
+  /**
+   * The {id} portion of the URL to this service which is available at
+   * {baseUrl}/cds-services/{id}. REQUIRED
+   */
+  public String id;
+
+  /**
+   * The hook this service should be invoked on. REQUIRED
+   */
+  public Hook hook;
+
+  /**
+   * The human-friendly name of this service. RECOMMENDED
+   */
+  public String title;
+
+  /**
+   * The description of this service. REQUIRED
+   */
+  public String description;
+
+  /**
+   * An object containing key/value pairs of FHIR queries that this service is
+   * requesting that the EHR prefetch and provide on each service call. The key is
+   * a string that describes the type of data being requested and the value is a
+   * string representing the FHIR query. OPTIONAL
+   */
+  public Prefetch prefetch;
+
+  @Autowired
+  private YamlConfig myConfig;
+
   @Autowired
   RequestService requestService;
 
   @Autowired
   FileStore fileStore;
 
+  @Autowired
+  private FhirResourceRepository fhirResourceRepository;
+
+  private final List<PrefetchTemplateElement> prefetchElements;
+
+  protected FhirComponentsT fhirComponents;
+
+  private final DiscoveryExtension extension;
+
+  /**
+   * Create a new cdsservice.
+   *
+   * @param id               Will be used in the url, should be unique.
+   * @param hook             Which hook can call this.
+   * @param title            Human title.
+   * @param description      Human description.
+   * @param prefetchElements List of prefetch elements, will be in prefetch
+   *                         template.
+   * @param fhirComponents   Fhir components to use
+   * @param extension        Custom CDS Hooks extensions.
+   */
   public CdsService(String id, Hook hook, String title, String description,
       List<PrefetchTemplateElement> prefetchElements, FhirComponentsT fhirComponents,
       DiscoveryExtension extension) {
-    super(id, hook, title, description, prefetchElements, fhirComponents, extension);
+
+    if (id == null) {
+      throw new NullPointerException("CDSService id cannot be null");
+    }
+    if (hook == null) {
+      throw new NullPointerException("CDSService hook cannot be null");
+    }
+    if (description == null) {
+      throw new NullPointerException("CDSService description cannot be null");
+    }
+    this.id = id;
+    this.hook = hook;
+    this.title = title;
+    this.description = description;
+    this.prefetchElements = prefetchElements;
+    prefetch = new Prefetch();
+    for (PrefetchTemplateElement prefetchElement : prefetchElements) {
+      this.prefetch.put(prefetchElement.getKey(), prefetchElement.getQuery());
+    }
+    this.fhirComponents = fhirComponents;
+    this.extension = extension;
+  }
+
+  public DiscoveryExtension getExtension() { return extension; }
+
+  public List<PrefetchTemplateElement> getPrefetchElements() {
+    return prefetchElements;
   }
 
   /**
@@ -105,22 +192,30 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
 
         if (results.getCoverageRequirements().getApplies()) {
 
-          if (coverageRequirements.isDocumentationRequired() || coverageRequirements.isPriorAuthRequired()) {
+          // if prior auth already approved
+          if (coverageRequirements.isPriorAuthApproved()) {
+            response.addCard(CardBuilder.priorAuthCard(results, results.getRequest(), fhirComponents, coverageRequirements.getPriorAuthId(),
+                request.getContext().getPatientId(), lookupResult.getCriteria().getPayorId(), request.getContext().getUserId(),
+                applicationBaseUrl.toString() + "/fhir/" + fhirComponents.getFhirVersion().toString(),
+                fhirResourceRepository));
+
+          } else if (coverageRequirements.isDocumentationRequired() || coverageRequirements.isPriorAuthRequired()) {
             if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireOrderUri())
                 || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireFaceToFaceUri())
                 || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireLabUri())
                 || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireProgressNoteUri())
                 || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePARequestUri())
                 || StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePlanOfCareUri())
-                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireDispenseUri())) {
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireDispenseUri())
+                || StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireAdditionalUri())) {
               List<Link> smartAppLinks = createQuestionnaireLinks(request, applicationBaseUrl, lookupResult, results);
               response.addCard(CardBuilder.transform(results, smartAppLinks));
 
               // add a card for an alternative therapy if there is one
               if (results.getAlternativeTherapy().getApplies() && hookConfiguration.getAlternativeTherapy()) {
                 try {
-                  response.addCard(CardBuilder.alternativeTherapyCard(results.getAlternativeTherapy(), results.getRequest(),
-                      fhirComponents));
+                  response.addCard(CardBuilder.alternativeTherapyCard(results.getAlternativeTherapy(),
+                      results.getRequest(), fhirComponents));
                 } catch (RuntimeException e) {
                   logger.warn("Failed to process alternative therapy: " + e.getMessage());
                 }
@@ -133,7 +228,10 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
             // no prior auth or documentation required
             logger.info("Add the no doc or prior auth required card");
             Card card = CardBuilder.transform(results);
-            card = CardBuilder.createSuggestionsWithNote(card, results, fhirComponents);
+            card.addSuggestionsItem(CardBuilder.createSuggestionWithNote(card, results.getRequest(), fhirComponents,
+                "Save Update To EHR", "Update original " + results.getRequest().fhirType() + " to add note",
+                true));
+            card.setSelectionBehavior(Card.SelectionBehaviorEnum.ANY);
             response.addCard(card);
           }
         }
@@ -157,7 +255,7 @@ public CdsResponse handleRequest(@Valid @RequestBody requestTypeT request, URL a
       CardBuilder.errorCardIfNonePresent(response);
     }
 
-    // Adding card to requestLog
+    // Ading card to requestLog
     requestLog.setCardListFromCards(response.getCards());
     requestService.edit(requestLog);
 
@@ -170,41 +268,47 @@ private List<Link> createQuestionnaireLinks(requestTypeT request, URL applicatio
     CoverageRequirements coverageRequirements = results.getCoverageRequirements();
     if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireOrderUri())) {
       listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
-          coverageRequirements.getQuestionnaireOrderUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
-          coverageRequirements.isPriorAuthRequired(), "Order Form"));
+          coverageRequirements.getQuestionnaireOrderUri(), coverageRequirements.getRequestId(),
+          lookupResult.getCriteria(), coverageRequirements.isPriorAuthRequired(), "Order Form"));
     }
     if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireFaceToFaceUri())) {
       listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
-          coverageRequirements.getQuestionnaireFaceToFaceUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
-          coverageRequirements.isPriorAuthRequired(), "Face to Face Encounter Form"));
+          coverageRequirements.getQuestionnaireFaceToFaceUri(), coverageRequirements.getRequestId(),
+          lookupResult.getCriteria(), coverageRequirements.isPriorAuthRequired(), "Face to Face Encounter Form"));
     }
     if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireLabUri())) {
       listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
-          coverageRequirements.getQuestionnaireLabUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
-          coverageRequirements.isPriorAuthRequired(), "Lab Form"));
+          coverageRequirements.getQuestionnaireLabUri(), coverageRequirements.getRequestId(),
+          lookupResult.getCriteria(), coverageRequirements.isPriorAuthRequired(), "Lab Form"));
     }
     if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireProgressNoteUri())) {
       listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
-          coverageRequirements.getQuestionnaireProgressNoteUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
-          coverageRequirements.isPriorAuthRequired(), "Progress Note"));
+          coverageRequirements.getQuestionnaireProgressNoteUri(), coverageRequirements.getRequestId(),
+          lookupResult.getCriteria(), coverageRequirements.isPriorAuthRequired(), "Progress Note"));
     }
 
     if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePARequestUri())) {
       listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
-          coverageRequirements.getQuestionnairePARequestUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
-          coverageRequirements.isPriorAuthRequired(), "PA Request"));
+          coverageRequirements.getQuestionnairePARequestUri(), coverageRequirements.getRequestId(),
+          lookupResult.getCriteria(), coverageRequirements.isPriorAuthRequired(), "PA Request"));
     }
 
     if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnairePlanOfCareUri())) {
       listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
-          coverageRequirements.getQuestionnairePlanOfCareUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
-          coverageRequirements.isPriorAuthRequired(), "Plan of Care/Certification"));
+          coverageRequirements.getQuestionnairePlanOfCareUri(), coverageRequirements.getRequestId(),
+          lookupResult.getCriteria(), coverageRequirements.isPriorAuthRequired(), "Plan of Care/Certification"));
     }
 
     if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireDispenseUri())) {
       listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
-          coverageRequirements.getQuestionnaireDispenseUri(), coverageRequirements.getRequestId(), lookupResult.getCriteria(),
-          coverageRequirements.isPriorAuthRequired(), "Dispense Form"));
+          coverageRequirements.getQuestionnaireDispenseUri(), coverageRequirements.getRequestId(),
+          lookupResult.getCriteria(), coverageRequirements.isPriorAuthRequired(), "Dispense Form"));
+    }
+
+    if (StringUtils.isNotEmpty(coverageRequirements.getQuestionnaireAdditionalUri())) {
+      listOfLinks.add(smartLinkBuilder(request.getContext().getPatientId(), request.getFhirServer(), applicationBaseUrl,
+          coverageRequirements.getQuestionnaireAdditionalUri(), coverageRequirements.getRequestId(),
+          lookupResult.getCriteria(), coverageRequirements.isPriorAuthRequired(), "Additional Form"));
     }
     return listOfLinks;
   }
@@ -222,7 +326,72 @@ protected Object evaluateStatement(String statement, Context context) {
     }
   }
 
+  private Link smartLinkBuilder(String patientId, String fhirBase, URL applicationBaseUrl, String questionnaireUri,
+      String reqResourceId, CoverageRequirementRuleCriteria criteria, boolean priorAuthRequired, String label) {
+    URI configLaunchUri = myConfig.getLaunchUrl();
+    questionnaireUri = applicationBaseUrl + "/fhir/r4/" + questionnaireUri;
+
+    String launchUrl;
+    if (myConfig.getLaunchUrl().isAbsolute()) {
+      launchUrl = myConfig.getLaunchUrl().toString();
+    } else {
+      try {
+        launchUrl = new URL(applicationBaseUrl.getProtocol(), applicationBaseUrl.getHost(),
+            applicationBaseUrl.getPort(), applicationBaseUrl.getFile() + configLaunchUri.toString(), null).toString();
+      } catch (MalformedURLException e) {
+        String msg = "Error creating smart launch URL";
+        logger.error(msg);
+        throw new RuntimeException(msg);
+      }
+    }
+
+    if (fhirBase != null && fhirBase.endsWith("/")) {
+      fhirBase = fhirBase.substring(0, fhirBase.length() - 1);
+    }
+    if (patientId != null && patientId.startsWith("Patient/")) {
+      patientId = patientId.substring(8);
+    }
 
+    // PARAMS:
+    // template is the uri of the questionnaire
+    // request is the ID of the device request or medrec (not the full URI like the
+    // IG says, since it should be taken from fhirBase
+
+    String filepath = "../../getfile/" + criteria.getQueryString();
+
+    String appContext = "template=" + questionnaireUri + "&request=" + reqResourceId;
+    appContext = appContext + "&fhirpath=" + applicationBaseUrl + "/fhir/";
+
+    appContext = appContext + "&priorauth=" + (priorAuthRequired ? "true" : "false");
+    appContext = appContext + "&filepath=" + applicationBaseUrl + "/";
+    if (myConfig.getUrlEncodeAppContext()) {
+      logger.info("CdsService::smartLinkBuilder: URL encoding appcontext");
+      try {
+        appContext = URLEncoder.encode(appContext, StandardCharsets.UTF_8.name()).toString();
+      } catch (UnsupportedEncodingException e) {
+        logger.error("CdsService::smartLinkBuilder: failed to encode URL: " + e.getMessage());
+      }
+    }
+
+    logger.info("smarLinkBuilder: appContext: " + appContext);
+
+    if (myConfig.isAppendParamsToSmartLaunchUrl()) {
+      launchUrl = launchUrl + "?iss=" + fhirBase + "&patientId=" + patientId + "&template=" + questionnaireUri
+          + "&request=" + reqResourceId;
+    } else {
+      // TODO: The iss should be set by the EHR?
+      launchUrl = launchUrl;
+    }
+
+    Link link = new Link();
+    link.setType("smart");
+    link.setLabel(label);
+    link.setUrl(launchUrl);
+
+    link.setAppContext(appContext);
+
+    return link;
+  }
 
   // Implement these in child class
   public abstract List<CoverageRequirementRuleResult> createCqlExecutionContexts(requestTypeT request,
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsServiceInformation.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsServiceInformation.java
index ca7ea731c..2e08fbcbc 100755
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsServiceInformation.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/CdsServiceInformation.java
@@ -4,14 +4,14 @@
 import java.util.List;
 
 public class CdsServiceInformation {
-  private List<CdsAbstract> services = null;
+  private List<CdsService> services = null;
 
   /**
    * Add a service.
    * @param servicesItem The service.
    * @return
    */
-  public CdsServiceInformation addServicesItem(CdsAbstract servicesItem) {
+  public CdsServiceInformation addServicesItem(CdsService servicesItem) {
     if (this.services == null) {
       this.services = new ArrayList<>();
     }
@@ -19,11 +19,11 @@ public CdsServiceInformation addServicesItem(CdsAbstract servicesItem) {
     return this;
   }
 
-  public List<CdsAbstract> getServices() {
+  public List<CdsService> getServices() {
     return services;
   }
 
-  public void setServices(List<CdsAbstract> services) {
+  public void setServices(List<CdsService> services) {
     this.services = services;
   }
 }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/FhirBundleProcessor.java b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/FhirBundleProcessor.java
index a5be23692..538169ef9 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/FhirBundleProcessor.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/cdshooks/services/crd/r4/FhirBundleProcessor.java
@@ -18,8 +18,6 @@
 import java.util.List;
 import java.util.stream.Collectors;
 
-import static org.hl7.davinci.endpoint.Utils.idInSelectionsList;
-
 public class FhirBundleProcessor {
   static final Logger logger = LoggerFactory.getLogger(FhirBundleProcessor.class);
 
@@ -50,7 +48,7 @@ public void processDeviceRequests() {
       logger.info("r4/FhirBundleProcessor::getAndProcessDeviceRequests: DeviceRequest(s) found");
 
       for (DeviceRequest deviceRequest : deviceRequestList) {
-        if (idInSelectionsList(deviceRequest.getId(), selections)) {
+        if (idInSelectionsList(deviceRequest.getId())) {
           List<CoverageRequirementRuleCriteria> criteriaList = createCriteriaList(deviceRequest.getCodeCodeableConcept(), deviceRequest.getInsurance(), null);
           buildExecutionContexts(criteriaList, (Patient) deviceRequest.getSubject().getResource(), "device_request", deviceRequest);
         }
@@ -65,7 +63,7 @@ public void processMedicationRequests() {
       logger.info("r4/FhirBundleProcessor::getAndProcessMedicationRequests: MedicationRequest(s) found");
 
       for (MedicationRequest medicationRequest : medicationRequestList) {
-        if (idInSelectionsList(medicationRequest.getId(), selections)) {
+        if (idInSelectionsList(medicationRequest.getId())) {
           List<CoverageRequirementRuleCriteria> criteriaList = createCriteriaList(medicationRequest.getMedicationCodeableConcept(), medicationRequest.getInsurance(), null);
           buildExecutionContexts(criteriaList, (Patient) medicationRequest.getSubject().getResource(), "medication_request", medicationRequest);
         }
@@ -83,7 +81,7 @@ public void processMedicationDispenses() {
           medicationDispenseBundle);
 
       for (MedicationDispense medicationDispense : medicationDispenseList) {
-        if (idInSelectionsList(medicationDispense.getId(), selections)) {
+        if (idInSelectionsList(medicationDispense.getId())) {
           List<CoverageRequirementRuleCriteria> criteriaList = createCriteriaList(medicationDispense.getMedicationCodeableConcept(), null, payorList);
           buildExecutionContexts(criteriaList, (Patient) medicationDispense.getSubject().getResource(), "medication_dispense", medicationDispense);
         }
@@ -98,7 +96,7 @@ public void processServiceRequests() {
       logger.info("r4/FhirBundleProcessor::getAndProcessServiceRequests: ServiceRequest(s) found");
 
       for (ServiceRequest serviceRequest : serviceRequestList) {
-        if (idInSelectionsList(serviceRequest.getId(), selections)) {
+        if (idInSelectionsList(serviceRequest.getId())) {
           List<CoverageRequirementRuleCriteria> criteriaList = createCriteriaList(serviceRequest.getCode(), serviceRequest.getInsurance(), null);
           buildExecutionContexts(criteriaList, (Patient) serviceRequest.getSubject().getResource(), "service_request", serviceRequest);
         }
@@ -118,7 +116,7 @@ public void processOrderSelectMedicationStatements() {
 
       // process each of the MedicationRequests
       for (MedicationRequest medicationRequest : medicationRequestList) {
-        if (idInSelectionsList(medicationRequest.getId(), selections)) {
+        if (idInSelectionsList(medicationRequest.getId())) {
 
           // run on each of the MedicationStatements
           for (MedicationStatement medicationStatement : medicationStatementList) {
@@ -212,4 +210,29 @@ private void buildExecutionContexts(List<CoverageRequirementRuleCriteria> criter
     }
   }
 
+  private String stripResourceType(String identifier) {
+    int indexOfDivider = identifier.indexOf('/');
+    if (indexOfDivider+1 == identifier.length()) {
+      // remove the trailing '/'
+      return identifier.substring(0, indexOfDivider);
+    } else {
+      return identifier.substring(indexOfDivider+1);
+    }
+  }
+
+  private boolean idInSelectionsList(String identifier) {
+    if (this.selections.isEmpty()) {
+      // if selections list is empty, just assume we should process the request
+      return true;
+    } else {
+      for ( String selection : selections) {
+        if (identifier.contains(stripResourceType(selection))) {
+          logger.info("r4/FhirBundleProcessor::idInSelectionsList(" + identifier + "): identifier found in selections list");
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
 }
diff --git a/server/src/main/java/org/hl7/davinci/endpoint/controllers/r4/CdsHooksController.java b/server/src/main/java/org/hl7/davinci/endpoint/controllers/r4/CdsHooksController.java
index b3afdfcc3..cb4ea5be6 100644
--- a/server/src/main/java/org/hl7/davinci/endpoint/controllers/r4/CdsHooksController.java
+++ b/server/src/main/java/org/hl7/davinci/endpoint/controllers/r4/CdsHooksController.java
@@ -1,10 +1,11 @@
 package org.hl7.davinci.endpoint.controllers.r4;
 
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
 import org.cdshooks.CdsResponse;
 import org.hl7.davinci.endpoint.Utils;
 import org.hl7.davinci.endpoint.cdshooks.services.crd.CdsServiceInformation;
 import org.hl7.davinci.endpoint.cdshooks.services.crd.r4.OrderSelectService;
-import org.hl7.davinci.endpoint.cdshooks.services.crd.r4.OrderSelectServiceRems;
 import org.hl7.davinci.endpoint.cdshooks.services.crd.r4.OrderSignService;
 import org.hl7.davinci.r4.crdhook.CrdPrefetch;
 import org.hl7.davinci.r4.crdhook.orderselect.OrderSelectRequest;
@@ -12,10 +13,14 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.validation.Valid;
+import java.io.IOException;
+import java.util.stream.Collectors;
 
 @RestController("r4_CdsHooksController")
 public class CdsHooksController {
@@ -28,7 +33,7 @@ public class CdsHooksController {
 
   @Autowired private OrderSelectService orderSelectService;
   @Autowired private OrderSignService orderSignService;
-  @Autowired private OrderSelectServiceRems orderSelectServiceRems;
+
   /**
    * The FHIR r4 services discovery endpoint.
    * @return A services object containing an array of all services available on this server
@@ -59,21 +64,6 @@ public CdsResponse handleOrderSelect(@Valid @RequestBody OrderSelectRequest requ
     return orderSelectService.handleRequest(request, Utils.getApplicationBaseUrl(httpServletRequest));
   }
 
-  /**
-   * The coverage requirement discovery endpoint for the order select rems hook.
-   * @param request An order select triggered cds request
-   * @return The card response
-   */
-  @CrossOrigin
-  @PostMapping(value = FHIR_RELEASE + URL_BASE + "/" + OrderSelectServiceRems.ID,
-          consumes = "application/json;charset=UTF-8")
-  public CdsResponse handleOrderSelectRems(@Valid @RequestBody OrderSelectRequest request, final HttpServletRequest httpServletRequest) {
-    logger.info("r4/handleOrderSelectRems");
-    if (request.getPrefetch() == null) {
-      request.setPrefetch(new CrdPrefetch());
-    }
-    return orderSelectServiceRems.handleRequest(request, Utils.getApplicationBaseUrl(httpServletRequest));
-  }
   /**
    * The coverage requirement discovery endpoint for the order sign hook.
    * @param request An order sign triggered cds request

From 2e08689e5b00b95b903bfcea0941bdec12b5a476 Mon Sep 17 00:00:00 2001
From: smalho01 <88040167+smalho01@users.noreply.github.com>
Date: Wed, 12 Jan 2022 02:24:18 -0500
Subject: [PATCH 30/30] Delete dockerRunner.sh

---
 dockerRunner.sh | 27 ---------------------------
 1 file changed, 27 deletions(-)
 delete mode 100755 dockerRunner.sh

diff --git a/dockerRunner.sh b/dockerRunner.sh
deleted file mode 100755
index fc2adb2ac..000000000
--- a/dockerRunner.sh
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-
-# Handle closing application on signal interrupt (ctrl + c)
-trap 'kill $CONTINUOUS_BUILD_PID $SERVER_PID; gradle --stop; exit' INT
-
-# Reset log file content for new application boot
-echo "*** Logs for 'gradle installBootDist --continuous' ***" > builder.log
-echo "*** Logs for 'gradle bootRun' ***" > runner.log
-
-# Print that the application is starting in watch mode
-echo "starting application in watch mode..."
-
-# Start the continious build listener process
-echo "starting continuous build listener..."
-gradle build --continuous 2>&1 | tee builder.log & CONTINUOUS_BUILD_PID=$!
-
-# Start server process once initial build finishes  
-( while ! grep -m1 'BUILD SUCCESSFUL' < builder.log; do
-    sleep 15
-done
-echo "starting crd server..."
-gradle bootRun 2>&1 | tee runner.log ) & SERVER_PID=$!
-
-# Handle application background process exiting
-wait $CONTINUOUS_BUILD_PID $SERVER_PID
-EXIT_CODE=$?
-echo "application exited with exit code $EXIT_CODE..."
\ No newline at end of file
