From e6ecc998eaefa92314fd968125b1381b4f37961a Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 11 Dec 2019 20:52:25 +1100 Subject: [PATCH] add narrative generation tests --- .../r5/terminologies/CodeSystemUtilities.java | 86 ++++++++++++ .../hl7/fhir/r5/utils/NarrativeGenerator.java | 36 +++-- .../java/org/hl7/fhir/r5/test/AllR5Tests.java | 1 + .../r5/test/NarrativeGenerationTests.java | 129 ++++++++++++++++++ pom.xml | 2 +- 5 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java index 289fa972d..e675fdc7e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java @@ -23,7 +23,9 @@ package org.hl7.fhir.r5.terminologies; import java.util.ArrayList; import java.util.Calendar; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; @@ -47,6 +49,90 @@ import org.hl7.fhir.utilities.Utilities; public class CodeSystemUtilities { + public static class CodeSystemNavigator { + + private CodeSystem cs; + private boolean restructure; + private Set processed = new HashSet<>(); + + public CodeSystemNavigator(CodeSystem cs) { + this.cs = cs; + restructure = hasExtraRelationships(cs.getConcept()); + } + + public boolean isRestructure() { + return restructure; + } + + private boolean hasExtraRelationships(List concept) { + for (ConceptDefinitionComponent cd : concept) { + if (getSubsumedBy(cd) != null) { + return true; + } + for (ConceptDefinitionComponent cdc : cd.getConcept()) { + if (hasExtraRelationships(cdc.getConcept())) { + return true; + } + } + } + return false; + } + + public List getConcepts(ConceptDefinitionComponent context) { + if (context == null) { + if (restructure) { + List res = new ArrayList<>(); + for (ConceptDefinitionComponent cd : cs.getConcept()) { + if (getSubsumedBy(cd) == null) { + res.add(cd); + processed.add(cd.getCode()); + } + } + return res; + } else { + return cs.getConcept(); + } + } else { + if (restructure) { + List res = new ArrayList<>(); + for (ConceptDefinitionComponent cd : context.getConcept()) { + res.add(cd); + processed.add(cd.getCode()); + } + for (ConceptDefinitionComponent cd : cs.getConcept()) { + if (context.getCode().equals(getSubsumedBy(cd)) && !processed.contains(cd.getCode())) { + res.add(cd); + processed.add(cd.getCode()); + } + } + return res; + } else { + return context.getConcept(); + } + } + } + + private String getSubsumedBy(ConceptDefinitionComponent cd) { + for (ConceptPropertyComponent cp : cd.getProperty()) { + if (cp.getCode().equals("subsumedBy")) { + return cp.getValue().primitiveValue(); + } + } + return null; + } + + public List getOtherChildren(ConceptDefinitionComponent context) { + List res = new ArrayList<>(); + for (ConceptDefinitionComponent cd : cs.getConcept()) { + if (context.getCode().equals(getSubsumedBy(cd)) && processed.contains(cd.getCode())) { + res.add(cd); + } + } + return res; + } + } + + public static boolean isNotSelectable(CodeSystem cs, ConceptDefinitionComponent def) { for (ConceptPropertyComponent p : def.getProperty()) { if (p.getCode().equals("notSelectable") && p.hasValue() && p.getValue() instanceof BooleanType) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java index ede47e728..e9d20433f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NarrativeGenerator.java @@ -177,6 +177,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; +import org.hl7.fhir.r5.terminologies.CodeSystemUtilities.CodeSystemNavigator; import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument; @@ -317,6 +318,9 @@ public class NarrativeGenerator implements INarrativeGenerator { return res; } + public boolean generate(DomainResource r) throws EOperationOutcome, FHIRException, IOException { + return generate(null, r, new HashSet<>()); + } public boolean generate(DomainResource r, Set outputTracker) throws EOperationOutcome, FHIRException, IOException { return generate(null, r, outputTracker); } @@ -2667,7 +2671,7 @@ public class NarrativeGenerator implements INarrativeGenerator { if (header) { XhtmlNode h = x.h2(); h.addText(cs.hasTitle() ? cs.getTitle() : cs.getName()); - addMarkdown(x, cs.getDescription()); + // .... addMarkdown(x, cs.getDescription()); if (cs.hasCopyright()) generateCopyright(x, cs, lang); } @@ -2768,9 +2772,10 @@ public class NarrativeGenerator implements INarrativeGenerator { version = version || conceptsHaveVersion(c); hierarchy = hierarchy || c.hasConcept(); } + CodeSystemNavigator csNav = new CodeSystemNavigator(cs); addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, true, commentS, version, deprecated, lang, properties), maps); - for (ConceptDefinitionComponent c : cs.getConcept()) { - hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, commentS, version, deprecated, maps, cs.getUrl(), cs, lang, properties) || hasExtensions; + for (ConceptDefinitionComponent c : csNav.getConcepts(null)) { + hasExtensions = addDefineRowToTable(t, c, 0, hierarchy || csNav.isRestructure(), display, commentS, version, deprecated, maps, cs.getUrl(), cs, lang, properties, csNav) || hasExtensions; } // if (langs.size() > 0) { // Collections.sort(langs); @@ -2799,10 +2804,19 @@ public class NarrativeGenerator implements INarrativeGenerator { private boolean showPropertyInTable(PropertyComponent cp) { if (cp.hasCode()) { + if (cp.hasExtension(ToolingExtensions.EXT_RENDERED_VALUE)) { + return true; + } + String uri = cp.getUri(); + if (Utilities.noString(uri)){ + return false; + } + if (uri.contains("#")) { + uri = uri.substring(0, uri.indexOf("#")); + } return - Utilities.existsInList(cp.getUri(), "http://hl7.org/fhir/concept-properties") || - codeSystemPropList.contains(cp.getUri()) || - cp.hasExtension(ToolingExtensions.EXT_RENDERED_VALUE); + Utilities.existsInList(uri, "http://hl7.org/fhir/concept-properties") || + codeSystemPropList.contains(uri); } return false; } @@ -3449,7 +3463,7 @@ public class NarrativeGenerator implements INarrativeGenerator { return "??Lang"; } - private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int i, boolean hasHierarchy, boolean hasDisplay, boolean comment, boolean version, boolean deprecated, List maps, String system, CodeSystem cs, String lang, List properties) throws FHIRFormatError, DefinitionException, IOException { + private boolean addDefineRowToTable(XhtmlNode t, ConceptDefinitionComponent c, int i, boolean hasHierarchy, boolean hasDisplay, boolean comment, boolean version, boolean deprecated, List maps, String system, CodeSystem cs, String lang, List properties, CodeSystemNavigator csNav) throws FHIRFormatError, DefinitionException, IOException { boolean hasExtensions = false; XhtmlNode tr = t.tr(); XhtmlNode td = tr.td(); @@ -3618,16 +3632,16 @@ public class NarrativeGenerator implements INarrativeGenerator { td.i().tx("("+mapping.comp.getComment()+")"); } } - for (String e : CodeSystemUtilities.getOtherChildren(cs, c)) { + for (ConceptDefinitionComponent cc : csNav.getOtherChildren(c)) { tr = t.tr(); td = tr.td(); String s = Utilities.padLeft("", '.', i*2); td.addText(s); - a = td.ah("#"+Utilities.nmtokenize(e)); + a = td.ah("#"+Utilities.nmtokenize(cc.getCode())); a.addText(c.getCode()); } - for (ConceptDefinitionComponent cc : c.getConcept()) { - hasExtensions = addDefineRowToTable(t, cc, i+1, hasHierarchy, hasDisplay, comment, version, deprecated, maps, system, cs, lang, properties) || hasExtensions; + for (ConceptDefinitionComponent cc : csNav.getConcepts(c)) { + hasExtensions = addDefineRowToTable(t, cc, i+1, hasHierarchy, hasDisplay, comment, version, deprecated, maps, system, cs, lang, properties, csNav) || hasExtensions; } return hasExtensions; } diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java index b1a0c1b15..5d10d4150 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/AllR5Tests.java @@ -15,6 +15,7 @@ import org.junit.runners.Suite.SuiteClasses; GraphQLEngineTests.class, LiquidEngineTests.class, FHIRPathTests.class, + NarrativeGenerationTests.class, NarrativeGeneratorTests.class, ShexGeneratorTests.class, BaseDateTimeTypeTest.class, diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java new file mode 100644 index 000000000..5f0bd4d46 --- /dev/null +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/NarrativeGenerationTests.java @@ -0,0 +1,129 @@ +package org.hl7.fhir.r5.test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.NotImplementedException; +import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.exceptions.PathEngineException; +import org.hl7.fhir.r5.conformance.ProfileUtilities; +import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider; +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.SimpleWorkerContext; +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.Base; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.DomainResource; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; +import org.hl7.fhir.r5.model.ExpressionNode.CollectionStatus; +import org.hl7.fhir.r5.model.MetadataResource; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; +import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; +import org.hl7.fhir.r5.model.TestScript; +import org.hl7.fhir.r5.model.TestScript.AssertionResponseTypes; +import org.hl7.fhir.r5.model.TestScript.SetupActionAssertComponent; +import org.hl7.fhir.r5.model.TestScript.SetupActionOperationComponent; +import org.hl7.fhir.r5.model.TestScript.TestActionComponent; +import org.hl7.fhir.r5.model.TestScript.TestScriptFixtureComponent; +import org.hl7.fhir.r5.model.TestScript.TestScriptTestComponent; +import org.hl7.fhir.r5.test.utils.TestingUtilities; +import org.hl7.fhir.r5.model.TypeDetails; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.utils.CodingUtilities; +import org.hl7.fhir.r5.utils.EOperationOutcome; +import org.hl7.fhir.r5.utils.FHIRPathEngine; +import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext; +import org.hl7.fhir.r5.utils.IResourceValidator; +import org.hl7.fhir.r5.utils.NarrativeGenerator; +import org.hl7.fhir.utilities.TextFile; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.VersionUtilities; +import org.hl7.fhir.utilities.cache.PackageCacheManager; +import org.hl7.fhir.utilities.cache.ToolsVersion; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.utilities.xhtml.XhtmlComposer; +import org.hl7.fhir.utilities.xml.XMLUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import ca.uhn.fhir.rest.api.Constants; +import junit.framework.Assert; + +@RunWith(Parameterized.class) +public class NarrativeGenerationTests { + + public static class TestDetails { + private String id; + + public TestDetails(Element test) { + super(); + id = test.getAttribute("id"); + } + public String getId() { + return id; + } + } + + private static FHIRPathEngine fp; + + @Parameters(name = "{index}: file {0}") + public static Iterable data() throws ParserConfigurationException, IOException, FHIRFormatError, SAXException { + + Document tests = XMLUtil.parseToDom(TestingUtilities.loadTestResource("r5", "narrative", "manifest.xml")); + Element test = XMLUtil.getFirstChild(tests.getDocumentElement()); + List objects = new ArrayList(); + while (test != null && test.getNodeName().equals("test")) { + TestDetails t = new TestDetails(test); + objects.add(new Object[] {t.getId(), t}); + test = XMLUtil.getNextSibling(test); + } + return objects; + + } + + + private final TestDetails test; + private IWorkerContext context; + private List messages; + + public NarrativeGenerationTests(String id, TestDetails test) { + this.test = test; + this.context = TestingUtilities.context(); + } + + @SuppressWarnings("deprecation") + @Test + public void test() throws Exception { + NarrativeGenerator gen = new NarrativeGenerator("", "http://hl7.org/fhir", context); + IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId()+"-expected.xml"), new FileOutputStream(TestingUtilities.tempFile("narrative", test.getId()+"-expected.xml"))); + DomainResource source = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId()+"-input.xml")); + DomainResource target = (DomainResource) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId()+"-expected.xml")); + gen.generate(source); + new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(TestingUtilities.tempFile("narrative", test.getId()+"-actual.xml")), source); + Assert.assertTrue("Output does not match expected", source.equalsDeep(target)); + } + +} diff --git a/pom.xml b/pom.xml index cded2375f..1b82dbd43 100644 --- a/pom.xml +++ b/pom.xml @@ -17,7 +17,7 @@ 4.1.0 - 1.0.14-SNAPSHOT + 1.0.15-SNAPSHOT org.hl7.fhir.core