Reverted out QuestionnaireScript changes
This commit is contained in:
parent
4810ed3e0e
commit
eba23cba20
|
@ -352,9 +352,9 @@ public class CapabilityStatementUtilities {
|
|||
* Selects whichever code exists if only one exists, otherwise checks that the two codes match and merges conformance expectations
|
||||
*/
|
||||
private Enumeration merge(Enumeration targetCode, Enumeration importedCode, String maxConformance, String context) throws FHIRException {
|
||||
if (targetCode == null)
|
||||
if (targetCode == null || targetCode.getCode() == null)
|
||||
return (Enumeration)fixMax(importedCode, maxConformance);
|
||||
else if (importedCode == null)
|
||||
else if (importedCode == null || importedCode.getCode() == null)
|
||||
return targetCode;
|
||||
else if (targetCode.getValue().equals(importedCode.getValue())) {
|
||||
mergeExpectations(targetCode, importedCode, maxConformance);
|
||||
|
|
|
@ -279,7 +279,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
|
|||
private int expandCodesLimit = 1000;
|
||||
protected org.hl7.fhir.r5.context.ILoggingService logger = new SystemOutLoggingService();
|
||||
protected Parameters expParameters;
|
||||
protected Map<String, PackageInformation> packages = new HashMap<>();
|
||||
private Map<String, PackageInformation> packages = new HashMap<>();
|
||||
|
||||
@Getter
|
||||
protected TerminologyCache txCache = new TerminologyCache(this, null);
|
||||
|
|
|
@ -536,7 +536,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
|
|||
for (PackageResourceInformation pri : pi.listIndexedResources(types)) {
|
||||
if (!pri.getFilename().contains("ig-r4") && (loader == null || loader.wantLoad(pi, pri))) {
|
||||
try {
|
||||
if (!pri.hasId() || pri.getResourceType().equals("Basic")) {
|
||||
if (!pri.hasId()) {
|
||||
loadDefinitionItem(pri.getFilename(), ManagedFileAccess.inStream(pri.getFilename()), loader, null, pii);
|
||||
} else {
|
||||
registerResourceFromPackage(new PackageResourceLoader(pri, loader, pii), pii);
|
||||
|
@ -830,7 +830,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
|
|||
|
||||
@Override
|
||||
public PackageInformation getPackage(String id, String ver) {
|
||||
return packages.get(id + "#" + ver);
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isAllowLazyLoading() {
|
||||
|
|
|
@ -214,7 +214,6 @@ public class ToolingExtensions {
|
|||
public static final String EXT_Q_HIDDEN = "http://hl7.org/fhir/StructureDefinition/questionnaire-hidden";
|
||||
public static final String EXT_Q_OTP_DISP = "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-optionalDisplay";
|
||||
public static final String EXT_O_LINK_PERIOD = "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-observationLinkPeriod";
|
||||
public static final String EXT_O_EXTRACTION_RESOURCE = "http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-extractionResource";
|
||||
public static final String EXT_Q_CHOICE_ORIENT = "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation";
|
||||
public static final String EXT_Q_DISPLAY_CAT = "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory";
|
||||
public static final String EXT_REND_MD = "http://hl7.org/fhir/StructureDefinition/rendering-markdown";
|
||||
|
|
|
@ -768,14 +768,11 @@ public class NpmPackage {
|
|||
|
||||
public List<PackageResourceInformation> listIndexedResources(List<String> types) throws IOException {
|
||||
List<PackageResourceInformation> res = new ArrayList<PackageResourceInformation>();
|
||||
List<String> basicTypes = new ArrayList<String>();
|
||||
List<String> effectiveTypes = effectiveTypes(types);
|
||||
for (NpmPackageFolder folder : folders.values()) {
|
||||
JsonObject index = folder.index();
|
||||
if (index != null) {
|
||||
for (JsonObject fi : index.getJsonObjects("files")) {
|
||||
String resourceType = fi.asString("resourceType");
|
||||
if (Utilities.existsInList(resourceType, effectiveTypes) || types.isEmpty()) {
|
||||
if (Utilities.existsInList(fi.asString("resourceType"), types) || types.isEmpty()) {
|
||||
res.add(new PackageResourceInformation(folder.folder == null ? "@"+folder.getFolderName() : folder.folder.getAbsolutePath(), fi));
|
||||
}
|
||||
}
|
||||
|
@ -785,17 +782,6 @@ public class NpmPackage {
|
|||
return res;
|
||||
}
|
||||
|
||||
private List<String> effectiveTypes(List<String> types) {
|
||||
List<String> effective = new ArrayList<String>();
|
||||
for (String type: types) {
|
||||
if (type.startsWith("Basic-"))
|
||||
effective.add("Basic");
|
||||
else
|
||||
effective.add(type);
|
||||
}
|
||||
return effective;
|
||||
}
|
||||
|
||||
/**
|
||||
* use the name from listResources()
|
||||
*
|
||||
|
|
|
@ -9,7 +9,6 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
@ -23,8 +22,6 @@ import java.util.Set;
|
|||
import java.util.UUID;
|
||||
|
||||
import org.fhir.ucum.UcumEssenceService;
|
||||
import org.hl7.fhir.QuestionnaireItem;
|
||||
import org.hl7.fhir.convertors.conv43_50.datatypes43_50.special43_50.Reference43_50;
|
||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50;
|
||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50;
|
||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50;
|
||||
|
@ -39,7 +36,6 @@ import org.hl7.fhir.r5.context.IWorkerContextManager;
|
|||
import org.hl7.fhir.r5.context.SimpleWorkerContext;
|
||||
import org.hl7.fhir.r5.context.SystemOutLoggingService;
|
||||
import org.hl7.fhir.r5.elementmodel.*;
|
||||
import org.hl7.fhir.r5.elementmodel.Element;
|
||||
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
||||
import org.hl7.fhir.r5.fhirpath.ExpressionNode;
|
||||
import org.hl7.fhir.r5.fhirpath.FHIRPathEngine;
|
||||
|
@ -47,9 +43,22 @@ import org.hl7.fhir.r5.formats.FormatUtilities;
|
|||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.formats.JsonParser;
|
||||
import org.hl7.fhir.r5.formats.XmlParser;
|
||||
import org.hl7.fhir.r5.model.*;
|
||||
import org.hl7.fhir.r5.model.Base;
|
||||
import org.hl7.fhir.r5.model.Bundle;
|
||||
import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.r5.renderers.ObligationsRenderer;
|
||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||
import org.hl7.fhir.r5.model.CanonicalType;
|
||||
import org.hl7.fhir.r5.model.Coding;
|
||||
import org.hl7.fhir.r5.model.DomainResource;
|
||||
import org.hl7.fhir.r5.model.ElementDefinition;
|
||||
import org.hl7.fhir.r5.model.ImplementationGuide;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome;
|
||||
import org.hl7.fhir.r5.model.PackageInformation;
|
||||
import org.hl7.fhir.r5.model.Parameters;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureMap;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.renderers.RendererFactory;
|
||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
||||
import org.hl7.fhir.r5.renderers.utils.ResourceWrapper;
|
||||
|
@ -85,7 +94,6 @@ import org.hl7.fhir.utilities.http.ManagedWebAccess;
|
|||
import org.hl7.fhir.utilities.npm.CommonPackages;
|
||||
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
|
||||
import org.hl7.fhir.utilities.npm.NpmPackage;
|
||||
import org.hl7.fhir.utilities.npm.PackageInfo;
|
||||
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlComposer;
|
||||
import org.hl7.fhir.validation.BaseValidator.ValidationControl;
|
||||
|
@ -1033,557 +1041,6 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
|
|||
return bs.toByteArray();
|
||||
}
|
||||
|
||||
public Questionnaire genScriptQuestionnaire(String igPackageId, String canonical) throws FHIRException, IOException, Exception {
|
||||
String[] packageParts = igPackageId.split("#");
|
||||
PackageInformation p = context.getPackage(packageParts[0], packageParts[1]);
|
||||
String igCanonical = p.getCanonical();
|
||||
ImplementationGuide ig = (ImplementationGuide)context.fetchResource(ImplementationGuide.class, igCanonical + "/ImplementationGuide/" + packageParts[0]);
|
||||
if (ig == null)
|
||||
throw new FHIRException("Unable to load/find implementation guide " + igPackageId);
|
||||
|
||||
Questionnaire q = new Questionnaire();
|
||||
q.setUrl(canonical);
|
||||
q.setName(ig.getName() + "TestScriptQuestionnaire");
|
||||
q.setTitle("Test Script Generation Questionnaire for " + ig.getTitle() + " IG");
|
||||
q.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
q.setDescription("A Questionnaire intended to gather information about the functionality a specific system has implemented from the '" +
|
||||
ig.getTitle() + " implementation guide. QuestionnaireResponses can then go through an 'extraction' process to produce a " +
|
||||
"TestScript designed to test only the implemented functionality");
|
||||
q.addSubjectType("Endpoint");
|
||||
q.addSubjectType("Device");
|
||||
q.setDate(Date.from(Instant.now()));
|
||||
|
||||
TestScript script = new TestScript();
|
||||
q.addContained(script);
|
||||
q.addExtension(ToolingExtensions.EXT_O_EXTRACTION_RESOURCE, new Reference("#script"));
|
||||
script.setId("script");
|
||||
script.setUrl(canonical.replace("Questionnaire","TestScript"));
|
||||
// Todo: Make name and description generate
|
||||
script.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||
StringType title = new StringType("test");
|
||||
Expression titleExp = new Expression().setLanguage("text/fhirpath").setExpression("%qr.item.where(linkId='title').answer & ' Test Script for '" +
|
||||
ig.getTitle() + " Implementation Guide'");
|
||||
title.addExtension(ToolingExtensions.EXT_CQF_EXP, titleExp);
|
||||
script.setTitleElement(title);
|
||||
MarkdownType descMd = new MarkdownType("test");
|
||||
Expression descExp = new Expression().setLanguage("text/fhirpath").setExpression("'A Test script verifying that the ' & %qr.item.where(linkId='title').answer & ' system complies with the " +
|
||||
ig.getTitle() + " implementation guide'");
|
||||
descMd.addExtension(ToolingExtensions.EXT_CQF_EXP, descExp);
|
||||
script.setDescriptionElement(descMd);
|
||||
|
||||
|
||||
|
||||
Map<String, CapabilityStatement> capabilities = new HashMap<String,CapabilityStatement>();
|
||||
List<CapabilityStatement> usedCapabilities = new ArrayList<CapabilityStatement>();
|
||||
List<ActorDefinition> actors = new ArrayList<ActorDefinition>();
|
||||
for (ImplementationGuide.ImplementationGuideDefinitionResourceComponent r: ig.getDefinition().getResource()) {
|
||||
if (r.getReference().hasReference()) {
|
||||
if (r.getReference().getReference().startsWith("CapabilityStatement")) {
|
||||
CapabilityStatement cap = (CapabilityStatement) context.fetchResource(CapabilityStatement.class, igCanonical + "/" + r.getReference().getReference());
|
||||
if (cap == null)
|
||||
throw new FHIRException("Unable to find CapabilityStatement " + r.getReference().getReference() + " in IG " + igPackageId);
|
||||
capabilities.put(cap.getUrl(), cap);
|
||||
} else if (r.getReference().getReference().startsWith("ActorDefinition")) {
|
||||
ActorDefinition ad = (ActorDefinition) context.fetchResource(ActorDefinition.class, igCanonical + "/" + r.getReference().getReference());
|
||||
if (ad == null)
|
||||
throw new FHIRException("Unable to find ActorDefinition " + r.getReference().getReference() + " in IG " + igPackageId);
|
||||
actors.add(ad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actors.isEmpty())
|
||||
throw new FHIRException("No ActorDefinitions found in IG " + igPackageId + ". TestScript generation depends on the presence of actor-specific obligations.");
|
||||
if (capabilities.isEmpty())
|
||||
throw new FHIRException("No CapabilityStatements found in IG " + igPackageId + ". TestScript generation depends on the presence of system-specific capabilities.");
|
||||
|
||||
Map<String, ActorDefinition> capabilityActors = new HashMap<String, ActorDefinition>();
|
||||
List<String> actorUrls = new ArrayList<String>();
|
||||
for (ActorDefinition actor: actors) {
|
||||
if (actor.hasCapabilities()) {
|
||||
if (capabilities.containsKey(actor.getCapabilities())) {
|
||||
CapabilityStatement cap = capabilities.get(actor.getCapabilities());
|
||||
usedCapabilities.add(cap);
|
||||
capabilityActors.put(cap.getUrl(), actor);
|
||||
actorUrls.add(actor.getUrl());
|
||||
} else
|
||||
throw new FHIRException("ActorDefinition " + actor.getUrl() + " refers to CapabilityStatement that is not defined in the same guide - " + actor.getCapabilities());
|
||||
} else
|
||||
System.out.println("ActorDefinition " + actor.getUrl() + " ignored because it is not associated with capabilities");
|
||||
}
|
||||
|
||||
Questionnaire.QuestionnaireItemComponent sysItem = q.addItem().setLinkId("title").setType(Questionnaire.QuestionnaireItemType.STRING).setRequired(true).setRepeats(true);
|
||||
sysItem.setText("Enter the name of the system for which the test script is being generated");
|
||||
Questionnaire.QuestionnaireItemComponent capItem = q.addItem().setLinkId("capabilities").setType(Questionnaire.QuestionnaireItemType.CODING).setRequired(true).setRepeats(true);
|
||||
capItem.setText("Which of the the following CapabilityStatements does your system implement?");
|
||||
for (CapabilityStatement cap: usedCapabilities) {
|
||||
capItem.addAnswerOption().setValue(new Coding("urn:ietf:rfc:3986", cap.getUrl(), cap.getTitle()));
|
||||
}
|
||||
Questionnaire.QuestionnaireItemComponent actorItem = q.addItem().setLinkId("actors").setType(Questionnaire.QuestionnaireItemType.CODING).setRequired(true).setRepeats(true);
|
||||
actorItem.setText("Which of the the following ActorDefinitions does your system implement?");
|
||||
actorItem.setReadOnly(true);
|
||||
for (ActorDefinition actor: actors) {
|
||||
Coding actorCoding = new Coding("urn:ietf:rfc:3986", actor.getUrl(), actor.getTitle());
|
||||
actorItem.addAnswerOption().setValue(actorCoding);
|
||||
actorItem.addInitial().setValue(actorCoding);
|
||||
}
|
||||
int capNum = 1;
|
||||
for (CapabilityStatement cap: usedCapabilities) {
|
||||
Questionnaire.QuestionnaireItemComponent capGroup = q.addItem().setLinkId("capabilities" + capNum).setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
capGroup.setText("Implementation Details for " + cap.getTitle() + " Capabilities");
|
||||
Questionnaire.QuestionnaireItemEnableWhenComponent ew = capGroup.addEnableWhen();
|
||||
ew.setQuestion("capabilities").setOperator(Questionnaire.QuestionnaireItemOperator.EQUAL).setAnswer(new Coding("urn:ietf:rfc:3986", cap.getUrl(), cap.getTitle()));
|
||||
processQuestionnaireCapabilities(capGroup, cap, capabilityActors.get(cap.getUrl()), actorUrls);
|
||||
capNum++;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
If all of the capabilities require something, fix it to required/read-only. If some of the capabilities require something, make it conditionally required/read-only
|
||||
If any of the capabilities require something, it's required. Otherwise, if something is optional for any of the capabilities, then it's optional
|
||||
|
||||
|
||||
Look for min and max examples for each profile
|
||||
*/
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
private void processQuestionnaireCapabilities(Questionnaire.QuestionnaireItemComponent capGroup, CapabilityStatement cap, ActorDefinition actor, List<String> actorUrls) {
|
||||
if (!cap.hasRest()) {
|
||||
System.out.println("Capability Statement " + cap.getUrl() + " is ignored as it has no REST features. Testing of messages, documents, and other exchanges is not yet supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (CapabilityStatement.CapabilityStatementRestComponent rest: cap.getRest()) {
|
||||
List<String> resources = new ArrayList<String>();
|
||||
Map<String, CapabilityStatement.CapabilityStatementRestResourceComponent> resourceComponents = new HashMap<String, CapabilityStatement.CapabilityStatementRestResourceComponent>();
|
||||
Map<String, List<StructureDefinition>> resourceProfiles = new HashMap<String, List<StructureDefinition>>();
|
||||
|
||||
Questionnaire.QuestionnaireItemComponent inst = capGroup.addItem().setLinkId(capGroup.getLinkId()+ "inst" + rest.getMode()).setType(Questionnaire.QuestionnaireItemType.DISPLAY);
|
||||
inst.setText("Indicate which resources are supported, and for each supported, resource, which interaction types are supported");
|
||||
inst.addExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT, Factory.newCodeableConcept("instructions", "http://hl7.org/fhir/questionnaire-item-control", "Instructions"));
|
||||
Questionnaire.QuestionnaireItemComponent restItem = capGroup.addItem().setLinkId(capGroup.getLinkId()+rest.getMode()).setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
restItem.setText(rest.getMode().getDisplay() + " Capabilities");
|
||||
restItem.addExtension(ToolingExtensions.EXT_CONTROL, Factory.newCodeableConcept("grid", "http://hl7.org/fhir/questionnaire-item-control", "Grid"));
|
||||
|
||||
for (CapabilityStatement.CapabilityStatementRestResourceComponent r: rest.getResource()) {
|
||||
if (!r.hasExtension(ToolingExtensions.EXT_CAP_STMT_EXPECT)) {
|
||||
System.out.println("Ignoring resource " + r.getType() + " for CapabilityStatement " + cap.getUrl() + " because it does not declare SHALL/SHOULD/MAY - and that is required for testing purposes.");
|
||||
} else if (!r.hasSupportedProfile()) {
|
||||
System.out.println("Ignoring resource " + r.getType() + " for CapabilityStatement " + cap.getUrl() + " because it does not declare a profile - and that is required for testing purposes.");
|
||||
} else {
|
||||
if (!resources.contains(r.getType())) {
|
||||
resources.add(r.getType());
|
||||
resourceComponents.put(r.getType(), r);
|
||||
List<StructureDefinition> profiles = new ArrayList<StructureDefinition>();
|
||||
for (CanonicalType canonical: r.getSupportedProfile()) {
|
||||
StructureDefinition profile = context.fetchResource(StructureDefinition.class, canonical.getValue());
|
||||
if (profile==null)
|
||||
throw new FHIRException("Unable to find profile " + canonical.getValue() + " referenced in CapabilityStatement " + cap.getUrl());
|
||||
profiles.add(profile);
|
||||
}
|
||||
resourceProfiles.put(r.getType(), profiles);
|
||||
}
|
||||
Questionnaire.QuestionnaireItemComponent resourceGrp = restItem.addItem().setLinkId(restItem.getLinkId() + r.getType()).setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
resourceGrp.setText(r.getType());
|
||||
Questionnaire.QuestionnaireItemComponent resourceItem = resourceGrp.addItem().setLinkId(resourceGrp.getLinkId() + "-supported").setType(Questionnaire.QuestionnaireItemType.BOOLEAN).setRequired(true).setRepeats(false);
|
||||
// resourceItem.addExtension(ToolingExtensions.EXT_CONTROL, Factory.newCodeableConcept("check-box", "http://hl7.org/fhir/questionnaire-item-control", "Check-box"));
|
||||
resourceItem.setText("Supported?");
|
||||
CodeType conformance = r.getExtensionByUrl(ToolingExtensions.EXT_CAP_STMT_EXPECT).getValueCodeType();
|
||||
if (conformance.getCode().equals("SHALL")) {
|
||||
resourceItem.setReadOnly(true);
|
||||
resourceItem.addInitial().setValue(new BooleanType(true));
|
||||
}
|
||||
/* In theory, all of these can be tested, but going to start with simpler tests for now */
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "read");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "vread");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "update");
|
||||
/* questionnaireResourceInteraction(resourceGrp, resourceItem, r, "update-conditional");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "patch");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "patch-conditional");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "delete");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "delete-conditional-single");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "delete-conditional-multiple");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "delete-history");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "delete-history-version");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "history-instance");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "history-type");*/
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "create");
|
||||
// questionnaireResourceInteraction(resourceGrp, resourceItem, r, "create-conditional");
|
||||
questionnaireResourceInteraction(resourceGrp, resourceItem, r, "search-type");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Questionnaire.QuestionnaireItemComponent inst2 = capGroup.addItem().setLinkId(capGroup.getLinkId()+ "inst" + rest.getMode()).setType(Questionnaire.QuestionnaireItemType.DISPLAY);
|
||||
inst2.setText("For each of the following resource profiles, for each element indicate which optional obligations are supported.");
|
||||
inst2.addExtension(ToolingExtensions.EXT_Q_DISPLAY_CAT, Factory.newCodeableConcept("instructions", "http://hl7.org/fhir/questionnaire-item-control", "Instructions"));
|
||||
Questionnaire.QuestionnaireItemComponent profilesGrp = capGroup.addItem().setLinkId(restItem.getLinkId() + "profiles").setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
profilesGrp.setText("Profiles support");
|
||||
|
||||
resources.sort(new Utilities.CaseInsensitiveSorter());
|
||||
for (String resourceName: resources) {
|
||||
List<String> obligationElementPaths = new ArrayList<String>();
|
||||
Map<String, TestingObligations> obligations = new HashMap<String, TestingObligations>();
|
||||
List<StructureDefinition> profiles = resourceProfiles.get(resourceName);
|
||||
|
||||
for (StructureDefinition profile: profiles) {
|
||||
// Get list of obligations declared on the resource rather than on particular elements.
|
||||
List<Extension> globals = profile.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION_CORE);
|
||||
Map<String, List<Extension>> globalsById = new HashMap<String, List<Extension>>();
|
||||
for (Extension ext: globals) {
|
||||
for (Extension element: ext.getExtensionsByUrl("elementId")) {
|
||||
String elementId = element.getValueStringType().toString();
|
||||
List<Extension> obligationList = globalsById.get(elementId);
|
||||
if (obligationList==null) {
|
||||
obligationList = new ArrayList<Extension>();
|
||||
globalsById.put(elementId, obligationList);
|
||||
}
|
||||
obligationList.add(ext);
|
||||
}
|
||||
}
|
||||
|
||||
boolean foundObligations = false;
|
||||
// NOTE: This process presumes that if the path is the same, the element is equivalent. In theory it's possible for slice
|
||||
// names to be the same in multiple profiles defined in a single IG to actually correspond to different discriminators and/or different discriminator values.
|
||||
// However, doing that is a super confusing thing for implementers and this code presumes IG authors won't be that foolish/sadistic.
|
||||
for (ElementDefinition e: profile.getSnapshot().getElement()) {
|
||||
TestingObligations testingObligations;
|
||||
if (obligationElementPaths.contains(e.getPath())) {
|
||||
testingObligations = obligations.get(e.getPath());
|
||||
} else
|
||||
testingObligations = new TestingObligations(actorUrls);
|
||||
boolean foundElementObligations = testingObligations.processObligations(e);
|
||||
boolean foundGlobalObligations = testingObligations.processObligations(globalsById.get(e.getPath()));
|
||||
foundObligations = foundObligations || foundElementObligations || foundGlobalObligations;
|
||||
if (!testingObligations.isEmpty() && !obligationElementPaths.contains(e.getPath())) {
|
||||
obligationElementPaths.add(e.getPath());
|
||||
obligations.put(e.getPath(), testingObligations);
|
||||
}
|
||||
}
|
||||
if (!foundObligations) {
|
||||
System.out.println("Not exposing profile " + profile.getTitle() + " because there are no obligations defined in the profile and obligations are required for testing");
|
||||
} else if (obligations.isEmpty()) {
|
||||
System.out.println("Not exposing resource " + profile.getTitle() + " because none of the found obligations were applicable for testing for the in-scope ActorDefinitions");
|
||||
}
|
||||
}
|
||||
Questionnaire.QuestionnaireItemComponent profileGrp = profilesGrp.addItem().setLinkId(restItem.getLinkId() + "profiles").setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
profileGrp.setText(resourceName + "(Amalgamated across " + profiles.size() + " profiles + ) Elements");
|
||||
profileGrp.addExtension(ToolingExtensions.EXT_CONTROL, Factory.newCodeableConcept("grid", "http://hl7.org/fhir/questionnaire-item-control", "Grid"));
|
||||
Questionnaire.QuestionnaireItemEnableWhenComponent ew = profileGrp.addEnableWhen();
|
||||
ew.setQuestion(restItem.getLinkId() + resourceName + "-supported").setOperator(Questionnaire.QuestionnaireItemOperator.EQUAL).setAnswer(new BooleanType(true));
|
||||
|
||||
// While alphabetic sorting kind of sucks, because we're grabbing elements from multiple profiles, each of which may have their own slicing hierarchy, there isn't really a better way.
|
||||
obligationElementPaths.sort(new Utilities.CaseInsensitiveSorter());
|
||||
Questionnaire.QuestionnaireItemComponent setAllGrp = null;
|
||||
List<String> allCodes = new ArrayList<String>();
|
||||
if (obligationElementPaths.size() > 2) {
|
||||
setAllGrp = profileGrp.addItem().setLinkId(profileGrp.getLinkId() + "SETALL").setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
setAllGrp.setText("Set/Clear all");
|
||||
}
|
||||
for (String path: obligationElementPaths) {
|
||||
Questionnaire.QuestionnaireItemComponent elementGrp = profileGrp.addItem().setLinkId(profileGrp.getLinkId() + path).setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
elementGrp.setText(path);
|
||||
|
||||
TestingObligations testingObligations = obligations.get(path);
|
||||
allCodes.addAll(testingObligations.getCodes());
|
||||
for (String oCode: testingObligations.getCodes()) {
|
||||
Questionnaire.QuestionnaireItemComponent obligationItem = elementGrp.addItem().setLinkId(elementGrp.getLinkId() + "-" + oCode).setType(Questionnaire.QuestionnaireItemType.BOOLEAN).setRequired(true).setRepeats(false);
|
||||
// Todo: Use displayname instead
|
||||
obligationItem.setText(oCode);
|
||||
if (testingObligations.isAlwaysRequired(oCode)) {
|
||||
obligationItem.addInitial().setValue(new BooleanType(true));
|
||||
obligationItem.setReadOnly(true);
|
||||
} else if (testingObligations.isConditionallyRequired(oCode)) {
|
||||
// todo
|
||||
String expression = "%resource.item.where(linkId='actors').answer.value.where(";
|
||||
boolean first = true;
|
||||
for (String actorUrl: testingObligations.requiredActors(oCode)) {
|
||||
expression += "code='" + actorUrl + "'";
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
expression += " or ";
|
||||
}
|
||||
expression += ").exists()";
|
||||
Expression expr = new Expression().setLanguage("text/fhirpath").setExpression(expression);
|
||||
BooleanType b = new BooleanType();
|
||||
b.addExtension(new Extension(ToolingExtensions.EXT_CQF_EXP, expr));
|
||||
obligationItem.addInitial().setValue(b);
|
||||
obligationItem.setReadOnlyElement(b);
|
||||
} else if (setAllGrp != null) {
|
||||
// todo handle descendants
|
||||
Expression expr = new Expression().setLanguage("text/fhirpath").setExpression("%resource.item.where(linkId='" + setAllGrp.getLinkId() + oCode + "').answer.value");
|
||||
obligationItem.addInitial().addExtension(ToolingExtensions.EXT_CQF_EXP, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if ()
|
||||
profileGrp = profilesGrp.addItem().setLinkId(restItem.getLinkId() + "profiles").setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
profileGrp.setText(resourceName + "(Amalgamated across " + profiles.size() + " profiles + ) Elements");
|
||||
}
|
||||
|
||||
/* for (CapabilityStatement.CapabilityStatementRestResourceComponent r: rest.getResource()) {
|
||||
if (r.hasExtension(ToolingExtensions.EXT_CAP_STMT_EXPECT)) {
|
||||
Questionnaire.QuestionnaireItemComponent resourceGrp = restItem.addItem().setLinkId(restItem.getLinkId() + r.getType()).setType(Questionnaire.QuestionnaireItemType.GROUP).setRequired(true).setRepeats(false);
|
||||
}
|
||||
}*/
|
||||
|
||||
if (!restItem.hasItem()) {
|
||||
// No questions about this rest mode, so drop it
|
||||
capGroup.getItem().remove(restItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes the set of obligations associated with an element and exposes information about the specific testable
|
||||
* obligation codes that apply, as well as to which actors and whether those obligations are optional or mandatory
|
||||
*/
|
||||
class TestingObligations {
|
||||
private List<String> actorUrls;
|
||||
private boolean hadObligations = false;
|
||||
private List<String> codes = new ArrayList<String>();
|
||||
private boolean sorted = false;
|
||||
private Map<String, Map<String,ActorObligation>> details = new HashMap<>();
|
||||
|
||||
/*
|
||||
* Class for capturing the details about a specific testable obligation code for a specific actor
|
||||
*/
|
||||
private class ActorObligation {
|
||||
String actorUrl;
|
||||
String code;
|
||||
boolean optional;
|
||||
|
||||
ActorObligation(String originalCode, String filteredCode, String actorUrl) {
|
||||
this.actorUrl = actorUrl;
|
||||
this.code = filteredCode;
|
||||
optional = checkOptional(originalCode);
|
||||
}
|
||||
|
||||
/*
|
||||
* In cases where the same code is declared for the same actor more than once, the stricter optionality applies
|
||||
*/
|
||||
public void updateOptionality(String originalCode) {
|
||||
optional = optional && checkOptional(originalCode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Obligatins are optional if they are SHOULD or MAY or if the obligation code is itself intrinsically optional
|
||||
*/
|
||||
private boolean checkOptional(String originalCode) {
|
||||
return originalCode.startsWith("SHOULD:") || originalCode.startsWith("MAY:") || originalCode.endsWith(":able-to-populate");
|
||||
}
|
||||
|
||||
public boolean isOptional() {
|
||||
return optional;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We take in the list of actor ids that are relevant for this particular element
|
||||
*/
|
||||
TestingObligations(List<String> actorUrls) {
|
||||
this.actorUrls = actorUrls;
|
||||
}
|
||||
|
||||
/*
|
||||
* Captures the computable obligation codes for the obligations on the specified element as well as the actors they apply to and whether
|
||||
* the obligations are optional or not
|
||||
*/
|
||||
boolean processObligations(ElementDefinition e) {
|
||||
return processObligations(e.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION_CORE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Captures the computable obligation codes for the specified obligations as well as the actors they apply to and whether
|
||||
* the obligations are optional or not
|
||||
*/
|
||||
boolean processObligations(List<Extension> obligations) {
|
||||
if (obligations==null || obligations.isEmpty())
|
||||
return false;
|
||||
|
||||
hadObligations = true;
|
||||
for (Extension obligation: obligations) {
|
||||
processObligation(obligation);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Captures the computable obligation codes for the specified obligation as well as the actors they apply to and whether
|
||||
* the obligations are optional or not
|
||||
*/
|
||||
protected void processObligation(Extension ext) {
|
||||
ObligationsRenderer.ObligationDetail obligation = new ObligationsRenderer.ObligationDetail(ext);
|
||||
|
||||
if (obligation.hasFilter()) {
|
||||
context.getLogger().logDebugMessage(ILoggingService.LogCategory.CONTEXT, "Obligations with filters are ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
if (obligation.hasUsage()) {
|
||||
context.getLogger().logDebugMessage(ILoggingService.LogCategory.CONTEXT, "Obligations with usage constraints are ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!inActors(obligation))
|
||||
return;
|
||||
|
||||
for (String originalCode: obligation.getCodeList()) {
|
||||
String filteredCode = filter(originalCode);
|
||||
Map<String, ActorObligation> actorObs;
|
||||
if (filteredCode==null) {
|
||||
continue;
|
||||
} else if (codes.contains(filteredCode)) {
|
||||
actorObs = details.get(filteredCode);
|
||||
} else {
|
||||
actorObs = new HashMap<String, ActorObligation>();
|
||||
details.put(filteredCode, actorObs);
|
||||
codes.add(filteredCode);
|
||||
sorted = false;
|
||||
}
|
||||
processDetail(obligation, actorObs, originalCode, filteredCode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the list of associated actor expectations for the specified code
|
||||
*/
|
||||
private void processDetail(ObligationsRenderer.ObligationDetail obligation, Map<String, ActorObligation> actorObs, String originalCode, String filteredCode) {
|
||||
for (String actorUrl: actorUrls) {
|
||||
if (obligation.hasActor(actorUrl)) {
|
||||
if (actorObs.containsKey(actorUrl)) {
|
||||
ActorObligation actorOb = actorObs.get(actorUrl);
|
||||
actorOb.updateOptionality(originalCode);
|
||||
} else {
|
||||
ActorObligation actorOb = new ActorObligation(originalCode, filteredCode, actorUrl);
|
||||
actorObs.put(actorUrl, actorOb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the specified obligation applies to any of the in-scope actors for the associated element
|
||||
*/
|
||||
private boolean inActors(ObligationsRenderer.ObligationDetail obligation) {
|
||||
for (String actorUrl: actorUrls) {
|
||||
if (obligation.hasActor(actorUrl))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a code without the 'conformance' prefix and turns intrinsically 'optional' obligation codes into their
|
||||
* equivalent non-optional code
|
||||
*/
|
||||
private String filter(String code) {
|
||||
String baseCode = code.substring(code.indexOf(":")+1);
|
||||
switch (baseCode) {
|
||||
case "able-to-populate":
|
||||
case "alter":
|
||||
case "process":
|
||||
return null;
|
||||
case "populate-if-known":
|
||||
return "populate";
|
||||
}
|
||||
|
||||
return baseCode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if obligations were present on the associated element, even if none were in scope
|
||||
*/
|
||||
public boolean hadObligations() {
|
||||
return this.hadObligations;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if there were no in-scope obligations
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return codes.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the 'simple' testable obligation codes that hold for the element with this element
|
||||
*/
|
||||
public List<String> getCodes() {
|
||||
if (!sorted)
|
||||
codes.sort(new Utilities.CaseInsensitiveSorter());
|
||||
sorted = true;
|
||||
return codes;
|
||||
}
|
||||
|
||||
public boolean isAlwaysRequired(String code) {
|
||||
Map<String, ActorObligation> actorObs = details.get(code);
|
||||
boolean alwaysRequired = true;
|
||||
|
||||
for (ActorObligation actorOb: actorObs.values()) {
|
||||
if (actorOb.isOptional())
|
||||
return false;
|
||||
}
|
||||
|
||||
return alwaysRequired;
|
||||
}
|
||||
|
||||
public boolean isConditionallyRequired(String code) {
|
||||
Map<String, ActorObligation> actorObs = details.get(code);
|
||||
boolean usesRequired = false;
|
||||
boolean usesOptional = false;
|
||||
|
||||
for (ActorObligation actorOb: actorObs.values()) {
|
||||
if (actorOb.isOptional())
|
||||
usesOptional = true;
|
||||
else
|
||||
usesRequired = true;
|
||||
}
|
||||
|
||||
return (usesOptional==usesRequired);
|
||||
}
|
||||
|
||||
public List<String> requiredActors(String code) {
|
||||
List<String> list = new ArrayList<String>();
|
||||
Map<String, ActorObligation> actorObs = details.get(code);
|
||||
for (ActorObligation actorOb: actorObs.values()) {
|
||||
if (!actorOb.isOptional())
|
||||
list.add(actorOb.actorUrl);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* todo
|
||||
*/
|
||||
public void questionnaireResourceInteraction(Questionnaire.QuestionnaireItemComponent resourceGrp, Questionnaire.QuestionnaireItemComponent resourceItem, CapabilityStatement.CapabilityStatementRestResourceComponent r, String interactionCode) {
|
||||
for (CapabilityStatement.ResourceInteractionComponent interaction: r.getInteraction()) {
|
||||
if (interaction.getCode().toCode().equals(interactionCode)) {
|
||||
Questionnaire.QuestionnaireItemComponent interactionItem = resourceGrp.addItem().setLinkId(resourceGrp.getLinkId() + interactionCode).setType(Questionnaire.QuestionnaireItemType.BOOLEAN).setRequired(true).setRepeats(false);
|
||||
// interactionItem.addExtension(ToolingExtensions.EXT_CONTROL, Factory.newCodeableConcept("check-box", "http://hl7.org/fhir/questionnaire-item-control", "Check-box"));
|
||||
interactionItem.setText(interaction.getCode().toCode());
|
||||
Questionnaire.QuestionnaireItemEnableWhenComponent ew = interactionItem.addEnableWhen();
|
||||
ew.setQuestion(resourceItem.getLinkId()).setOperator(Questionnaire.QuestionnaireItemOperator.EQUAL).setAnswer(new BooleanType(true));
|
||||
CodeType conformance = interaction.getExtensionByUrl(ToolingExtensions.EXT_CAP_STMT_EXPECT).getValueCodeType();
|
||||
if (conformance.getCode().equals("SHALL")) {
|
||||
interactionItem.setReadOnly(true);
|
||||
interactionItem.addInitial().setValue(new BooleanType(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* todo
|
||||
*/
|
||||
public void questionnaireExtract(String source, String outputDir, FhirFormat format) throws FHIRException, IOException, Exception {
|
||||
Content cnt = igLoader.loadContent(source, "questionnaireExtract", false, true);
|
||||
org.hl7.fhir.r5.elementmodel.Element src = Manager.parseSingle(context, new ByteArrayInputStream(cnt.getFocus().getBytes()), cnt.getCntType());
|
||||
}
|
||||
|
||||
private String getMapId(String type, String targetVer) {
|
||||
if (VersionUtilities.isR2Ver(version)) {
|
||||
if (VersionUtilities.isR3Ver(targetVer)) {
|
||||
|
|
|
@ -135,8 +135,6 @@ public class ValidatorCli {
|
|||
new TxTestsTask(),
|
||||
new TransformTask(),
|
||||
new VersionTask(),
|
||||
new QuestionnaireExtractTask(),
|
||||
new TestScriptQuestionnaireTask(),
|
||||
defaultCliTask);
|
||||
}
|
||||
|
||||
|
@ -340,10 +338,6 @@ public class ValidatorCli {
|
|||
res.add("5.0");
|
||||
res.add("-ig");
|
||||
res.add("hl7.fhir.uv.sql-on-fhir#current");
|
||||
} else if (a.equals("-questionnaire-extract") || a.equals("-script-questionnaire")) {
|
||||
res.add(a);
|
||||
res.add("-tx");
|
||||
res.add("n/a");
|
||||
} else {
|
||||
res.add(a);
|
||||
}
|
||||
|
|
|
@ -91,25 +91,25 @@ public class ValidatorUtils {
|
|||
return null;
|
||||
}
|
||||
if (VersionUtilities.isR2Ver(version)) {
|
||||
return new R2ToR5Loader(Utilities.strings("Conformance", "StructureDefinition", "ValueSet", "ActorDefinition", "ImplementationGuide", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader);
|
||||
return new R2ToR5Loader(Utilities.strings("Conformance", "StructureDefinition", "ValueSet", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader);
|
||||
}
|
||||
if (VersionUtilities.isR2BVer(version)) {
|
||||
return new R2016MayToR5Loader(Utilities.strings("Conformance", "StructureDefinition", "ValueSet", "Basic-ActorDefinition", "ActorDefinition", "ImplementationGuide", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader); // special case
|
||||
return new R2016MayToR5Loader(Utilities.strings("Conformance", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader); // special case
|
||||
}
|
||||
if (VersionUtilities.isR3Ver(version)) {
|
||||
return new R3ToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "Basic-ActorDefinition", "ActorDefinition", "ImplementationGuide", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader);
|
||||
return new R3ToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader);
|
||||
}
|
||||
if (VersionUtilities.isR4Ver(version)) {
|
||||
return new R4ToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "Basic-ActorDefinition", "ImplementationGuide", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader, version);
|
||||
return new R4ToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader, version);
|
||||
}
|
||||
if (VersionUtilities.isR4BVer(version)) {
|
||||
return new R4BToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "Basic-ActorDefinition", "ImplementationGuide", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader, version);
|
||||
return new R4BToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader, version);
|
||||
}
|
||||
if (VersionUtilities.isR5Ver(version)) {
|
||||
return new R5ToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "ActorDefinition", "ImplementationGuide", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader);
|
||||
return new R5ToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader);
|
||||
}
|
||||
if (VersionUtilities.isR6Ver(version)) {
|
||||
return new R6ToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "ActorDefinition", "ImplementationGuide", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader);
|
||||
return new R6ToR5Loader(Utilities.strings("CapabilityStatement", "StructureDefinition", "ValueSet", "CodeSystem", "SearchParameter", "OperationDefinition", "Questionnaire", "ConceptMap", "StructureMap", "NamingSystem"), loader);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -68,8 +68,6 @@ public class CliContext {
|
|||
private String map = null;
|
||||
@JsonProperty("output")
|
||||
private String output = null;
|
||||
@JsonProperty("outputCanonical")
|
||||
private String outputCanonical = null;
|
||||
@JsonProperty("outputSuffix")
|
||||
private String outputSuffix;
|
||||
@JsonProperty("htmlOutput")
|
||||
|
@ -441,17 +439,6 @@ public class CliContext {
|
|||
return this;
|
||||
}
|
||||
|
||||
@JsonProperty("outputCanonical")
|
||||
public String getOutputCanonical() {
|
||||
return outputCanonical;
|
||||
}
|
||||
|
||||
@JsonProperty("outputCanonical")
|
||||
public CliContext setOutputCanonical(String outputCanonical) {
|
||||
this.outputCanonical = outputCanonical;
|
||||
return this;
|
||||
}
|
||||
|
||||
@JsonProperty("outputSuffix")
|
||||
public String getOutputSuffix() {
|
||||
return outputSuffix;
|
||||
|
|
|
@ -26,7 +26,15 @@ import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
|
|||
import org.hl7.fhir.r5.elementmodel.ValidatedFragment;
|
||||
import org.hl7.fhir.r5.formats.IParser;
|
||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.model.*;
|
||||
import org.hl7.fhir.r5.model.Bundle;
|
||||
import org.hl7.fhir.r5.model.CanonicalResource;
|
||||
import org.hl7.fhir.r5.model.CodeSystem;
|
||||
import org.hl7.fhir.r5.model.ConceptMap;
|
||||
import org.hl7.fhir.r5.model.OperationOutcome;
|
||||
import org.hl7.fhir.r5.model.Resource;
|
||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||
import org.hl7.fhir.r5.model.StructureMap;
|
||||
import org.hl7.fhir.r5.model.ValueSet;
|
||||
import org.hl7.fhir.r5.renderers.spreadsheets.CodeSystemSpreadsheetGenerator;
|
||||
import org.hl7.fhir.r5.renderers.spreadsheets.ConceptMapSpreadsheetGenerator;
|
||||
import org.hl7.fhir.r5.renderers.spreadsheets.StructureDefinitionSpreadsheetGenerator;
|
||||
|
@ -475,49 +483,6 @@ public class ValidationService {
|
|||
}
|
||||
}
|
||||
|
||||
public void genScriptQuestionnaire(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||
if (cliContext.getIgs().size() != 1) {
|
||||
throw new Exception("Must have exactly one IG when generating TestScript questionnaires (found " + cliContext.getIgs().size() + ")");
|
||||
}
|
||||
if (cliContext.getOutput() == null) {
|
||||
throw new Exception("Must nominate an output when generating TestScript questionnaires");
|
||||
}
|
||||
if (cliContext.getOutputCanonical() == null) {
|
||||
throw new Exception("Must specify an outputCanonical URL for the generated Questionnaire");
|
||||
}
|
||||
try {
|
||||
/* if (cliContext.getMapLog() != null) {
|
||||
validator.setMapLog(cliContext.getMapLog());
|
||||
}*/
|
||||
Questionnaire q = validator.genScriptQuestionnaire(cliContext.getIgs().get(0), cliContext.getOutputCanonical());
|
||||
System.out.println(" ...success");
|
||||
validator.handleOutput(q, cliContext.getOutput(), cliContext.getSv());
|
||||
} catch (Exception e) {
|
||||
System.out.println(" ...Failure: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void questionnaireExtract(CliContext cliContext, ValidationEngine validator) throws Exception {
|
||||
if (cliContext.getSources().size() != 1) {
|
||||
throw new Exception("Must have exactly one source QuestionnaireResponse when performing a Questionnaire extract");
|
||||
}
|
||||
|
||||
if (cliContext.getOutput() == null) {
|
||||
throw new Exception("Must nominate an output folder when extracting from a QuestionnaireResponse");
|
||||
}
|
||||
try {
|
||||
/* if (cliContext.getMapLog() != null) {
|
||||
validator.setMapLog(cliContext.getMapLog());
|
||||
}*/
|
||||
validator.questionnaireExtract(cliContext.getSources().get(0), cliContext.getOutput(), cliContext.getOutputSuffix().equals("json") ? Manager.FhirFormat.JSON : Manager.FhirFormat.XML);
|
||||
System.out.println(" ...success");
|
||||
} catch (Exception e) {
|
||||
System.out.println(" ...Failure: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public ValidationEngine initializeValidator(CliContext cliContext, String definitions, TimeTracker tt) throws Exception {
|
||||
return sessionCache.fetchSessionValidatorEngine(initializeValidator(cliContext, definitions, tt, null));
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package org.hl7.fhir.validation.cli.tasks;
|
||||
|
||||
import org.hl7.fhir.utilities.TimeTracker;
|
||||
import org.hl7.fhir.validation.ValidationEngine;
|
||||
import org.hl7.fhir.validation.cli.model.CliContext;
|
||||
import org.hl7.fhir.validation.cli.services.ValidationService;
|
||||
import org.hl7.fhir.validation.cli.utils.Display;
|
||||
import org.hl7.fhir.validation.cli.utils.EngineMode;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class QuestionnaireExtractTask extends ValidationEngineTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "questionnaire-extract";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Questionnaire Extraction";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
|
||||
return cliContext.getMode() == EngineMode.QUESTIONNAIRE_EXTRACT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printHelp(PrintStream out) {
|
||||
Display.displayHelpDetails(out,"help/questionnaire-extract.txt");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
|
||||
validationService.questionnaireExtract(cliContext, validationEngine);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package org.hl7.fhir.validation.cli.tasks;
|
||||
|
||||
import org.hl7.fhir.utilities.TimeTracker;
|
||||
import org.hl7.fhir.validation.ValidationEngine;
|
||||
import org.hl7.fhir.validation.cli.model.CliContext;
|
||||
import org.hl7.fhir.validation.cli.services.ValidationService;
|
||||
import org.hl7.fhir.validation.cli.utils.Display;
|
||||
import org.hl7.fhir.validation.cli.utils.EngineMode;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class TestScriptQuestionnaireTask extends ValidationEngineTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "script-questionnaire";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Generate Test Script Questionnaire";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldExecuteTask(CliContext cliContext, String[] args) {
|
||||
return cliContext.getMode() == EngineMode.SCRIPT_QUESTIONNAIRE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printHelp(PrintStream out) {
|
||||
Display.displayHelpDetails(out,"help/script-questionnaire.txt");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeTask(ValidationService validationService, ValidationEngine validationEngine, CliContext cliContext, String[] args, TimeTracker tt, TimeTracker.Session tts) throws Exception {
|
||||
validationService.genScriptQuestionnaire(cliContext, validationEngine);
|
||||
}
|
||||
|
||||
}
|
|
@ -13,7 +13,5 @@ public enum EngineMode {
|
|||
FHIRPATH,
|
||||
VERSION,
|
||||
RUN_TESTS,
|
||||
INSTALL,
|
||||
SCRIPT_QUESTIONNAIRE,
|
||||
QUESTIONNAIRE_EXTRACT
|
||||
INSTALL
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ public class Params {
|
|||
public static final String OUTPUT = "-output";
|
||||
|
||||
public static final String OUTPUT_SUFFIX = "-outputSuffix";
|
||||
public static final String OUTPUT_CANONICAL = "-outputCanonical";
|
||||
public static final String LEVEL = "-level";
|
||||
public static final String HTML_OUTPUT = "-html-output";
|
||||
public static final String PROXY = "-proxy";
|
||||
|
@ -68,8 +67,6 @@ public class Params {
|
|||
public static final String HELP = "help";
|
||||
public static final String COMPARE = "-compare";
|
||||
public static final String SPREADSHEET = "-spreadsheet";
|
||||
public static final String SCRIPT_QUESTIONNAIRE = "-script-questionnaire";
|
||||
public static final String QUESTIONNAIRE_EXTRACT = "-questionnaire-extract";
|
||||
public static final String DESTINATION = "-dest";
|
||||
public static final String LEFT = "-left";
|
||||
public static final String RIGHT = "-right";
|
||||
|
@ -168,11 +165,6 @@ public class Params {
|
|||
throw new Error("Specified -outputSuffix without indicating output suffix");
|
||||
else
|
||||
cliContext.setOutputSuffix(args[++i]);
|
||||
} else if (args[i].equals(OUTPUT_CANONICAL)) {
|
||||
if (i + 1 == args.length)
|
||||
throw new Error("Specified -outputCanonical without indicating output canonical");
|
||||
else
|
||||
cliContext.setOutputCanonical(args[++i]);
|
||||
}
|
||||
else if (args[i].equals(HTML_OUTPUT)) {
|
||||
if (i + 1 == args.length)
|
||||
|
@ -458,10 +450,6 @@ public class Params {
|
|||
cliContext.setFhirpath(args[++i]);
|
||||
else
|
||||
throw new Exception("Can only nominate a single -fhirpath parameter");
|
||||
} else if (args[i].equals(SCRIPT_QUESTIONNAIRE)) {
|
||||
cliContext.setMode(EngineMode.SCRIPT_QUESTIONNAIRE);
|
||||
} else if (args[i].equals(QUESTIONNAIRE_EXTRACT)) {
|
||||
cliContext.setMode(EngineMode.SCRIPT_QUESTIONNAIRE);
|
||||
} else {
|
||||
cliContext.addSource(args[i]);
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
You can use the validator to take a QuestionnaireResponse and a Questionnaire (or IG containing the Questionnaire) and perform an extraction process
|
||||
to generate resources based on the information found in the QuestionnaireResponse.
|
||||
|
||||
NOTE: At present, this feature only supports the 'contained resource' extraction approach. (Refer to the Structured Data Capture IG.)
|
||||
|
||||
For parameters, you must specify:
|
||||
-ig to identify the IG containing the CapabilityStatements, and ActorDefinitions to generate test scripts for
|
||||
-output to identify the filename of the Questionnaire to create
|
||||
|
||||
Example: `-questionnaire-extract -questionnaire questionnaire.json -questionnaire-response response.json -output c:\someFolder\`
|
||||
This will put the generated resources in the someFolder location
|
|
@ -1,9 +0,0 @@
|
|||
You can use the validator to generate a Questionnaire which, when completed, can produce a TestScript (using questionnaire-extract) that
|
||||
can test a specific system against a guide. The Questionnaire will prompt a user to answer questions about which optional capabilities
|
||||
defined by an IG a given system supports - allowing creation of a system-specific test script focused on exactly what the system does.
|
||||
|
||||
For parameters, you must specify:
|
||||
-ig to identify the IG containing the CapabilityStatements, and ActorDefinitions to generate test scripts for
|
||||
-questionnaire to identify the filename of the Questionnaire to create
|
||||
|
||||
Example: `-ig hl7.fhir.us.physical-activity#1.0.0 -script-questionnaire -output outputQuestionnaire.json`
|
|
@ -58,12 +58,6 @@ public class ValidatorCliTests {
|
|||
@Spy
|
||||
SpreadsheetTask spreadsheetTask;
|
||||
|
||||
@Spy
|
||||
TestScriptQuestionnaireTask testScriptQuestionnaireTask;
|
||||
|
||||
@Spy
|
||||
QuestionnaireExtractTask questionnaireExtractTask;
|
||||
|
||||
@Spy
|
||||
PreloadCacheTask preloadCacheTask = new PreloadCacheTask() {
|
||||
@Override
|
||||
|
@ -124,8 +118,6 @@ public class ValidatorCliTests {
|
|||
txTestsTask,
|
||||
transformTask,
|
||||
versionTask,
|
||||
questionnaireExtractTask,
|
||||
testScriptQuestionnaireTask,
|
||||
//validate is the default
|
||||
validateTask
|
||||
);
|
||||
|
@ -260,26 +252,6 @@ public class ValidatorCliTests {
|
|||
Mockito.verify(validationService).transformLang(same(cliContext), same(validationEngine));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptQuestionnaireTest() throws Exception {
|
||||
final String[] args = new String[]{"-ig", "hl7.fhir.us.physical-activity#dev", "-script-questionnaire", "-output", "outputQuestionnaire.json"};
|
||||
CliContext cliContext = Params.loadCliContext(args);
|
||||
ValidatorCli cli = mockValidatorCliWithService(cliContext);
|
||||
cli.readParamsAndExecuteTask(cliContext, args);
|
||||
Mockito.verify(validationService).determineVersion(same(cliContext));
|
||||
Mockito.verify(validationService).genScriptQuestionnaire(same(cliContext), same(validationEngine));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void questionnaireExtractTest() throws Exception {
|
||||
final String[] args = new String[]{"questionnaireResponse.json", "-questionnaire-extract", "-output", "testScripts"};
|
||||
CliContext cliContext = Params.loadCliContext(args);
|
||||
ValidatorCli cli = mockValidatorCliWithService(cliContext);
|
||||
cli.readParamsAndExecuteTask(cliContext, args);
|
||||
Mockito.verify(validationService).determineVersion(same(cliContext));
|
||||
Mockito.verify(validationService).questionnaireExtract(same(cliContext), same(validationEngine));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultTest() throws Exception {
|
||||
final String[] args = new String[]{"dummyFile.json"};
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package org.hl7.fhir.validation.tests;
|
||||
|
||||
import org.hl7.fhir.r4.context.SimpleWorkerContext;
|
||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||
import org.hl7.fhir.utilities.SystemExitManager;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
import org.hl7.fhir.utilities.filesystem.ManagedFileAccess;
|
||||
import org.hl7.fhir.utilities.settings.FhirSettings;
|
||||
import org.hl7.fhir.validation.ValidatorCli;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@Disabled
|
||||
public class TestScriptQuestionnaireTests {
|
||||
private SimpleWorkerContext context;
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
String fn = TestingUtilities.tempFile("testScriptQuestionnaire", "questionnaire.json");
|
||||
SystemExitManager.setNoExit(true);
|
||||
ValidatorCli.main(new String[] {"-ig", "hl7.fhir.us.physical-activity#dev", "-script-questionnaire", "-outputCanonical",
|
||||
"http://hl7.org/fhir/uv/physical-activity/Questionnaire/script-generation", "-version", "4.0", "-output", fn});
|
||||
// Todo: Compare against known good file
|
||||
fn = fn;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue