Work on narrative generator

This commit is contained in:
James 2014-07-03 18:02:14 -04:00
parent c811c164b5
commit ca0929df07
31 changed files with 500 additions and 119 deletions

View File

@ -60,9 +60,11 @@ public class RuntimeChildAny extends RuntimeChildChoiceDefinition {
if (o1res && o2res) { if (o1res && o2res) {
return theO1.getSimpleName().compareTo(theO2.getSimpleName()); return theO1.getSimpleName().compareTo(theO2.getSimpleName());
} else if (o1res) { } else if (o1res) {
return 1;
}else {
return -1; return -1;
} else if (o1res == false && o2res == false) {
return 0;
}else {
return 1;
} }
}}); }});

View File

@ -221,7 +221,12 @@ public class Bundle extends BaseBundle /* implements IElement */{
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource); RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
if (theResource.getId() != null && StringUtils.isNotBlank(theResource.getId().getValue())) { if (theResource.getId() != null && StringUtils.isNotBlank(theResource.getId().getValue())) {
String title = ResourceMetadataKeyEnum.TITLE.get(theResource);
if (title != null) {
entry.getTitle().setValue(title);
} else {
entry.getTitle().setValue(def.getName() + " " + theResource.getId().getValue()); entry.getTitle().setValue(def.getName() + " " + theResource.getId().getValue());
}
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append(theServerBase); b.append(theServerBase);

View File

@ -25,6 +25,8 @@ import static org.apache.commons.lang3.StringUtils.*;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -129,6 +131,26 @@ public abstract class ResourceMetadataKeyEnum<T> {
} }
}; };
/**
* If present and populated with a string (as an instance of {@link String}),
* this value contains the title for this resource, as supplied in any bundles containing the
* resource.
* <p>
* Values for this key are of type <b>{@link String}</b>
* </p>
*/
public static final ResourceMetadataKeyEnum<String> TITLE = new ResourceMetadataKeyEnum<String>("TITLE") {
@Override
public String get(IResource theResource) {
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE);
}
@Override
public void put(IResource theResource, String theObject) {
theResource.getResourceMetadata().put(TITLE, theObject);
}
};
/** /**
* The value for this key is the bundle entry <b>Updated</b> time. This is * The value for this key is the bundle entry <b>Updated</b> time. This is
@ -182,7 +204,6 @@ public abstract class ResourceMetadataKeyEnum<T> {
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (this == obj) if (this == obj)
@ -200,6 +221,8 @@ public abstract class ResourceMetadataKeyEnum<T> {
return true; return true;
} }
public abstract T get(IResource theResource); public abstract T get(IResource theResource);
@Override @Override
@ -210,6 +233,10 @@ public abstract class ResourceMetadataKeyEnum<T> {
return result; return result;
} }
private String name() {
return myValue;
}
public abstract void put(IResource theResource, T theObject); public abstract void put(IResource theResource, T theObject);
@Override @Override
@ -217,10 +244,6 @@ public abstract class ResourceMetadataKeyEnum<T> {
return myValue; return myValue;
} }
private String name() {
return myValue;
}
private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) { private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
Object retValObj = theResourceMetadata.get(theKey); Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) { if (retValObj == null) {
@ -259,4 +282,18 @@ public abstract class ResourceMetadataKeyEnum<T> {
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName()); throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
} }
private static String getStringFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<String> theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof String) {
if (StringUtils.isBlank(((String) retValObj))) {
return null;
} else {
return (String) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + String.class.getCanonicalName());
}
} }

View File

@ -408,8 +408,10 @@ public class CodingDt
public String getValueAsQueryToken() { public String getValueAsQueryToken() {
if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) { if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) {
return getSystem().getValueAsString() + '|' + getCode().getValueAsString(); return getSystem().getValueAsString() + '|' + getCode().getValueAsString();
} else { } else if (getSystem().getValue()==null) {
return getCode().getValueAsString(); return getCode().getValueAsString();
} else {
return '|' + getCode().getValueAsString();
} }
} }

View File

@ -414,6 +414,8 @@ public class IdentifierDt
public String getValueAsQueryToken() { public String getValueAsQueryToken() {
if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) { if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) {
return getSystem().getValueAsString() + '|' + getValue().getValueAsString(); return getSystem().getValueAsString() + '|' + getValue().getValueAsString();
} else if (getSystem().getValue() == null) {
return getValue().getValueAsString();
} else { } else {
return '|' + getValue().getValueAsString(); return '|' + getValue().getValueAsString();
} }

View File

@ -69,17 +69,100 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
private boolean myIgnoreMissingTemplates = true; private boolean myIgnoreMissingTemplates = true;
private TemplateEngine myProfileTemplateEngine; private TemplateEngine myProfileTemplateEngine;
private HashMap<String, String> myProfileToNarrativeName; private TemplateEngine myTitleTemplateEngine;
private HashMap<Class<?>, String> myClassToNarrativeName; private HashMap<String, String> myProfileToName;
private HashMap<Class<?>, String> myClassToName;
private HashMap<String, String> myNameToNarrativeTemplate; private HashMap<String, String> myNameToNarrativeTemplate;
private boolean myApplyDefaultDatatypeTemplates=true; private boolean myApplyDefaultDatatypeTemplates = true;
private volatile boolean myInitialized; private volatile boolean myInitialized;
private HashMap<String, String> myNameToTitleTemplate;
@Override @Override
public NarrativeDt generateNarrative(IResource theResource) { public NarrativeDt generateNarrative(IResource theResource) {
return generateNarrative(null, theResource); return generateNarrative(null, theResource);
} }
@Override
public String generateTitle(IResource theResource) {
return generateTitle(null, theResource);
}
@Override
public String generateTitle(String theProfile, IResource theResource) {
if (!myInitialized) {
initialize();
}
String name = null;
if (StringUtils.isNotBlank(theProfile)) {
name = myProfileToName.get(theProfile);
}
if (name == null) {
name = myClassToName.get(theResource.getClass());
}
if (name == null) {
if (myIgnoreMissingTemplates) {
ourLog.debug("No title template available for profile: {}", theProfile);
return null;
} else {
throw new DataFormatException("No title template for class " + theResource.getClass().getCanonicalName());
}
}
try {
Context context = new Context();
context.setVariable("resource", theResource);
String result = myTitleTemplateEngine.process(name, context);
StringBuilder b = new StringBuilder();
boolean inTag = false;
for (int i = 0; i < result.length(); i++) {
char nextChar = result.charAt(i);
char prevChar = i > 0 ? result.charAt(i - 1) : '\n';
if (nextChar == '<') {
inTag = true;
continue;
} else if (inTag) {
if (nextChar == '>') {
inTag = false;
}
continue;
} else if (nextChar <= ' ') {
if (prevChar <= ' ' || prevChar == '>') {
continue;
} else {
b.append(' ');
}
} else {
b.append(nextChar);
}
}
while (b.length() > 0 && b.charAt(b.length()-1) == ' ') {
b.setLength(b.length() - 1);
}
result = b.toString();
if (result.startsWith("<") && result.contains(">")) {
result = result.substring(result.indexOf('>') + 1);
}
if (result.endsWith(">") && result.contains("<")) {
result = result.substring(0, result.lastIndexOf('<'));
}
return result;
} catch (Exception e) {
if (myIgnoreFailures) {
ourLog.error("Failed to generate narrative", e);
return "No title available - Error: " + e.getMessage();
} else {
throw new DataFormatException(e);
}
}
}
@Override @Override
public NarrativeDt generateNarrative(String theProfile, IResource theResource) { public NarrativeDt generateNarrative(String theProfile, IResource theResource) {
if (!myInitialized) { if (!myInitialized) {
@ -88,10 +171,10 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
String name = null; String name = null;
if (StringUtils.isNotBlank(theProfile)) { if (StringUtils.isNotBlank(theProfile)) {
name = myProfileToNarrativeName.get(theProfile); name = myProfileToName.get(theProfile);
} }
if (name == null) { if (name == null) {
name = myClassToNarrativeName.get(theResource.getClass()); name = myClassToName.get(theResource.getClass());
} }
if (name == null) { if (name == null) {
@ -131,9 +214,10 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
if (myInitialized) { if (myInitialized) {
return; return;
} }
myProfileToNarrativeName = new HashMap<String, String>(); myProfileToName = new HashMap<String, String>();
myClassToNarrativeName = new HashMap<Class<?>, String>(); myClassToName = new HashMap<Class<?>, String>();
myNameToNarrativeTemplate = new HashMap<String, String>(); myNameToNarrativeTemplate = new HashMap<String, String>();
myNameToTitleTemplate = new HashMap<String, String>();
List<String> propFileName = getPropertyFile(); List<String> propFileName = getPropertyFile();
@ -160,6 +244,15 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
myProfileTemplateEngine.setDialect(dialect); myProfileTemplateEngine.setDialect(dialect);
myProfileTemplateEngine.initialize(); myProfileTemplateEngine.initialize();
} }
{
myTitleTemplateEngine = new TemplateEngine();
TemplateResolver resolver = new TemplateResolver();
resolver.setResourceResolver(new TitleResourceResolver());
myTitleTemplateEngine.setTemplateResolver(resolver);
StandardDialect dialect = new StandardDialect();
myTitleTemplateEngine.setDialect(dialect);
myTitleTemplateEngine.initialize();
}
myInitialized = true; myInitialized = true;
} }
@ -167,12 +260,9 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
protected abstract List<String> getPropertyFile(); protected abstract List<String> getPropertyFile();
/** /**
* If set to <code>true</code> (which is the default), most whitespace will * If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative before it is returned.
* be trimmed from the generated narrative before it is returned.
* <p> * <p>
* Note that in order to preserve formatting, not all whitespace is trimmed. * Note that in order to preserve formatting, not all whitespace is trimmed. Repeated whitespace characters (e.g. "\n \n ") will be trimmed to a single space.
* Repeated whitespace characters (e.g. "\n \n ") will be
* trimmed to a single space.
* </p> * </p>
*/ */
public boolean isCleanWhitespace() { public boolean isCleanWhitespace() {
@ -180,30 +270,24 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
} }
/** /**
* If set to <code>true</code>, which is the default, if any failure occurs * If set to <code>true</code>, which is the default, if any failure occurs during narrative generation the generator will suppress any generated exceptions, and simply return a default narrative
* during narrative generation the generator will suppress any generated * indicating that no narrative is available.
* exceptions, and simply return a default narrative indicating that no
* narrative is available.
*/ */
public boolean isIgnoreFailures() { public boolean isIgnoreFailures() {
return myIgnoreFailures; return myIgnoreFailures;
} }
/** /**
* If set to true, will return an empty narrative block for any profiles * If set to true, will return an empty narrative block for any profiles where no template is available
* where no template is available
*/ */
public boolean isIgnoreMissingTemplates() { public boolean isIgnoreMissingTemplates() {
return myIgnoreMissingTemplates; return myIgnoreMissingTemplates;
} }
/** /**
* If set to <code>true</code> (which is the default), most whitespace will * If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative before it is returned.
* be trimmed from the generated narrative before it is returned.
* <p> * <p>
* Note that in order to preserve formatting, not all whitespace is trimmed. * Note that in order to preserve formatting, not all whitespace is trimmed. Repeated whitespace characters (e.g. "\n \n ") will be trimmed to a single space.
* Repeated whitespace characters (e.g. "\n \n ") will be
* trimmed to a single space.
* </p> * </p>
*/ */
public void setCleanWhitespace(boolean theCleanWhitespace) { public void setCleanWhitespace(boolean theCleanWhitespace) {
@ -211,18 +295,15 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
} }
/** /**
* If set to <code>true</code>, which is the default, if any failure occurs * If set to <code>true</code>, which is the default, if any failure occurs during narrative generation the generator will suppress any generated exceptions, and simply return a default narrative
* during narrative generation the generator will suppress any generated * indicating that no narrative is available.
* exceptions, and simply return a default narrative indicating that no
* narrative is available.
*/ */
public void setIgnoreFailures(boolean theIgnoreFailures) { public void setIgnoreFailures(boolean theIgnoreFailures) {
myIgnoreFailures = theIgnoreFailures; myIgnoreFailures = theIgnoreFailures;
} }
/** /**
* If set to true, will return an empty narrative block for any profiles * If set to true, will return an empty narrative block for any profiles where no template is available
* where no template is available
*/ */
public void setIgnoreMissingTemplates(boolean theIgnoreMissingTemplates) { public void setIgnoreMissingTemplates(boolean theIgnoreMissingTemplates) {
myIgnoreMissingTemplates = theIgnoreMissingTemplates; myIgnoreMissingTemplates = theIgnoreMissingTemplates;
@ -245,13 +326,22 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
String narrativePropName = name + ".narrative"; String narrativePropName = name + ".narrative";
String narrativeName = file.getProperty(narrativePropName); String narrativeName = file.getProperty(narrativePropName);
if (isBlank(narrativeName)) { String titlePropName = name + ".title";
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' in file " + propFileName); String titleName = file.getProperty(titlePropName);
if (isBlank(narrativeName) && isBlank(titleName)) {
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' or '" + titlePropName + "' in file " + propFileName);
} }
myProfileToName.put(file.getProperty(nextKey), name);
if (StringUtils.isNotBlank(narrativeName)) {
String narrative = IOUtils.toString(loadResource(narrativeName)); String narrative = IOUtils.toString(loadResource(narrativeName));
myProfileToNarrativeName.put(file.getProperty(nextKey), name);
myNameToNarrativeTemplate.put(name, narrative); myNameToNarrativeTemplate.put(name, narrative);
}
if (StringUtils.isNotBlank(titleName)) {
String title = IOUtils.toString(loadResource(titleName));
myNameToTitleTemplate.put(name, title);
}
} else if (nextKey.endsWith(".class")) { } else if (nextKey.endsWith(".class")) {
@ -262,9 +352,9 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
String className = file.getProperty(nextKey); String className = file.getProperty(nextKey);
Class<?> dtClass; Class<?> clazz;
try { try {
dtClass = Class.forName(className); clazz = Class.forName(className);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
ourLog.warn("Unknown datatype class '{}' identified in narrative file {}", name, propFileName); ourLog.warn("Unknown datatype class '{}' identified in narrative file {}", name, propFileName);
continue; continue;
@ -272,16 +362,27 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
String narrativePropName = name + ".narrative"; String narrativePropName = name + ".narrative";
String narrativeName = file.getProperty(narrativePropName); String narrativeName = file.getProperty(narrativePropName);
if (isBlank(narrativeName)) { String titlePropName = name + ".title";
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' in file " + propFileName); String titleName = file.getProperty(titlePropName);
if (isBlank(narrativeName) && isBlank(titleName)) {
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' or '" + titlePropName + "' in file " + propFileName);
} }
myClassToName.put(clazz, name);
if (StringUtils.isNotBlank(narrativeName)) {
String narrative = IOUtils.toString(loadResource(narrativeName)); String narrative = IOUtils.toString(loadResource(narrativeName));
myClassToNarrativeName.put(dtClass, name);
myNameToNarrativeTemplate.put(name, narrative); myNameToNarrativeTemplate.put(name, narrative);
}
if (StringUtils.isNotBlank(titleName)) {
String title = IOUtils.toString(loadResource(titleName));
myNameToTitleTemplate.put(name, title);
}
} else if (nextKey.endsWith(".narrative")) { } else if (nextKey.endsWith(".narrative")) {
continue; continue;
} else if (nextKey.endsWith(".title")) {
continue;
} else { } else {
throw new ConfigurationException("Invalid property name: " + nextKey); throw new ConfigurationException("Invalid property name: " + nextKey);
} }
@ -379,14 +480,13 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
final IStandardExpression expression = expressionParser.parseExpression(configuration, theArguments, attributeValue); final IStandardExpression expression = expressionParser.parseExpression(configuration, theArguments, attributeValue);
final Object value = expression.execute(configuration, theArguments); final Object value = expression.execute(configuration, theArguments);
theElement.removeAttribute(theAttributeName); theElement.removeAttribute(theAttributeName);
theElement.clearChildren(); theElement.clearChildren();
Context context = new Context(); Context context = new Context();
context.setVariable("resource", value); context.setVariable("resource", value);
String name = myClassToNarrativeName.get(value.getClass()); String name = myClassToName.get(value.getClass());
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());
@ -438,4 +538,21 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
return new ReaderInputStream(new StringReader(template)); return new ReaderInputStream(new StringReader(template));
} }
} }
private final class TitleResourceResolver implements IResourceResolver {
@Override
public String getName() {
return getClass().getCanonicalName();
}
@Override
public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) {
String template = myNameToTitleTemplate.get(theName);
if (template == null) {
ourLog.info("No narative template for resource profile: {}", theName);
return new ReaderInputStream(new StringReader(""));
}
return new ReaderInputStream(new StringReader(template));
}
}
} }

