Skip to content

Commit

Permalink
Fix issue with item generation for hidden items (#441)
Browse files Browse the repository at this point in the history
* Fix element type determination and allow for null if element has children

* spotless

* Fix tests

* cleanup
  • Loading branch information
barhodes committed Mar 21, 2024
1 parent a50e8eb commit 4ee24d3
Show file tree
Hide file tree
Showing 15 changed files with 624 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.elm.r1.VersionedIdentifier;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.opencds.cqf.fhir.cql.engine.parameters.CqlParameterDefinition;
import org.opencds.cqf.fhir.utility.FhirPathCache;
import org.slf4j.Logger;
Expand Down Expand Up @@ -102,27 +101,6 @@ private void constructHeader(StringBuilder sb) {
sb.append(String.format("library expression version '1.0.0'%n%n"));
}

private String getFhirVersion(IBaseParameters parameters) {
if (parameters == null) {
return null;
}

switch (parameters.getStructureFhirVersionEnum()) {
case DSTU3:
return "3.0.1";
case R4:
return "4.0.1";
case DSTU2:
case DSTU2_1:
case DSTU2_HL7ORG:
case R5:
default:
throw new IllegalArgumentException(String.format(
"Unsupported version of FHIR: %s",
parameters.getStructureFhirVersionEnum().getFhirVersionString()));
}
}

protected VersionedIdentifier getVersionedIdentifier(String url) {
if (!url.contains("/Library/")) {
throw new IllegalArgumentException(
Expand All @@ -135,7 +113,7 @@ protected VersionedIdentifier getVersionedIdentifier(String url) {
}

// TODO: Use the namespace manager here to do the mapping?
String cqlNamespaceUrl = urlSplit[0];
// String cqlNamespaceUrl = urlSplit[0];

String cqlName = urlSplit[1];
VersionedIdentifier versionedIdentifier = new VersionedIdentifier();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class DynamicModelResolver extends CachingModelResolverDecorator {

private final FhirContext fhirContext;

@SuppressWarnings("rawtypes")
public DynamicModelResolver(ModelResolver modelResolver) {
super(modelResolver);
this.fhirContext = ((FhirModelResolver) modelResolver).getFhirContext();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public IBaseBackboneElement addProperties(
questionnaireItem,
"extension",
buildBooleanExt(request.getFhirVersion(), sdcQuestionnaireHidden(Boolean.TRUE)));
// Collections.singletonList(buildBooleanExt(request.getFhirVersion(), sdcQuestionnaireHidden(Boolean.TRUE))));
request.getModelResolver()
.setValue(questionnaireItem, "readOnly", buildBooleanType(request.getFhirVersion(), Boolean.TRUE));
return questionnaireItem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@

public interface IElementProcessor {
IBaseBackboneElement processElement(
GenerateRequest request, ICompositeType element, String childLinkId, IBaseResource caseFeature);
GenerateRequest request,
ICompositeType element,
String elementType,
String childLinkId,
IBaseResource caseFeature,
Boolean isGroup);

public static IElementProcessor createProcessor(Repository repository) {
switch (repository.fhirContext().getVersion().getVersion()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype;
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
Expand Down Expand Up @@ -54,7 +53,7 @@ public IBaseBackboneElement generate(GenerateRequest request) {
processExtensions(request, questionnaireItem);
int childCount = request.getItems(questionnaireItem).size();
var caseFeature = getCaseFeature(request, linkId);
var parentElements = getElementsWithNonNullElementType(request, null, null);
var parentElements = getElements(request, null, null);
processElements(request, questionnaireItem, parentElements, childCount, linkId, caseFeature);
return questionnaireItem;
} catch (Exception ex) {
Expand Down Expand Up @@ -107,28 +106,32 @@ protected <E extends ICompositeType> void processElements(
for (var element : elements) {
childCount++;
var childLinkId = String.format(CHILD_LINK_ID_FORMAT, itemLinkId, childCount);
var childItem = processElement(request, element, childLinkId, caseFeature);
if (request.resolvePath(childItem, "type", IBaseEnumeration.class)
.getValueAsString()
.equals("group")) {
var childElements = getElementsWithNonNullElementType(
request,
request.resolvePathString(element, "path"),
request.resolvePathString(element, "sliceName"));
if (!childElements.isEmpty()) {
processElements(request, childItem, childElements, 0, childLinkId, caseFeature);
request.getModelResolver().setValue(item, "item", Collections.singletonList(childItem));
}
var childElements = getElements(
request,
request.resolvePathString(element, "path"),
request.resolvePathString(element, "sliceName"));
// if child elements exist ignore the type and create a group
var elementType = getElementType(request, element);
var childItem =
processElement(request, element, elementType, childLinkId, caseFeature, !childElements.isEmpty());
if (childElements.isEmpty()) {
request.getModelResolver().setValue(item, "item", Collections.singletonList(childItem));
} else {
processElements(request, childItem, childElements, 0, childLinkId, caseFeature);
request.getModelResolver().setValue(item, "item", Collections.singletonList(childItem));
}
}
}

protected IBaseBackboneElement processElement(
GenerateRequest request, ICompositeType element, String childLinkId, IBaseResource caseFeature) {
GenerateRequest request,
ICompositeType element,
String elementType,
String childLinkId,
IBaseResource caseFeature,
Boolean isGroup) {
try {
return elementProcessor.processElement(request, element, childLinkId, caseFeature);
return elementProcessor.processElement(request, element, elementType, childLinkId, caseFeature, isGroup);
} catch (Exception ex) {
final String message = String.format(ITEM_CREATION_ERROR, ex.getMessage());
logger.warn(message);
Expand All @@ -137,7 +140,7 @@ protected IBaseBackboneElement processElement(
}

@SuppressWarnings("unchecked")
protected <E extends ICompositeType> List<E> getElementsWithNonNullElementType(
protected <E extends ICompositeType> List<E> getElements(
GenerateRequest request, String parentPath, String sliceName) {
List<E> elements = new ArrayList<>();
// Add all elements from the differential
Expand All @@ -154,10 +157,7 @@ protected <E extends ICompositeType> List<E> getElementsWithNonNullElementType(
.filter(e -> filterElement(request, e, elements, parentPath, sliceName, request.getRequiredOnly()))
.collect(Collectors.toList()));
}
// Filter out elements for which a type cannot be derived
return elements.stream()
.filter(element -> getElementType(request, element) != null)
.collect(Collectors.toList());
return elements;
}

protected <E extends ICompositeType> Boolean filterElement(
Expand All @@ -173,6 +173,10 @@ protected <E extends ICompositeType> Boolean filterElement(
return false;
}
}
// filter out slicing definitions
if (request.resolvePath(element, "slicing") != null) {
return false;
}
var pathSplit = path.split("\\.");
if (parentPath == null) {
// grab only top level elements
Expand Down Expand Up @@ -208,9 +212,27 @@ protected IBaseBackboneElement createErrorItem(GenerateRequest request, String l
return createQuestionnaireItemComponent(request, errorMessage, linkId, null, true);
}

protected String resolveElementType(GenerateRequest request, ICompositeType element) {
var typeList = request.resolvePathList(element, "type");
return typeList.isEmpty() ? null : request.resolvePathString(typeList.get(0), "code");
}

protected String getElementType(GenerateRequest request, ICompositeType element) {
var type = request.resolvePathList(element, "type");
return type.isEmpty() ? null : request.resolvePathString(type.get(0), "code");
var type = resolveElementType(request, element);
if (type == null) {
// Attempt to resolve the type from the Snapshot if it is available
var path = request.resolvePathString(element, "path");
var snapshot = request.getSnapshotElements() == null
? null
: request.getSnapshotElements().stream()
.filter(e -> path.equals(request.resolvePathString(e, "path")))
.findFirst()
.orElse(null);
if (snapshot != null) {
type = resolveElementType(request, snapshot);
}
}
return type;
}

public IBaseBackboneElement createQuestionnaireItem(GenerateRequest request, String linkId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,22 @@ public ElementProcessor(Repository repository) {

@Override
public IBaseBackboneElement processElement(
GenerateRequest request, ICompositeType baseElement, String childLinkId, IBaseResource caseFeature) {
GenerateRequest request,
ICompositeType baseElement,
String elementType,
String childLinkId,
IBaseResource caseFeature,
Boolean isGroup) {
var element = (ElementDefinition) baseElement;
final QuestionnaireItemType itemType = getItemType(element);
final QuestionnaireItemComponent item =
initializeQuestionnaireItem(itemType, request.getProfileUrl(), element, childLinkId);
final var itemType = isGroup ? QuestionnaireItemType.GROUP : parseItemType(elementType, element.hasBinding());
final var item = initializeQuestionnaireItem(itemType, request.getProfileUrl(), element, childLinkId);
item.setRequired(element.hasMin() && element.getMin() > 0);
item.setRepeats(element.hasMax() && !element.getMax().equals("1"));
// set enableWhen based on? use
// http:https://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression
if (itemType == QuestionnaireItemType.GROUP) {
return item;
}
if (itemType == QuestionnaireItemType.CHOICE) {
questionnaireTypeIsChoice.addProperties(element, item);
}
Expand All @@ -55,10 +66,6 @@ public IBaseBackboneElement processElement(
item.setInitial(transformValueToItem((Type) pathValue));
}
}
item.setRequired(element.hasMin() && element.getMin() > 0);
item.setRepeats(element.hasMax() && !element.getMax().equals("1"));
// set enableWhen based on? use
// http:https://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression
return item;
}

Expand All @@ -72,20 +79,13 @@ protected QuestionnaireItemComponent initializeQuestionnaireItem(
.setText(getElementText(element));
}

public QuestionnaireItemType getItemType(ElementDefinition element) {
final String elementType = element.getType().get(0).getCode();
final QuestionnaireItemType itemType = parseItemType(elementType, element.hasBinding());
if (itemType == null) {
final String message = String.format(ITEM_TYPE_ERROR, element.getId());
throw new IllegalArgumentException(message);
}
return itemType;
}

public QuestionnaireItemType parseItemType(String elementType, Boolean hasBinding) {
if (Boolean.TRUE.equals(hasBinding)) {
return QuestionnaireItemType.CHOICE;
}
if (elementType == null) {
throw new IllegalArgumentException("Unable to determine item type.");
}
switch (elementType) {
case "code":
case "coding":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,22 @@ public ElementProcessor(Repository repository) {

@Override
public IBaseBackboneElement processElement(
GenerateRequest request, ICompositeType baseElement, String childLinkId, IBaseResource caseFeature) {
GenerateRequest request,
ICompositeType baseElement,
String elementType,
String childLinkId,
IBaseResource caseFeature,
Boolean isGroup) {
var element = (ElementDefinition) baseElement;
final QuestionnaireItemType itemType = getItemType(element);
final QuestionnaireItemComponent item =
initializeQuestionnaireItem(itemType, request.getProfileUrl(), element, childLinkId);
final var itemType = isGroup ? QuestionnaireItemType.GROUP : parseItemType(elementType, element.hasBinding());
final var item = initializeQuestionnaireItem(itemType, request.getProfileUrl(), element, childLinkId);
item.setRequired(element.hasMin() && element.getMin() > 0);
item.setRepeats(element.hasMax() && !element.getMax().equals("1"));
// set enableWhen based on? use
// http:https://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression
if (itemType == QuestionnaireItemType.GROUP) {
return item;
}
if (itemType == QuestionnaireItemType.CHOICE) {
questionnaireTypeIsChoice.addProperties(element, item);
}
Expand All @@ -53,10 +64,6 @@ public IBaseBackboneElement processElement(
item.addInitial().setValue(transformValueToItem((Type) pathValue));
}
}
item.setRequired(element.hasMin() && element.getMin() > 0);
item.setRepeats(element.hasMax() && !element.getMax().equals("1"));
// set enableWhen based on? use
// http:https://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression
return item;
}

Expand All @@ -70,21 +77,13 @@ protected QuestionnaireItemComponent initializeQuestionnaireItem(
.setText(getElementText(element));
}

public QuestionnaireItemType getItemType(ElementDefinition element) {
final String elementType = element.getType().get(0).getCode();
QuestionnaireItemType itemType = null;
try {
itemType = parseItemType(elementType, element.hasBinding());
} catch (Exception e) {
itemType = QuestionnaireItemType.GROUP;
}
return itemType;
}

public QuestionnaireItemType parseItemType(String elementType, Boolean hasBinding) {
if (Boolean.TRUE.equals(hasBinding)) {
return QuestionnaireItemType.CHOICE;
}
if (elementType == null) {
throw new IllegalArgumentException("Unable to determine item type.");
}
switch (elementType) {
case "code":
case "coding":
Expand Down Expand Up @@ -112,7 +111,7 @@ public QuestionnaireItemType parseItemType(String elementType, Boolean hasBindin
}
}

public String getElementText(ElementDefinition element) {
protected String getElementText(ElementDefinition element) {
return element.hasLabel() ? element.getLabel() : getElementDescription(element);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,22 @@ public ElementProcessor(Repository repository) {

@Override
public IBaseBackboneElement processElement(
GenerateRequest request, ICompositeType baseElement, String childLinkId, IBaseResource caseFeature) {
GenerateRequest request,
ICompositeType baseElement,
String elementType,
String childLinkId,
IBaseResource caseFeature,
Boolean isGroup) {
var element = (ElementDefinition) baseElement;
final QuestionnaireItemType itemType = getItemType(element);
final QuestionnaireItemComponent item =
initializeQuestionnaireItem(itemType, request.getProfileUrl(), element, childLinkId);
final var itemType = isGroup ? QuestionnaireItemType.GROUP : parseItemType(elementType, element.hasBinding());
final var item = initializeQuestionnaireItem(itemType, request.getProfileUrl(), element, childLinkId);
item.setRequired(element.hasMin() && element.getMin() > 0);
item.setRepeats(element.hasMax() && !element.getMax().equals("1"));
// set enableWhen based on? use
// http:https://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression
if (itemType == QuestionnaireItemType.GROUP) {
return item;
}
if (itemType == QuestionnaireItemType.QUESTION) {
questionnaireTypeIsChoice.addProperties(element, item);
}
Expand All @@ -53,10 +64,6 @@ public IBaseBackboneElement processElement(
item.addInitial().setValue(transformValueToItem((DataType) pathValue));
}
}
item.setRequired(element.hasMin() && element.getMin() > 0);
item.setRepeats(element.hasMax() && !element.getMax().equals("1"));
// set enableWhen based on? use
// http:https://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-enableWhenExpression
return item;
}

Expand All @@ -70,20 +77,13 @@ protected QuestionnaireItemComponent initializeQuestionnaireItem(
.setText(getElementText(element));
}

public QuestionnaireItemType getItemType(ElementDefinition element) {
final String elementType = element.getType().get(0).getCode();
final QuestionnaireItemType itemType = parseItemType(elementType, element.hasBinding());
if (itemType == null) {
final String message = String.format(ITEM_TYPE_ERROR, element.getId());
throw new IllegalArgumentException(message);
}
return itemType;
}

public QuestionnaireItemType parseItemType(String elementType, Boolean hasBinding) {
if (Boolean.TRUE.equals(hasBinding)) {
return QuestionnaireItemType.QUESTION;
}
if (elementType == null) {
throw new IllegalArgumentException("Unable to determine item type.");
}
switch (elementType) {
case "code":
case "coding":
Expand Down
Loading

0 comments on commit 4ee24d3

Please sign in to comment.