Narrative fixes

This commit is contained in:
James 2014-07-04 16:17:53 -04:00
parent 80c13494a8
commit ec2326b1f2
23 changed files with 213 additions and 41 deletions

View File

@ -583,6 +583,23 @@ public class Encounter extends BaseResource implements IResource {
return myType; return myType;
} }
/**
* Gets the value(s) for <b>type</b> (Specific type of encounter).
* creating it if it does
* not exist. Will not return <code>null</code>.
*
* <p>
* <b>Definition:</b>
* Specific type of encounter (e.g. e-mail consultation, surgical day-care, skilled nursing, rehabilitation)
* </p>
*/
public BoundCodeableConceptDt<EncounterTypeEnum> getTypeFirstRep() {
if (getType().size()==0) {
addType();
}
return getType().get(0);
}
/** /**
* Sets the value(s) for <b>type</b> (Specific type of encounter) * Sets the value(s) for <b>type</b> (Specific type of encounter)
* *

View File

@ -126,7 +126,8 @@ public class StringDt extends BasePrimitive<String> implements IQueryParameterTy
*/ */
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return super.isBaseEmpty() && StringUtils.isBlank(getValue()); boolean retVal = super.isBaseEmpty() && StringUtils.isBlank(getValue());
return retVal;
} }
@Override @Override

View File