View File

@ -64,4 +64,6 @@ public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGe
return myUseHapiServerConformanceNarrative; return myUseHapiServerConformanceNarrative;
} }
} }

View File

@ -30,4 +30,8 @@ public interface INarrativeGenerator {
NarrativeDt generateNarrative(IResource theResource); NarrativeDt generateNarrative(IResource theResource);
String generateTitle(IResource theResource);
String generateTitle(String theProfile, IResource theResource);
} }

View File

@ -222,6 +222,7 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.writeEnd(); // entry array eventWriter.writeEnd(); // entry array
eventWriter.writeEnd(); eventWriter.writeEnd();
eventWriter.flush();
eventWriter.close(); eventWriter.close();
} }
@ -506,6 +507,7 @@ 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
@ -536,6 +538,7 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.writeEnd(); eventWriter.writeEnd();
eventWriter.flush(); eventWriter.flush();
eventWriter.close();
} }
/** /**

View File

@ -417,6 +417,9 @@ class ParserState<T> {
if (myEntry.getUpdated().isEmpty() == false) { if (myEntry.getUpdated().isEmpty() == false) {
ResourceMetadataKeyEnum.UPDATED.put(myEntry.getResource(), myEntry.getUpdated()); ResourceMetadataKeyEnum.UPDATED.put(myEntry.getResource(), myEntry.getUpdated());
} }
ResourceMetadataKeyEnum.TITLE.put(myEntry.getResource(), myEntry.getTitle().getValue());
if (myEntry.getCategories().isEmpty() == false) { if (myEntry.getCategories().isEmpty() == false) {
TagList tagList = new TagList(); TagList tagList = new TagList();
for (Tag next : myEntry.getCategories()) { for (Tag next : myEntry.getCategories()) {
@ -608,7 +611,6 @@ class ParserState<T> {
myPreResourceState = thePreResourceState; myPreResourceState = thePreResourceState;
} }
@SuppressWarnings("unused")
public void attributeValue(String theName, String theValue) throws DataFormatException { public void attributeValue(String theName, String theValue) throws DataFormatException {
// ignore by default // ignore by default
} }
@ -617,7 +619,6 @@ class ParserState<T> {
// ignore by default // ignore by default
} }
@SuppressWarnings("unused")
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
// ignore by default // ignore by default
} }
@ -657,7 +658,7 @@ class ParserState<T> {
myStack = theState; myStack = theState;
} }
public void string(@SuppressWarnings("unused") String theData) { public void string(String theData) {
// ignore by default // ignore by default
} }
@ -665,7 +666,7 @@ class ParserState<T> {
// allow an implementor to override // allow an implementor to override
} }
public void xmlEvent(@SuppressWarnings("unused") XMLEvent theNextEvent) { public void xmlEvent(XMLEvent theNextEvent) {
// ignore // ignore
} }

View File

@ -64,4 +64,10 @@ public class IdentifierListParam implements IQueryParameterOr {
} }
} }
public void addIdentifier(IdentifierDt theIdentifierDt) {
if (theIdentifierDt != null && theIdentifierDt.isEmpty() == false) {
getIdentifiers().add(theIdentifierDt);
}
}
} }

View File

@ -160,6 +160,8 @@ public class SearchParameter extends BaseQueryParameter {
myParamType = SearchParamTypeEnum.QUANTITY; myParamType = SearchParamTypeEnum.QUANTITY;
} else if (ReferenceParam.class.isAssignableFrom(type)) { } else if (ReferenceParam.class.isAssignableFrom(type)) {
myParamType = SearchParamTypeEnum.REFERENCE; myParamType = SearchParamTypeEnum.REFERENCE;
} else if (IdentifierListParam.class.isAssignableFrom(type)) {
myParamType = SearchParamTypeEnum.TOKEN;
} else { } else {
throw new ConfigurationException("Unknown search parameter type: " + type); throw new ConfigurationException("Unknown search parameter type: " + type);
} }

View File

@ -45,6 +45,7 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
@ -557,7 +558,7 @@ public class RestfulServer extends HttpServlet {
statusCode=((BaseServerResponseException) e).getStatusCode(); statusCode=((BaseServerResponseException) e).getStatusCode();
issue.getDetails().setValue(e.getMessage()); issue.getDetails().setValue(e.getMessage());
} else { } else {
ourLog.warn("Failure during REST processing: {}", e.toString()); ourLog.error("Failure during REST processing: {}"+ e.toString(), e);
issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e)); issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
} }
@ -779,6 +780,14 @@ public class RestfulServer extends HttpServlet {
bundle.getLinkSelf().setValue(theCompleteUrl); bundle.getLinkSelf().setValue(theCompleteUrl);
for (IResource next : theResult) { for (IResource next : theResult) {
if (theContext.getNarrativeGenerator() != null) {
String title = theContext.getNarrativeGenerator().generateTitle(next);
if (StringUtils.isNotBlank(title)) {
ResourceMetadataKeyEnum.TITLE.put(next, title);
}
}
bundle.addResource(next, theContext, theServerBase); bundle.addResource(next, theContext, theServerBase);
} }
@ -1019,6 +1028,7 @@ public class RestfulServer extends HttpServlet {
RestfulServer.getNewParser(theServer.getFhirContext(), theResponseEncoding, thePrettyPrint, theNarrativeMode).encodeBundleToWriter(bundle, writer); RestfulServer.getNewParser(theServer.getFhirContext(), theResponseEncoding, thePrettyPrint, theNarrativeMode).encodeBundleToWriter(bundle, writer);
} }
} finally { } finally {
writer.flush();
writer.close(); writer.close();
} }
} }
@ -1093,6 +1103,7 @@ public class RestfulServer extends HttpServlet {
RestfulServer.getNewParser(theServer.getFhirContext(), theResponseEncoding, thePrettyPrint, theNarrativeMode).encodeResourceToWriter(theResource, writer); RestfulServer.getNewParser(theServer.getFhirContext(), theResponseEncoding, thePrettyPrint, theNarrativeMode).encodeResourceToWriter(theResource, writer);
} }
} finally { } finally {
writer.flush();
writer.close(); writer.close();
} }
} }

View File

@ -32,6 +32,9 @@ quantity.narrative=classpath:ca/uhn/fhir/narrative/QuantityDt.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
diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport
diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html
diagnosticreport.title=classpath:ca/uhn/fhir/narrative/title/DiagnosticReport.html

View File

@ -0,0 +1,9 @@
<div>
<th:block th:if="${not resource.name.text.empty}" th:text="${resource.name.text.value}"/>
<th:block th:if=" ${resource.name.text.empty} and ${not resource.name.codingFirstRep.display.empty}" th:text="${resource.name.codingFirstRep.display}"/>
<th:block th:if= "${resource.name.text.empty} and ${resource.name.codingFirstRep.display.empty}" th:text="Untitled Diagnostic Report"/>
<th:block th:if="${not resource.status.empty}" th:text="' - ' + ${resource.status.value}"/>
<th:block th:text="' - ' + ${resource.result.size} + ' observations'"/>
</div>

View File

@ -0,0 +1,9 @@
<div>
<th:block th:each="prefix : ${resource.nameFirstRep.prefix}" th:text="${prefix.value} + ' '">Dr</th:block>
<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:text="${resource.identifierFirstRep.value.value}">8708660</th:block>)
</th:block>
</div>

View File

@ -0,0 +1,41 @@
package ca.uhn.fhir.model.primitive;
import static org.junit.Assert.*;
import org.junit.Test;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
public class CodingDtTest {
@Test
public void testTokenWithPipeInValue() {
CodingDt dt = new CodingDt();
dt.setValueAsQueryToken(null, "a|b|c");
assertEquals("a", dt.getSystem().getValueAsString());
assertEquals("b|c", dt.getCode().getValue());
assertEquals("a|b|c", dt.getValueAsQueryToken());
}
@Test
public void testTokenWithPipeInValueAndNoSystem() {
CodingDt dt = new CodingDt();
dt.setValueAsQueryToken(null, "|b|c");
assertEquals("", dt.getSystem().getValueAsString());
assertEquals("b|c", dt.getCode().getValue());
assertEquals("|b|c", dt.getValueAsQueryToken());
}
@Test
public void testTokenNoSystem() {
CodingDt dt = new CodingDt();
dt.setValueAsQueryToken(null, "c");
assertEquals(null, dt.getSystem().getValueAsString());
assertEquals("c", dt.getCode().getValue());
assertEquals("c", dt.getValueAsQueryToken());
}
}

View File

@ -28,4 +28,14 @@ public class IdentifierDtTest {
assertEquals("|b|c", dt.getValueAsQueryToken()); assertEquals("|b|c", dt.getValueAsQueryToken());
} }
@Test
public void testTokenNoSystem() {
IdentifierDt dt = new IdentifierDt();
dt.setValueAsQueryToken(null, "c");
assertEquals(null, dt.getSystem().getValueAsString());
assertEquals("c", dt.getValue().getValue());
assertEquals("c", dt.getValueAsQueryToken());
}
} }

View File

@ -46,9 +46,12 @@ public class DefaultThymeleafNarrativeGeneratorTest {
value.setBirthDate(new Date(), TemporalPrecisionEnum.DAY); value.setBirthDate(new Date(), TemporalPrecisionEnum.DAY);
String output = gen.generateNarrative("http://hl7.org/fhir/profiles/Patient", value).getDiv().getValueAsString(); String output = gen.generateNarrative(value).getDiv().getValueAsString();
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> joe john <b>BLOW </b></div>"));
ourLog.info(output); String title = gen.generateTitle(value);
assertEquals("joe john BLOW (123456)", title);
ourLog.info(title);
} }
@Test @Test
@ -105,6 +108,11 @@ public class DefaultThymeleafNarrativeGeneratorTest {
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 Diagnostic Report </div>"));
String title = gen.generateTitle(value);
ourLog.info(title);
assertEquals("Some Diagnostic Report - final - 2 observations", title);
// Now try it with the parser // Now try it with the parser
FhirContext context = new FhirContext(); FhirContext context = new FhirContext();

View File

@ -23,9 +23,8 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider; import ca.uhn.fhir.testutil.RandomServerPortProvider;
@ -36,7 +35,6 @@ import ca.uhn.fhir.testutil.RandomServerPortProvider;
public class SearchTest { public class SearchTest {
private static CloseableHttpClient ourClient; private static CloseableHttpClient ourClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchTest.class);
private static int ourPort; private static int ourPort;
private static Server ourServer; private static Server ourServer;
private static FhirContext ourCtx = new FhirContext(); private static FhirContext ourCtx = new FhirContext();
@ -46,12 +44,14 @@ public class SearchTest {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa"); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
HttpResponse status = ourClient.execute(httpGet); HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent()); String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent); Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size()); assertEquals(1, bundle.getEntries().size());
Patient p = bundle.getResources(Patient.class).get(0); Patient p = bundle.getResources(Patient.class).get(0);
assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString()); assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString());
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
} }
@AfterClass @AfterClass
@ -68,6 +68,8 @@ public class SearchTest {
ServletHandler proxyHandler = new ServletHandler(); ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(); RestfulServer servlet = new RestfulServer();
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
servlet.setResourceProviders(patientProvider); servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet); ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*"); proxyHandler.addServletWithMapping(servletHolder, "/*");
@ -92,6 +94,7 @@ public class SearchTest {
Patient patient = new Patient(); Patient patient = new Patient();
patient.setId("1"); patient.setId("1");
patient.addIdentifier("system", "identifier123");
patient.addName().addFamily("id"+theParam.getValue()); patient.addName().addFamily("id"+theParam.getValue());
retVal.add(patient); retVal.add(patient);
return retVal; return retVal;

View File

@ -747,6 +747,9 @@ public abstract class BaseFhirDao {
} }
} }
String title = ResourceMetadataKeyEnum.TITLE.get(theResource);
theEntity.setTitle(title);
} }
protected ResourceTable toEntity(IResource theResource) { protected ResourceTable toEntity(IResource theResource) {
@ -784,6 +787,10 @@ public abstract class BaseFhirDao {
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished()); retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished());
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated()); retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated());
if (theEntity.getTitle()!=null) {
ResourceMetadataKeyEnum.TITLE.put(retVal, theEntity.getTitle());
}
if (theEntity.getDeleted()!=null) { if (theEntity.getDeleted()!=null) {
ResourceMetadataKeyEnum.DELETED_AT.put(retVal, new InstantDt(theEntity.getDeleted())); ResourceMetadataKeyEnum.DELETED_AT.put(retVal, new InstantDt(theEntity.getDeleted()));
} }

View File

@ -431,7 +431,10 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>()); ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>());
if (StringUtils.isNotBlank(system)) { if (StringUtils.isNotBlank(system)) {
singleCodePredicates.add(builder.equal(from.get("mySystem"), system)); singleCodePredicates.add(builder.equal(from.get("mySystem"), system));
} else if (system == null) {
// don't check the system
} else { } else {
// If the system is "", we only match on null systems
singleCodePredicates.add(builder.isNull(from.get("mySystem"))); singleCodePredicates.add(builder.isNull(from.get("mySystem")));
} }
if (StringUtils.isNotBlank(code)) { if (StringUtils.isNotBlank(code)) {

View File

@ -35,6 +35,17 @@ public class SearchParameterMap extends HashMap<String, List<List<IQueryParamete
} }
} }
public void add(String theName, IQueryParameterOr theOr) {
if (theOr == null) {
return;
}
if (!containsKey(theName)) {
put(theName, new ArrayList<List<IQueryParameterType>>());
}
get(theName).add(theOr.getValuesAsQueryTokens());
}
public void add(String theName, IQueryParameterType theParam) { public void add(String theName, IQueryParameterType theParam) {
if (theParam == null) { if (theParam == null) {
return; return;

View File

@ -17,6 +17,8 @@ import ca.uhn.fhir.model.primitive.InstantDt;
@MappedSuperclass @MappedSuperclass
public abstract class BaseHasResource { public abstract class BaseHasResource {
private static final int MAX_TITLE_LENGTH = 100;
@Column(name = "RES_DELETED_AT", nullable = true) @Column(name = "RES_DELETED_AT", nullable = true)
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date myDeleted; private Date myDeleted;
@ -33,6 +35,9 @@ public abstract class BaseHasResource {
@Lob() @Lob()
private byte[] myResource; private byte[] myResource;
@Column(name = "RES_TITLE", nullable = true, length = MAX_TITLE_LENGTH)
private String myTitle;
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
@Column(name = "RES_UPDATED", nullable = false) @Column(name = "RES_UPDATED", nullable = false)
private Date myUpdated; private Date myUpdated;
@ -61,6 +66,10 @@ public abstract class BaseHasResource {
public abstract Collection<? extends BaseTag> getTags(); public abstract Collection<? extends BaseTag> getTags();
public String getTitle() {
return myTitle;
}
public InstantDt getUpdated() { public InstantDt getUpdated() {
return new InstantDt(myUpdated); return new InstantDt(myUpdated);
} }
@ -87,6 +96,10 @@ public abstract class BaseHasResource {
myResource = theResource; myResource = theResource;
} }
public void setTitle(String theTitle) {
myTitle = theTitle;
}
public void setUpdated(Date theUpdated) { public void setUpdated(Date theUpdated) {
myUpdated = theUpdated; myUpdated = theUpdated;
} }

View File

@ -8,7 +8,10 @@ import org.apache.commons.lang3.StringUtils;
@Entity @Entity
@Table(name = "HFJ_SPIDX_TOKEN" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */) @Table(name = "HFJ_SPIDX_TOKEN" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_TOKEN", indexes = { @org.hibernate.annotations.Index(name = "IDX_SP_TOKEN", columnNames = { "RES_TYPE", "SP_NAME", "SP_SYSTEM", "SP_VALUE" }) }) @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_TOKEN", indexes = {
@org.hibernate.annotations.Index(name = "IDX_SP_TOKEN", columnNames = { "RES_TYPE", "SP_NAME", "SP_SYSTEM", "SP_VALUE" }),
@org.hibernate.annotations.Index(name = "IDX_SP_TOKEN_UNQUAL", columnNames = { "RES_TYPE", "SP_NAME", "SP_VALUE" })
})
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam { public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -252,6 +252,7 @@ public class ResourceTable extends BaseHasResource implements Serializable {
retVal.setResourceType(myResourceType); retVal.setResourceType(myResourceType);
retVal.setVersion(myVersion); retVal.setVersion(myVersion);
retVal.setTitle(getTitle());
retVal.setPublished(getPublished()); retVal.setPublished(getPublished());
retVal.setUpdated(getUpdated()); retVal.setUpdated(getUpdated());
retVal.setEncoding(getEncoding()); retVal.setEncoding(getEncoding());

View File

@ -39,7 +39,9 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.gclient.IQuery;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.IdentifierListParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam; import ca.uhn.fhir.rest.param.QualifiedDateParam;
import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.StringParam;
@ -122,6 +124,50 @@ public class FhirResourceDaoTest {
assertEquals(o1id.toUnqualifiedVersionless(), p1.getManagingOrganization().getReference().toUnqualifiedVersionless()); assertEquals(o1id.toUnqualifiedVersionless(), p1.getManagingOrganization().getReference().toUnqualifiedVersionless());
} }
@Test
public void testSearchTokenParam() {
Patient patient = new Patient();
patient.addIdentifier("urn:system", "testSearchTokenParam001");
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam1");
ourPatientDao.create(patient);
patient = new Patient();
patient.addIdentifier("urn:system", "testSearchTokenParam002");
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam2");
ourPatientDao.create(patient);
{
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testSearchTokenParam001"));
IBundleProvider retrieved = ourPatientDao.search(map);
assertEquals(1, retrieved.size());
}
{
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_IDENTIFIER, new IdentifierDt(null, "testSearchTokenParam001"));
IBundleProvider retrieved = ourPatientDao.search(map);
assertEquals(1, retrieved.size());
}
{
SearchParameterMap map = new SearchParameterMap();
IdentifierListParam listParam = new IdentifierListParam();
listParam.addIdentifier(new IdentifierDt("urn:system", "testSearchTokenParam001"));
listParam.addIdentifier(new IdentifierDt("urn:system", "testSearchTokenParam002"));
map.add(Patient.SP_IDENTIFIER, listParam);
IBundleProvider retrieved = ourPatientDao.search(map);
assertEquals(2, retrieved.size());
}
{
SearchParameterMap map = new SearchParameterMap();
IdentifierListParam listParam = new IdentifierListParam();
listParam.addIdentifier(new IdentifierDt(null, "testSearchTokenParam001"));
listParam.addIdentifier(new IdentifierDt("urn:system", "testSearchTokenParam002"));
map.add(Patient.SP_IDENTIFIER, listParam);
IBundleProvider retrieved = ourPatientDao.search(map);
assertEquals(2, retrieved.size());
}
}
@Test @Test
public void testIdParam() { public void testIdParam() {
Patient patient = new Patient(); Patient patient = new Patient();
@ -360,6 +406,7 @@ public class FhirResourceDaoTest {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier("urn:system", "001"); patient.addIdentifier("urn:system", "001");
patient.addName().addFamily("testSearchNameParam01Fam").addGiven("testSearchNameParam01Giv"); patient.addName().addFamily("testSearchNameParam01Fam").addGiven("testSearchNameParam01Giv");
ResourceMetadataKeyEnum.TITLE.put(patient, "P1TITLE");
id1 = ourPatientDao.create(patient).getId(); id1 = ourPatientDao.create(patient).getId();
} }
{ {
@ -374,6 +421,7 @@ public class FhirResourceDaoTest {
List<Patient> patients = toList(ourPatientDao.search(params)); List<Patient> patients = toList(ourPatientDao.search(params));
assertEquals(1, patients.size()); assertEquals(1, patients.size());
assertEquals(id1.getIdPart(), patients.get(0).getId().getIdPart()); assertEquals(id1.getIdPart(), patients.get(0).getId().getIdPart());
assertEquals("P1TITLE", ResourceMetadataKeyEnum.TITLE.get(patients.get(0)));
// Given name shouldn't return for family param // Given name shouldn't return for family param
params = new HashMap<String, IQueryParameterType>(); params = new HashMap<String, IQueryParameterType>();

View File

@ -1,15 +1,15 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<div th:fragment="banner" class="well"> <div th:fragment="banner" class="well">
<th:block th:if="${serverEntry.key} == 'home'">
<p> <p>
This is the home for the FHIR test server operated by This is the home for the FHIR test server operated by
<a href="http://uhn.ca">University Health Network</a>. <a href="http://uhn.ca">University Health Network</a>. This server
</p> (and the testing application you are currently using to access it)
<p> is entirely built using
<b style="color: red;">This is not a production server!</b> <a href="https://github.com/jamesagnew/hapi-fhir">HAPI-FHIR</a>,
Do not store any information here that contains personal health information a 100% open-source Java implementation of the
or otherwise confidential information. This server will be regularly purged <a href="http://hl7.org/implement/standards/fhir/">FHIR specification</a>.
and reloaded with fixed test data.
</p> </p>
<p> <p>
Here are some things you might wish to try: Here are some things you might wish to try:
@ -31,5 +31,22 @@
(use the <b>Server</b> menu at the top of the page to see a list of public FHIR servers) (use the <b>Server</b> menu at the top of the page to see a list of public FHIR servers)
</li> </li>
</ul> </ul>
</th:block>
<th:block th:if="${serverEntry.key} != 'home'">
<p>
You are accessing the public FHIR server
<b th:text="${baseName}"/>. This server is hosted elsewhere on the internet
but is being accessed using
</p>
</th:block>
<p>
<b style="color: red;">
<span class="glyphicon glyphicon-warning-sign/>
This is not a production server!
</b>
Do not store any information here that contains personal health information
or any other confidential information. This server will be regularly purged
and reloaded with fixed test data.
</p>
</div> </div>
</html> </html>

View File

@ -11,6 +11,7 @@
<bean class="ca.uhn.fhir.to.TesterConfig"> <bean class="ca.uhn.fhir.to.TesterConfig">
<property name="servers"> <property name="servers">
<list> <list>
<value>test, TEST, http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/</value>
<value>home , Localhost Server , http://localhost:8887/fhir/context </value> <value>home , Localhost Server , http://localhost:8887/fhir/context </value>
<value>hi , Health Intersections , http://fhir.healthintersections.com.au/open</value> <value>hi , Health Intersections , http://fhir.healthintersections.com.au/open</value>
<value>furore , Spark - Furore Reference Server , http://spark.furore.com/fhir</value> <value>furore , Spark - Furore Reference Server , http://spark.furore.com/fhir</value>

View File

@ -34,7 +34,7 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
#if (${param.type} == 'string' ) #if (${param.type} == 'string' )
StringParam the${param.nameCapitalized}, StringParam the${param.nameCapitalized},
#elseif (${param.type} == 'token' ) #elseif (${param.type} == 'token' )
IdentifierDt the${param.nameCapitalized}, IdentifierListParam the${param.nameCapitalized},
#elseif (${param.type} == 'date' ) #elseif (${param.type} == 'date' )
DateRangeParam the${param.nameCapitalized}, DateRangeParam the${param.nameCapitalized},
#elseif (${param.type} == 'quantity' ) #elseif (${param.type} == 'quantity' )