@ -53,8 +53,10 @@ import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressions; import org.thymeleaf.standard.expression.StandardExpressions;
import org.thymeleaf.templateresolver.TemplateResolver; import org.thymeleaf.templateresolver.TemplateResolver;
import org.thymeleaf.util.DOMUtils; import org.thymeleaf.util.DOMUtils;
import org.w3c.dom.Text;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum; import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
@ -94,6 +96,8 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
initialize(); initialize();
} }
ourLog.trace("Generating resource title {}", theResource);
String name = null; String name = null;
if (StringUtils.isNotBlank(theProfile)) { if (StringUtils.isNotBlank(theProfile)) {
name = myProfileToName.get(theProfile); name = myProfileToName.get(theProfile);
@ -102,6 +106,8 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
name = myClassToName.get(theResource.getClass()); name = myClassToName.get(theResource.getClass());
} }
ourLog.trace("Template name is {}", name);
if (name == null) { if (name == null) {
if (myIgnoreMissingTemplates) { if (myIgnoreMissingTemplates) {
ourLog.debug("No title template available for profile: {}", theProfile); ourLog.debug("No title template available for profile: {}", theProfile);
@ -116,6 +122,9 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
context.setVariable("resource", theResource); context.setVariable("resource", theResource);
String result = myTitleTemplateEngine.process(name, context); String result = myTitleTemplateEngine.process(name, context);
ourLog.trace("Produced {}", result);
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
boolean inTag = false; boolean inTag = false;
for (int i = 0; i < result.length(); i++) { for (int i = 0; i < result.length(); i++) {
@ -152,6 +161,8 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
result = result.substring(0, result.lastIndexOf('<')); result = result.substring(0, result.lastIndexOf('<'));
} }
result = result.replace("&gt;", ">").replace("&lt;", "<").replace("&amp;", "&");
return result; return result;
} catch (Exception e) { } catch (Exception e) {
if (myIgnoreFailures) { if (myIgnoreFailures) {
@ -214,6 +225,9 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
if (myInitialized) { if (myInitialized) {
return; return;
} }
ourLog.info("Initializing narrative generator");
myProfileToName = new HashMap<String, String>(); myProfileToName = new HashMap<String, String>();
myClassToName = new HashMap<Class<?>, String>(); myClassToName = new HashMap<Class<?>, String>();
myNameToNarrativeTemplate = new HashMap<String, String>(); myNameToNarrativeTemplate = new HashMap<String, String>();
@ -250,6 +264,9 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
resolver.setResourceResolver(new TitleResourceResolver()); resolver.setResourceResolver(new TitleResourceResolver());
myTitleTemplateEngine.setTemplateResolver(resolver); myTitleTemplateEngine.setTemplateResolver(resolver);
StandardDialect dialect = new StandardDialect(); StandardDialect dialect = new StandardDialect();
HashSet<IProcessor> additionalProcessors = new HashSet<IProcessor>();
additionalProcessors.add(new NarrativeAttributeProcessor());
dialect.setAdditionalProcessors(additionalProcessors);
myTitleTemplateEngine.setDialect(dialect); myTitleTemplateEngine.setDialect(dialect);
myTitleTemplateEngine.initialize(); myTitleTemplateEngine.initialize();
} }
@ -486,7 +503,13 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
Context context = new Context(); Context context = new Context();
context.setVariable("resource", value); context.setVariable("resource", value);
String name = myClassToName.get(value.getClass()); String name = null;
Class<? extends Object> nextClass = value.getClass();
do {
name = myClassToName.get(nextClass);
nextClass=nextClass.getSuperclass();
} while (name == null && nextClass.equals(Object.class)==false);
if (name == null) { if (name == null) {
if (myIgnoreMissingTemplates) { if (myIgnoreMissingTemplates) {
ourLog.debug("No narrative template available for type: {}", value.getClass()); ourLog.debug("No narrative template available for type: {}", value.getClass());
@ -497,10 +520,18 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
} }
String result = myProfileTemplateEngine.process(name, context); String result = myProfileTemplateEngine.process(name, context);
Document dom = DOMUtils.getXhtmlDOMFor(new StringReader(result)); String trim = result.trim();
Document dom = DOMUtils.getXhtmlDOMFor(new StringReader(trim));
Element firstChild = (Element) dom.getFirstChild(); Element firstChild = (Element) dom.getFirstChild();
for (Node next : firstChild.getChildren()) { for (int i = 0; i < firstChild.getChildren().size(); i++) {
Node next = firstChild.getChildren().get(i);
if (i == 0 && firstChild.getChildren().size() == 1) {
if (next instanceof org.thymeleaf.dom.Text) {
org.thymeleaf.dom.Text nextText = (org.thymeleaf.dom.Text) next;
nextText.setContent(nextText.getContent().trim());
}
}
theElement.addChild(next); theElement.addChild(next);
} }

View File

@ -223,7 +223,6 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.writeEnd(); eventWriter.writeEnd();
eventWriter.flush(); eventWriter.flush();
eventWriter.close();
} }
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException { private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException {
@ -507,7 +506,6 @@ public class JsonParser extends BaseParser implements IParser {
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null,false); encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null,false);
eventWriter.flush(); eventWriter.flush();
eventWriter.close();
} }
@Override @Override
@ -538,7 +536,6 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.writeEnd(); eventWriter.writeEnd();
eventWriter.flush(); eventWriter.flush();
eventWriter.close();
} }
/** /**

View File

@ -783,9 +783,12 @@ public class RestfulServer extends HttpServlet {
if (theContext.getNarrativeGenerator() != null) { if (theContext.getNarrativeGenerator() != null) {
String title = theContext.getNarrativeGenerator().generateTitle(next); String title = theContext.getNarrativeGenerator().generateTitle(next);
ourLog.info("Narrative generator created title: {}", title);
if (StringUtils.isNotBlank(title)) { if (StringUtils.isNotBlank(title)) {
ResourceMetadataKeyEnum.TITLE.put(next, title); ResourceMetadataKeyEnum.TITLE.put(next, title);
} }
} else {
ourLog.info("No narrative generator specified");
} }
bundle.addResource(next, theContext, theServerBase); bundle.addResource(next, theContext, theServerBase);

View File

@ -1,6 +0,0 @@
<div>
<th:block th:each="prefix : ${resource.prefix}" th:text="${prefix.value} + ' '">Dr</th:block>
<th:block th:each="givenName : ${resource.given}" th:text="${givenName.value} + ' '">John</th:block>
<b th:each="familyName : ${resource.family}" th:text="${#strings.toUpperCase(familyName.value)} + ' '">SMITH</b>
<th:block th:each="suffix : ${resource.suffix}" th:text="${suffix.value} + ' '">Jr</th:block>
</div>

View File

@ -16,7 +16,7 @@ a browser.
<tbody> <tbody>
<tr th:if="${not resource.identifierFirstRep.empty}"> <tr th:if="${not resource.identifierFirstRep.empty}">
<td>Identifier</td> <td>Identifier</td>
<td th:text="${resource.identifierFirstRep.value.value}">8708660</td> <td th:narrative="${resource.identifierFirstRep}">8708660</td>
</tr> </tr>
<tr th:if="${not resource.addressFirstRep.empty}"> <tr th:if="${not resource.addressFirstRep.empty}">
<td>Address</td> <td>Address</td>

View File

@ -0,0 +1,3 @@
<div>
<th:block th:if="${not resource.empty}" th:text="${resource.value}"/>
</div>

View File

@ -0,0 +1,13 @@
<div>
<th:block th:if="${not resource.text.empty}" th:text="${resource.text.value}"/>
<th:block th:if="${resource.text.empty}">
<th:block th:if="${!resource.codingFirstRep.empty}">
<th:block th:if="${!resource.codingFirstRep.display.empty}" th:text="${!resource.codingFirstRep.display.value}"/>
<th:block th:if="${resource.codingFirstRep.display.empty}">
<th:block th:if="${!resource.codingFirstRep.code.empty}">
<th:block th:text="${resource.codingFirstRep.code.value}"/>
</th:block>
</th:block>
</th:block>
</th:block>
</div>

View File

@ -0,0 +1,6 @@
<div>
<th:block th:each="prefix : ${resource.prefix}" th:if="${!prefix.empty}" th:text="${prefix.value} + ' '">Dr</th:block>
<th:block th:each="givenName : ${resource.given}" th:if="${!givenName.empty}" th:text="${givenName.value} + ' '">John</th:block>
<b th:each="familyName : ${resource.family}" th:if="${!familyName.empty}" th:text="${#strings.toUpperCase(familyName.value)} + ' '">SMITH</b>
<th:block th:each="suffix : ${resource.suffix}" th:if="${!suffix.empty}" th:text="${suffix.value} + ' '">Jr</th:block>
</div>

View File

@ -0,0 +1,4 @@
<div>
<th:block th:if="${!resource.label.empty}" th:text="${resource.label.value}"/>
<th:block th:if="${resource.label.empty}" th:text="${resource.value.valueAsString}"/>
</div>

View File

@ -0,0 +1,5 @@
<div>
<th:block th:if="${!resource.start.empty} and ${!resource.end.empty}" th:text="${resource.start.value} + ' - ' + ${resource.end.value}"/>
<th:block th:if="${!resource.start.empty} and ${resource.end.empty}" th:text="${resource.start.value} + ' - ?'"/>
<th:block th:if="${resource.start.empty} and ${!resource.end.empty}" th:text="'? - ' + ${resource.end.value}"/>
</div>

View File

@ -3,38 +3,53 @@
# Primitive Datatypes # Primitive Datatypes
################################################ ################################################
string.class=ca.uhn.fhir.model.primitive.StringDt code.class=ca.uhn.fhir.model.primitive.CodeDt
string.narrative=classpath:ca/uhn/fhir/narrative/StringDt.html code.narrative=classpath:ca/uhn/fhir/narrative/datatype/CodeDt.html
datetime.class=ca.uhn.fhir.model.primitive.DateTimeDt datetime.class=ca.uhn.fhir.model.primitive.DateTimeDt
datetime.narrative=classpath:ca/uhn/fhir/narrative/DateTimeDt.html datetime.narrative=classpath:ca/uhn/fhir/narrative/datatype/DateTimeDt.html
# Instant uses DateTime narrative # Instant uses DateTime narrative
instant.class=ca.uhn.fhir.model.primitive.InstantDt instant.class=ca.uhn.fhir.model.primitive.InstantDt
instant.narrative=classpath:ca/uhn/fhir/narrative/DateTimeDt.html instant.narrative=classpath:ca/uhn/fhir/narrative/datatype/DateTimeDt.html
string.class=ca.uhn.fhir.model.primitive.StringDt
string.narrative=classpath:ca/uhn/fhir/narrative/datatype/StringDt.html
################################################ ################################################
# Composite Datatypes # Composite Datatypes
################################################ ################################################
address.class=ca.uhn.fhir.model.dstu.composite.AddressDt address.class=ca.uhn.fhir.model.dstu.composite.AddressDt
address.narrative=classpath:ca/uhn/fhir/narrative/AddressDt.html address.narrative=classpath:ca/uhn/fhir/narrative/datatype/AddressDt.html
codeableconcept.class=ca.uhn.fhir.model.dstu.composite.CodeableConceptDt
codeableconcept.narrative=classpath:ca/uhn/fhir/narrative/datatype/CodeableConceptDt.html
humanname.class=ca.uhn.fhir.model.dstu.composite.HumanNameDt humanname.class=ca.uhn.fhir.model.dstu.composite.HumanNameDt
humanname.narrative=classpath:ca/uhn/fhir/narrative/HumanNameDt.html humanname.narrative=classpath:ca/uhn/fhir/narrative/datatype/HumanNameDt.html
identifier.class=ca.uhn.fhir.model.dstu.composite.IdentifierDt
identifier.narrative=classpath:ca/uhn/fhir/narrative/datatype/IdentifierDt.html
period.class=ca.uhn.fhir.model.dstu.composite.PeriodDt
period.narrative=classpath:ca/uhn/fhir/narrative/datatype/PeriodDt.html
quantity.class=ca.uhn.fhir.model.dstu.composite.QuantityDt quantity.class=ca.uhn.fhir.model.dstu.composite.QuantityDt
quantity.narrative=classpath:ca/uhn/fhir/narrative/QuantityDt.html quantity.narrative=classpath:ca/uhn/fhir/narrative/datatype/QuantityDt.html
################################################ ################################################
# Resources # Resources
################################################ ################################################
diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport
diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html
diagnosticreport.title=classpath:ca/uhn/fhir/narrative/title/DiagnosticReport.html
encounter.class=ca.uhn.fhir.model.dstu.resource.Encounter
encounter.title=classpath:ca/uhn/fhir/narrative/title/Encounter.html
patient.class=ca.uhn.fhir.model.dstu.resource.Patient patient.class=ca.uhn.fhir.model.dstu.resource.Patient
patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html
patient.title=classpath:ca/uhn/fhir/narrative/title/Patient.html patient.title=classpath:ca/uhn/fhir/narrative/title/Patient.html
diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport
diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html
diagnosticreport.title=classpath:ca/uhn/fhir/narrative/title/DiagnosticReport.html

View File

@ -0,0 +1,15 @@
<div>
<th:block th:if="${!resource.identifierFirstRep.empty}" th:narrative="${resource.identifierFirstRep}" />
<th:block th:if="${not resource.status.empty}">
/ <th:block th:narrative="${resource.status}"/>
</th:block>
<th:block th:if="${not resource.typeFirstRep.empty}">
/ <th:block th:narrative="${resource.typeFirstRep}"/>
</th:block>
<th:block th:if="${not resource.classElement.empty}">
/ <th:block th:narrative="${resource.classElement}"/>
</th:block>
<th:block th:if="${not resource.period.empty}">
/ <th:block th:narrative="${resource.period}"/>
</th:block>
</div>

View File

@ -1,9 +1,6 @@
<div> <div>
<th:block th:each="prefix : ${resource.nameFirstRep.prefix}" th:text="${prefix.value} + ' '">Dr</th:block> <th:block th:narrative="${resource.nameFirstRep}" />
<th:block th:each="givenName : ${resource.nameFirstRep.given}" th:text="${givenName.value} + ' '">John</th:block>
<th:block th:each="familyName : ${resource.nameFirstRep.family}" th:text="${#strings.toUpperCase(familyName.value)} + ' '">SMITH</th:block>
<th:block th:each="suffix : ${resource.nameFirstRep.suffix}" th:text="${suffix.value} + ' '">Jr</th:block>
<th:block th:if="${not resource.identifierFirstRep.empty}"> <th:block th:if="${not resource.identifierFirstRep.empty}">
(<th:block th:text="${resource.identifierFirstRep.value.value}">8708660</th:block>) (<th:block th:narrative="${resource.identifierFirstRep}">8708660</th:block>)
</th:block> </th:block>
</div> </div>

View File

@ -10,20 +10,27 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.PeriodDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt; import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Encounter;
import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.DiagnosticReportStatusEnum; import ca.uhn.fhir.model.dstu.valueset.DiagnosticReportStatusEnum;
import ca.uhn.fhir.model.dstu.valueset.EncounterClassEnum;
import ca.uhn.fhir.model.dstu.valueset.EncounterTypeEnum;
import ca.uhn.fhir.model.dstu.valueset.ObservationStatusEnum; import ca.uhn.fhir.model.dstu.valueset.ObservationStatusEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
public class DefaultThymeleafNarrativeGeneratorTest { public class DefaultThymeleafNarrativeGeneratorTest {
private FhirContext myCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultThymeleafNarrativeGeneratorTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultThymeleafNarrativeGeneratorTest.class);
private DefaultThymeleafNarrativeGenerator gen; private DefaultThymeleafNarrativeGenerator gen;
@ -33,14 +40,20 @@ public class DefaultThymeleafNarrativeGeneratorTest {
gen.setUseHapiServerConformanceNarrative(true); gen.setUseHapiServerConformanceNarrative(true);
gen.setIgnoreFailures(false); gen.setIgnoreFailures(false);
gen.setIgnoreMissingTemplates(false); gen.setIgnoreMissingTemplates(false);
myCtx=new FhirContext();
myCtx.setNarrativeGenerator(gen);
} }
@Test @Test
public void testGeneratePatient() throws DataFormatException { public void testGeneratePatient() throws DataFormatException {
Patient value = new Patient(); Patient value = new Patient();
value.addIdentifier().setSystem("urn:names").setValue("123456"); value.addIdentifier().setSystem("urn:names").setValue("123456");
value.addName().addFamily("blow").addGiven("joe").addGiven("john"); value.addName().addFamily("blow").addGiven("joe").addGiven(null).addGiven("john");
value.getAddressFirstRep().addLine("123 Fake Street").addLine("Unit 1"); value.getAddressFirstRep().addLine("123 Fake Street").addLine("Unit 1");
value.getAddressFirstRep().setCity("Toronto").setState("ON").setCountry("Canada"); value.getAddressFirstRep().setCity("Toronto").setState("ON").setCountry("Canada");
@ -52,11 +65,33 @@ public class DefaultThymeleafNarrativeGeneratorTest {
String title = gen.generateTitle(value); String title = gen.generateTitle(value);
assertEquals("joe john BLOW (123456)", title); assertEquals("joe john BLOW (123456)", title);
ourLog.info(title); ourLog.info(title);
value.getIdentifierFirstRep().setLabel("FOO MRN 123");
title = gen.generateTitle(value);
assertEquals("joe john BLOW (FOO MRN 123)", title);
ourLog.info(title);
}
@Test
public void testGenerateEncounter() throws DataFormatException {
Encounter enc = new Encounter();
enc.addIdentifier("urn:visits", "1234567");
enc.setClassElement(EncounterClassEnum.AMBULATORY);
enc.setPeriod(new PeriodDt().setStart(new DateTimeDt("2001-01-02T11:11:00")));
enc.setType(EncounterTypeEnum.ANNUAL_DIABETES_MELLITUS_SCREENING);
String title = gen.generateTitle(enc);
assertEquals("1234567 / ADMS / ambulatory / Tue Jan 02 11:11:00 EST 2001 - ?", title);
ourLog.info(title);
} }
@Test @Test
public void testGenerateServerConformance() throws DataFormatException { public void testGenerateServerConformance() throws DataFormatException {
Conformance value = new FhirContext().newXmlParser().parseResource(Conformance.class, new InputStreamReader(getClass().getResourceAsStream("/server-conformance-statement.xml"))); Conformance value = myCtx.newXmlParser().parseResource(Conformance.class, new InputStreamReader(getClass().getResourceAsStream("/server-conformance-statement.xml")));
String output = gen.generateNarrative(value).getDiv().getValueAsString(); String output = gen.generateNarrative(value).getDiv().getValueAsString();
@ -76,6 +111,7 @@ public class DefaultThymeleafNarrativeGeneratorTest {
String output = gen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value).getDiv().getValueAsString(); String output = gen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value).getDiv().getValueAsString();
ourLog.info(output); ourLog.info(output);
assertThat(output,StringContains.containsString(value.getName().getText().getValue()));
} }
@Test @Test
@ -85,7 +121,7 @@ public class DefaultThymeleafNarrativeGeneratorTest {
value.getIssued().setValueAsString("2011-02-22T11:13:00"); value.getIssued().setValueAsString("2011-02-22T11:13:00");
value.setStatus(DiagnosticReportStatusEnum.FINAL); value.setStatus(DiagnosticReportStatusEnum.FINAL);
value.getName().setText("Some Diagnostic Report"); value.getName().setText("Some & Diagnostic Report");
{ {
Observation obs = new Observation(); Observation obs = new Observation();
obs.getName().addCoding().setCode("1938HB").setDisplay("Hemoglobin"); obs.getName().addCoding().setCode("1938HB").setDisplay("Hemoglobin");
@ -106,7 +142,7 @@ public class DefaultThymeleafNarrativeGeneratorTest {
String output = generateNarrative.getDiv().getValueAsString(); String output = generateNarrative.getDiv().getValueAsString();
ourLog.info(output); ourLog.info(output);
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some Diagnostic Report </div>")); assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some &amp; Diagnostic Report </div>"));
String title = gen.generateTitle(value); String title = gen.generateTitle(value);
ourLog.info(title); ourLog.info(title);
@ -115,11 +151,9 @@ public class DefaultThymeleafNarrativeGeneratorTest {
// Now try it with the parser // Now try it with the parser
FhirContext context = new FhirContext(); output = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(value);
context.setNarrativeGenerator(gen);
output = context.newXmlParser().setPrettyPrint(true).encodeResourceToString(value);
ourLog.info(output); ourLog.info(output);
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some Diagnostic Report </div>")); assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some &amp; Diagnostic Report </div>"));
} }

View File

@ -9,8 +9,24 @@
</encoder> </encoder>
</appender> </appender>
<root> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<appender-ref ref="STDOUT" /> <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<file>${fhir.logdir}/logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${fhir.logdir}/logFile.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<!-- [%file:%line] -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE" />
</root> </root>
</configuration> </configuration>

View File

@ -16,7 +16,7 @@
</div> </div>
<br /> <label class="navBarButtonLabel">Pretty</label> <br /> <label class="navBarButtonLabel">Pretty</label>
<div class="btn-group top-buffer" data-toggle="buttons"> <div class="btn-group top-buffer" data-toggle="buttons" id="prettyBtnGroup">
<label class="btn btn-sm btn-default active"> <input <label class="btn btn-sm btn-default active"> <input
type="radio" name="pretty" id="pretty-default" value="" />(default) type="radio" name="pretty" id="pretty-default" value="" />(default)
</label> <label class="btn btn-sm btn-default"> <input </label> <label class="btn btn-sm btn-default"> <input
@ -28,6 +28,11 @@
<script type="text/javascript"> <script type="text/javascript">
$( document ).ready(function() { $( document ).ready(function() {
// Encoding buttons are wider, so set the shorter group to the same width
// so that they wrap at the same time if the page is narrow
$('#prettyBtnGroup').width($('#encodingBtnGroup').width());
<th:block th:switch="${encoding}"> <th:block th:switch="${encoding}">
<th:block th:case="'xml'"> <th:block th:case="'xml'">
$('#encode-xml').trigger("click"); $('#encode-xml').trigger("click");

View File

@ -151,6 +151,22 @@
return retVal; return retVal;
} }
/**
* Gets the first repetition for <b>${child.elementName}</b> (${child.shortName}),
* creating it if it does not already exist.
*
* <p>
* <b>Definition:</b>
* ${child.definition}
* </p>
*/
public ${child.boundDatatype}<${child.bindingClass}> get${child.methodName}FirstRep() {
if (get${child.methodName}().size() == 0) {
add${child.methodName}();
}
return get${child.methodName}().get(0);
}
/** /**
* Add a value for <b>${child.elementName}</b> (${child.shortName}) * Add a value for <b>${child.elementName}</b> (${child.shortName})
* *