Support displayLanguage through validator framework

This commit is contained in:
Grahame Grieve 2019-06-16 14:02:09 +10:00
parent 4ff9e0626a
commit 23eb7282bd
18 changed files with 278 additions and 70 deletions

View File

@ -82,6 +82,7 @@ import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.NarrativeGenerator;
import org.hl7.fhir.r5.utils.ToolingExtensions;
@ -215,6 +216,7 @@ public class ProfileUtilities extends TranslatingUtilities {
private ProfileKnowledgeProvider pkp;
private boolean igmode;
private boolean exception;
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public ProfileUtilities(IWorkerContext context, List<ValidationMessage> messages, ProfileKnowledgeProvider pkp) {
super();
@ -2787,13 +2789,13 @@ public class ProfileUtilities extends TranslatingUtilities {
private Piece describeCoded(HierarchicalTableGenerator gen, Type fixed) {
if (fixed instanceof Coding) {
Coding c = (Coding) fixed;
ValidationResult vr = context.validateCode(c.getSystem(), c.getCode(), c.getDisplay());
ValidationResult vr = context.validateCode(terminologyServiceOptions , c.getSystem(), c.getCode(), c.getDisplay());
if (vr.getDisplay() != null)
return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen");
} else if (fixed instanceof CodeableConcept) {
CodeableConcept cc = (CodeableConcept) fixed;
for (Coding c : cc.getCoding()) {
ValidationResult vr = context.validateCode(c.getSystem(), c.getCode(), c.getDisplay());
ValidationResult vr = context.validateCode(terminologyServiceOptions, c.getSystem(), c.getCode(), c.getDisplay());
if (vr.getDisplay() != null)
return gen.new Piece(null, " ("+vr.getDisplay()+")", null).addStyle("color: darkgreen");
}
@ -4236,4 +4238,16 @@ public class ProfileUtilities extends TranslatingUtilities {
public void setThrowException(boolean exception) {
this.exception = exception;
}
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public void setTerminologyServiceOptions(TerminologyServiceOptions terminologyServiceOptions) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
}

View File

@ -68,6 +68,7 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent;
import org.hl7.fhir.r5.terminologies.TerminologyClient;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
@ -458,39 +459,39 @@ public abstract class BaseWorkerContext implements IWorkerContext {
// --- validate code -------------------------------------------------------------------------------
@Override
public ValidationResult validateCode(String system, String code, String display) {
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display) {
Coding c = new Coding(system, code, display);
return validateCode(c, null);
return validateCode(options, c, null);
}
@Override
public ValidationResult validateCode(String system, String code, String display, ValueSet vs) {
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ValueSet vs) {
Coding c = new Coding(system, code, display);
return validateCode(c, vs);
return validateCode(options, c, vs);
}
@Override
public ValidationResult validateCode(String code, ValueSet vs) {
public ValidationResult validateCode(TerminologyServiceOptions options, String code, ValueSet vs) {
Coding c = new Coding(null, code, null);
return doValidateCode(c, vs, true);
return doValidateCode(options, c, vs, true);
}
@Override
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi) {
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ConceptSetComponent vsi) {
Coding c = new Coding(system, code, display);
ValueSet vs = new ValueSet();
vs.setUrl(Utilities.makeUuidUrn());
vs.getCompose().addInclude(vsi);
return validateCode(c, vs);
return validateCode(options, c, vs);
}
@Override
public ValidationResult validateCode(Coding code, ValueSet vs) {
return doValidateCode(code, vs, false);
public ValidationResult validateCode(TerminologyServiceOptions options, Coding code, ValueSet vs) {
return doValidateCode(options, code, vs, false);
}
public ValidationResult doValidateCode(Coding code, ValueSet vs, boolean implySystem) {
CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(code, vs) : null;
public ValidationResult doValidateCode(TerminologyServiceOptions options, Coding code, ValueSet vs, boolean implySystem) {
CacheToken cacheToken = txCache != null ? txCache.generateValidationToken(options, code, vs) : null;
ValidationResult res = null;
if (txCache != null)
res = txCache.getValidation(cacheToken);
@ -499,7 +500,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
// ok, first we try to validate locally
try {
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, this);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
res = vsc.validateCode(code);
if (txCache != null)
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
@ -520,6 +521,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
pIn.addParameter().setName("coding").setValue(code);
if (implySystem)
pIn.addParameter().setName("implySystem").setValue(new BooleanType(true));
if (options != null)
options.updateParameters(pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog == null ? null : txLog.getLastId());
@ -530,15 +533,15 @@ public abstract class BaseWorkerContext implements IWorkerContext {
}
@Override
public ValidationResult validateCode(CodeableConcept code, ValueSet vs) {
CacheToken cacheToken = txCache.generateValidationToken(code, vs);
public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) {
CacheToken cacheToken = txCache.generateValidationToken(options, code, vs);
ValidationResult res = txCache.getValidation(cacheToken);
if (res != null)
return res;
// ok, first we try to validate locally
try {
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, this);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this);
res = vsc.validateCode(code);
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
return res;
@ -552,6 +555,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
try {
Parameters pIn = new Parameters();
pIn.addParameter().setName("codeableConcept").setValue(code);
if (options != null)
options.updateParameters(pIn);
res = validateOnServer(vs, pIn);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage()).setTxLink(txLog.getLastId());

View File

@ -42,6 +42,7 @@ import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureMap;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.INarrativeGenerator;
@ -371,7 +372,7 @@ public interface IWorkerContext {
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display);
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display);
/**
* Validation of a code - consult the terminology service
@ -387,10 +388,10 @@ public interface IWorkerContext {
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ValueSet vs);
public ValidationResult validateCode(String code, ValueSet vs);
public ValidationResult validateCode(Coding code, ValueSet vs);
public ValidationResult validateCode(CodeableConcept code, ValueSet vs);
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ValueSet vs);
public ValidationResult validateCode(TerminologyServiceOptions options, String code, ValueSet vs);
public ValidationResult validateCode(TerminologyServiceOptions options, Coding code, ValueSet vs);
public ValidationResult validateCode(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs);
/**
* Validation of a code - consult the terminology service
@ -406,7 +407,7 @@ public interface IWorkerContext {
* @param display
* @return
*/
public ValidationResult validateCode(String system, String code, String display, ConceptSetComponent vsi);
public ValidationResult validateCode(TerminologyServiceOptions options, String system, String code, String display, ConceptSetComponent vsi);
/**
* returns the recommended tla for the type

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.TerminologyServiceErrorClass;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
@ -111,7 +112,7 @@ public class TerminologyCache {
load();
}
public CacheToken generateValidationToken(Coding code, ValueSet vs) {
public CacheToken generateValidationToken(TerminologyServiceOptions options, Coding code, ValueSet vs) {
CacheToken ct = new CacheToken();
if (code.hasSystem())
ct.name = getNameForSystem(code.getSystem());
@ -121,7 +122,7 @@ public class TerminologyCache {
json.setOutputStyle(OutputStyle.PRETTY);
ValueSet vsc = getVSEssense(vs);
try {
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsc == null ? "null" : json.composeString(vsc))+"}";
ct.request = "{\"code\" : "+json.composeString(code, "code")+", \"valueSet\" :"+(vsc == null ? "null" : json.composeString(vsc))+(options == null ? "" : ", "+options.toJson())+"}";
} catch (IOException e) {
throw new Error(e);
}
@ -129,7 +130,7 @@ public class TerminologyCache {
return ct;
}
public CacheToken generateValidationToken(CodeableConcept code, ValueSet vs) {
public CacheToken generateValidationToken(TerminologyServiceOptions options, CodeableConcept code, ValueSet vs) {
CacheToken ct = new CacheToken();
for (Coding c : code.getCoding()) {
if (c.hasSystem())
@ -139,7 +140,7 @@ public class TerminologyCache {
json.setOutputStyle(OutputStyle.PRETTY);
ValueSet vsc = getVSEssense(vs);
try {
ct.request = "{\"code\" : "+json.composeString(code, "codeableConcept")+", \"valueSet\" :"+json.composeString(vsc)+"}";
ct.request = "{\"code\" : "+json.composeString(code, "codeableConcept")+", \"valueSet\" :"+json.composeString(vsc)+(options == null ? "" : ", "+options.toJson())+"}";
} catch (IOException e) {
throw new Error(e);
}

View File

@ -0,0 +1,36 @@
package org.hl7.fhir.r5.terminologies;
import org.fhir.ucum.Utilities;
import org.hl7.fhir.r5.model.Parameters;
public class TerminologyServiceOptions {
private String language;
public TerminologyServiceOptions() {
super();
}
public TerminologyServiceOptions(String language) {
super();
this.language = language;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String toJson() {
return "\"lang\":\""+language+"\"";
}
public void updateParameters(Parameters pIn) {
if (!Utilities.noString(language))
pIn.addParameter("displayLanguage", language);
}
}

View File

@ -51,10 +51,12 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
private ValueSet valueset;
private IWorkerContext context;
private Map<String, ValueSetCheckerSimple> inner = new HashMap<>();
private TerminologyServiceOptions options;
public ValueSetCheckerSimple(ValueSet source, IWorkerContext context) {
public ValueSetCheckerSimple(TerminologyServiceOptions options, ValueSet source, IWorkerContext context) {
this.valueset = source;
this.context = context;
this.options = options;
}
public ValidationResult validateCode(CodeableConcept code) throws FHIRException {
@ -425,7 +427,7 @@ public class ValueSetCheckerSimple implements ValueSetChecker {
return inner.get(url);
}
ValueSet vs = context.fetchResource(ValueSet.class, url);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(vs, context);
ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, context);
inner.put(url, vsc);
return vsc;
}

View File

@ -47,6 +47,7 @@ import org.hl7.fhir.r5.model.TemporalPrecisionEnum;
import org.hl7.fhir.r5.model.TimeType;
import org.hl7.fhir.r5.model.TypeDetails;
import org.hl7.fhir.r5.model.TypeDetails.ProfiledType;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
@ -175,6 +176,7 @@ public class FHIRPathEngine {
private Set<String> primitiveTypes = new HashSet<String>();
private Map<String, StructureDefinition> allTypes = new HashMap<String, StructureDefinition>();
private boolean legacyMode; // some R2 and R3 constraints assume that != is valid for emptty sets, so when running for R2/R3, this is set ot true
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
// if the fhir path expressions are allowed to use constants beyond those defined in the specification
// the application can implement them by providing a constant resolver
@ -1893,13 +1895,13 @@ public class FHIRPathEngine {
if (vs != null) {
for (Base l : left) {
if (l.fhirType().equals("code")) {
if (worker.validateCode(l.castToCoding(l), vs).isOk())
if (worker.validateCode(terminologyServiceOptions , l.castToCoding(l), vs).isOk())
ans = true;
} else if (l.fhirType().equals("Coding")) {
if (worker.validateCode(l.castToCoding(l), vs).isOk())
if (worker.validateCode(terminologyServiceOptions, l.castToCoding(l), vs).isOk())
ans = true;
} else if (l.fhirType().equals("CodeableConcept")) {
if (worker.validateCode(l.castToCodeableConcept(l), vs).isOk())
if (worker.validateCode(terminologyServiceOptions, l.castToCodeableConcept(l), vs).isOk())
ans = true;
}
}
@ -4307,4 +4309,9 @@ public class FHIRPathEngine {
return b ? Equality.True : Equality.False;
}
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
}

View File

@ -133,6 +133,9 @@ public interface IResourceValidator {
public boolean isErrorForUnknownProfiles();
public void setErrorForUnknownProfiles(boolean errorForUnknownProfiles);
public String getValidationLanguage();
public void setValidationLanguage(String value);
/**
* Validate suite
*

View File

@ -175,6 +175,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.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument;
@ -999,6 +1000,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
private List<ConceptMapRenderInstructions> renderingMaps = new ArrayList<ConceptMapRenderInstructions>();
private boolean pretty;
private boolean canonicalUrlsAsLinks;
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public NarrativeGenerator(String prefix, String basePath, IWorkerContext context) {
super();
@ -1836,7 +1838,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
private String lookupCode(String system, String code) {
ValidationResult t = context.validateCode(system, code, null);
ValidationResult t = context.validateCode(terminologyServiceOptions , system, code, null);
if (t != null && t.getDisplay() != null)
return t.getDisplay();
@ -2568,7 +2570,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
private String getDisplayForConcept(String system, String value) {
if (value == null || system == null)
return null;
ValidationResult cl = context.validateCode(system, value, null);
ValidationResult cl = context.validateCode(terminologyServiceOptions, system, value, null);
return cl == null ? null : cl.getDisplay();
}
@ -3765,7 +3767,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
li.ah(href).addText(f.getValue());
} else if ("concept".equals(f.getProperty()) && inc.hasSystem()) {
li.addText(f.getValue());
ValidationResult vr = context.validateCode(inc.getSystem(), f.getValue(), null);
ValidationResult vr = context.validateCode(terminologyServiceOptions, inc.getSystem(), f.getValue(), null);
if (vr.isOk()) {
li.tx(" ("+vr.getDisplay()+")");
}
@ -3847,7 +3849,7 @@ public class NarrativeGenerator implements INarrativeGenerator {
}
}
return context.validateCode(inc.getSystem(), code, null).asConceptDefinition();
return context.validateCode(terminologyServiceOptions, inc.getSystem(), code, null).asConceptDefinition();
}
@ -4752,5 +4754,13 @@ public class NarrativeGenerator implements INarrativeGenerator {
return this;
}
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public void setTerminologyServiceOptions(TerminologyServiceOptions terminologyServiceOptions) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
}

View File

@ -102,6 +102,7 @@ import org.hl7.fhir.r5.model.TypeDetails.ProfiledType;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.terminologies.ValueSetExpander.ValueSetExpansionOutcome;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine.IEvaluationContext;
@ -222,6 +223,7 @@ public class StructureMapUtilities {
private ITransformerServices services;
private ProfileKnowledgeProvider pkp;
private Map<String, Integer> ids = new HashMap<String, Integer>();
private TerminologyServiceOptions terminologyServiceOptions = new TerminologyServiceOptions();
public StructureMapUtilities(IWorkerContext worker, ITransformerServices services, ProfileKnowledgeProvider pkp) {
super();
@ -1965,7 +1967,7 @@ public class StructureMapUtilities {
throw new FHIRException("The code '"+code+"' is not in the value set '"+uri+"' (valid codes: "+b.toString()+"; also checked displays)");
} else
system = uri;
ValidationResult vr = worker.validateCode(system, code, null);
ValidationResult vr = worker.validateCode(terminologyServiceOptions, system, code, null);
if (vr != null && vr.getDisplay() != null)
display = vr.getDisplay();
return new Coding().setSystem(system).setCode(code).setDisplay(display);
@ -2959,4 +2961,12 @@ public class StructureMapUtilities {
return null;
}
public TerminologyServiceOptions getTerminologyServiceOptions() {
return terminologyServiceOptions;
}
public void setTerminologyServiceOptions(TerminologyServiceOptions terminologyServiceOptions) {
this.terminologyServiceOptions = terminologyServiceOptions;
}
}

View File

@ -33,6 +33,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.UUID;
import org.apache.commons.lang3.NotImplementedException;
@ -121,6 +122,7 @@ import org.hl7.fhir.r5.model.TypeDetails;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.terminologies.TerminologyServiceOptions;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.r5.utils.FHIRLexer.FHIRLexerException;
import org.hl7.fhir.r5.utils.FHIRPathEngine;
@ -264,6 +266,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private boolean noTerminologyChecks;
private boolean hintAboutNonMustSupport;
private BestPracticeWarningLevel bpWarnings;
private String validationLanguage;
private List<String> extensionDomains = new ArrayList<String>();
@ -821,13 +824,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
// public API
private boolean checkCode(List<ValidationMessage> errors, Element element, String path, String code, String system, String display, boolean checkDisplay) throws TerminologyServiceException {
private boolean checkCode(List<ValidationMessage> errors, Element element, String path, String code, String system, String display, boolean checkDisplay, NodeStack stack) throws TerminologyServiceException {
long t = System.nanoTime();
boolean ss = context.supportsSystem(system);
txTime = txTime + (System.nanoTime() - t);
if (ss) {
t = System.nanoTime();
ValidationResult s = context.validateCode(system, code, checkDisplay ? display : null);
ValidationResult s = context.validateCode(new TerminologyServiceOptions(stack.workingLang), system, code, checkDisplay ? display : null);
txTime = txTime + (System.nanoTime() - t);
if (s == null)
return true;
@ -935,7 +938,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
private boolean checkCodeableConcept(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext) {
private boolean checkCodeableConcept(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack) {
boolean res = true;
if (!noTerminologyChecks && theElementCntext != null && theElementCntext.hasBinding()) {
ElementDefinitionBindingComponent binding = theElementCntext.getBinding();
@ -968,7 +971,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) {
// ignore this since we can't validate but it doesn't matter..
} else {
ValidationResult vr = context.validateCode(cc, valueset);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), cc, valueset);
res = false;
if (!vr.isOk()) {
bindingsOk = false;
@ -977,7 +980,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code from this value set is required (class = "+vr.getErrorClass().toString()+")");
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc);
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack);
else if (!noExtensibleWarnings)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " and a code should come from this value set unless it has no suitable code (class = "+vr.getErrorClass().toString()+")");
} else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -987,7 +990,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+", and a code from this value set is required) (codes = "+ccSummary(cc)+")");
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc);
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), cc, stack);
else if (!noExtensibleWarnings)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "None of the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code) (codes = "+ccSummary(cc)+")");
} else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -1003,7 +1006,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String nextCode = nextCoding.getCode();
String nextSystem = nextCoding.getSystem();
if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem)) {
ValidationResult vr = context.validateCode(nextSystem, nextCode, null);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), nextSystem, nextCode, null);
if (!vr.isOk()) {
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Code {0} is not a valid code in code system {1}", nextCode, nextSystem);
}
@ -1027,13 +1030,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return res;
}
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc) {
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, CodeableConcept cc, NodeStack stack) {
// TODO Auto-generated method stub
ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl());
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(cc, valueset);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), cc, valueset);
txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1047,13 +1050,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c) {
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, Coding c, NodeStack stack) {
// TODO Auto-generated method stub
ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl());
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(c, valueset);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), c, valueset);
txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1067,13 +1070,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value) {
private void checkMaxValueSet(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, String maxVSUrl, String value, NodeStack stack) {
// TODO Auto-generated method stub
ValueSet valueset = resolveBindingReference(profile, maxVSUrl, profile.getUrl());
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, "ValueSet " + describeReference(maxVSUrl) + " not found")) {
try {
long t = System.nanoTime();
ValidationResult vr = context.validateCode(value, valueset);
ValidationResult vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), value, valueset);
txTime = txTime + (System.nanoTime() - t);
if (!vr.isOk()) {
if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure())
@ -1101,7 +1104,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkFixedValue(errors, path + ".userSelected", focus.getNamedChild("userSelected"), fixed.getUserSelectedElement(), "userSelected", focus);
}
private void checkCoding(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay) {
private void checkCoding(List<ValidationMessage> errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack) {
String code = element.getNamedChildValue("code");
String system = element.getNamedChildValue("system");
String display = element.getNamedChildValue("display");
@ -1110,7 +1113,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (system != null && code != null && !noTerminologyChecks) {
rule(errors, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(system), "The Coding references a value set, not a code system (\""+system+"\")");
try {
if (checkCode(errors, element, path, code, system, display, checkDisplay))
if (checkCode(errors, element, path, code, system, display, checkDisplay, stack))
if (theElementCntext != null && theElementCntext.hasBinding()) {
ElementDefinitionBindingComponent binding = theElementCntext.getBinding();
if (warning(errors, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, "Binding for " + path + " missing")) {
@ -1122,7 +1125,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime();
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(c, valueset);
vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), c, valueset);
}
txTime = txTime + (System.nanoTime() - t);
if (vr != null && !vr.isOk()) {
@ -1133,7 +1136,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl()+", and a code from this value set is required)");
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c);
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c, stack);
else if (!noExtensibleWarnings)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "Could not confirm that the codes provided are in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code)");
} else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -1142,7 +1145,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code is required from this value set)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : ""));
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c);
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), c, stack);
else
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The Coding provided is not in the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getUrl() + ", and a code should come from this value set unless it has no suitable code)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : ""));
} else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -1495,7 +1498,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
checkFixedValue(errors, path + ".end", focus.getNamedChild("end"), fixed.getEndElement(), "end", focus);
}
private void checkPrimitive(Object appContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile) throws FHIRException, IOException {
private void checkPrimitive(Object appContext, List<ValidationMessage> errors, String path, String type, ElementDefinition context, Element e, StructureDefinition profile, NodeStack node) throws FHIRException, IOException {
if (isBlank(e.primitiveValue())) {
if (e.primitiveValue() == null)
rule(errors, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), "Primitive types must have a value or must have child extensions");
@ -1663,7 +1666,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
if (context.hasBinding() && e.primitiveValue() != null) {
checkPrimitiveBinding(errors, path, type, context, e, profile);
checkPrimitiveBinding(errors, path, type, context, e, profile, node);
}
if (type.equals("xhtml")) {
@ -1730,7 +1733,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
private void checkPrimitiveBinding(List<ValidationMessage> errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile) {
private void checkPrimitiveBinding(List<ValidationMessage> errors, String path, String type, ElementDefinition elementContext, Element element, StructureDefinition profile, NodeStack stack) {
// We ignore bindings that aren't on string, uri or code
if (!element.hasPrimitiveValue() || !("code".equals(type) || "string".equals(type) || "uri".equals(type) || "url".equals(type) || "canonical".equals(type))) {
return;
@ -1749,7 +1752,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
long t = System.nanoTime();
ValidationResult vr = null;
if (binding.getStrength() != BindingStrength.EXAMPLE) {
vr = context.validateCode(value, vs);
vr = context.validateCode(new TerminologyServiceOptions(stack.workingLang), value, vs);
}
txTime = txTime + (System.nanoTime() - t);
if (vr != null && !vr.isOk()) {
@ -1759,7 +1762,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
txRule(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('"+value+"') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code is required from this value set)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : ""));
else if (binding.getStrength() == BindingStrength.EXTENSIBLE) {
if (binding.hasExtension("http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"))
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), value);
checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, "http://hl7.org/fhir/StructureDefinition/elementdefinition-maxValueSet"), value, stack);
else if (!noExtensibleWarnings)
txWarning(errors, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, "The value provided ('"+value+"') is not in the value set " + describeReference(binding.getValueSet()) + " (" + vs.getUrl() + ", and a code should come from this value set unless it has no suitable code)"+(vr.getMessage() != null ? " (error message = "+vr.getMessage()+")" : ""));
} else if (binding.getStrength() == BindingStrength.PREFERRED)
@ -2862,6 +2865,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// the instance at root is valid against the schema and schematron
// the instance validator had no issues against the base resource profile
private void start(ValidatorHostContext hostContext, List<ValidationMessage> errors, Element resource, Element element, StructureDefinition defn, NodeStack stack) throws FHIRException, FHIRException, IOException {
checkLang(resource, stack);
// profile is valid, and matches the resource name
ResourceProfiles resourceProfiles = getResourceProfiles(element, stack);
if (!resourceProfiles.isProcessed())
@ -2896,6 +2901,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
private void checkLang(Element resource, NodeStack stack) {
String lang = resource.getNamedChildValue("language");
if (!Utilities.noString(lang))
stack.workingLang = lang;
}
private void validateResourceRules(List<ValidationMessage> errors, Element element, NodeStack stack) {
String lang = element.getNamedChildValue("language");
Element text = element.getNamedChild("text");
@ -3256,7 +3267,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
}
long t = System.nanoTime();
ValidationResult res = context.validateCode(c, vs);
ValidationResult res = context.validateCode(new TerminologyServiceOptions(stack.workingLang), c, vs);
txTime = txTime + (System.nanoTime() - t);
if (!res.isOk()) {
txRule(errors, res.getTxLink(), IssueType.CODEINVALID, value.line(), value.col(), stack.getLiteralPath(), false, "The value provided (" + c.getSystem() + "::" + c.getCode() + ") is not in the options value set in the questionnaire");
@ -3968,7 +3979,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
ei.element.markValidation(profile, ei.definition);
if (type != null) {
if (isPrimitiveType(type)) {
checkPrimitive(hostContext, errors, ei.path, type, ei.definition, ei.element, profile);
checkPrimitive(hostContext, errors, ei.path, type, ei.definition, ei.element, profile, stack);
} else {
if (ei.definition.hasFixed()) {
checkFixedValue(errors,ei.path, ei.element, ei.definition.getFixed(), ei.definition.getSliceName(), null);
@ -3980,9 +3991,9 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
if (type.equals("Identifier")) {
checkIdentifier(errors, ei.path, ei.element, ei.definition);
} else if (type.equals("Coding")) {
checkCoding(errors, ei.path, ei.element, profile, ei.definition, inCodeableConcept, checkDisplayInContext);
checkCoding(errors, ei.path, ei.element, profile, ei.definition, inCodeableConcept, checkDisplayInContext, stack);
} else if (type.equals("CodeableConcept")) {
checkDisplay = checkCodeableConcept(errors, ei.path, ei.element, profile, ei.definition);
checkDisplay = checkCodeableConcept(errors, ei.path, ei.element, profile, ei.definition, stack);
thisIsCodeableConcept = true;
} else if (type.equals("Reference")) {
checkReference(hostContext, errors, ei.path, ei.element, profile, ei.definition, actualType, localStack);
@ -4423,13 +4434,16 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
private List<String> logicalPaths; // dotted format, various entry points
private NodeStack parent;
private ElementDefinition type;
private String workingLang;
public NodeStack() {
workingLang = validationLanguage;
}
public NodeStack(Element element) {
this.element = element;
literalPath = element.getName();
workingLang = validationLanguage;
}
public String addToLiteralPath(String... path) {
@ -4471,6 +4485,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
private NodeStack push(Element element, int count, ElementDefinition definition, ElementDefinition type) {
NodeStack res = new NodeStack();
res.parent = this;
res.workingLang = this.workingLang;
res.element = element;
res.definition = definition;
res.literalPath = getLiteralPath() + "." + element.getName();
@ -4632,5 +4647,13 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
return externalHostServices;
}
public String getValidationLanguage() {
return validationLanguage;
}
public void setValidationLanguage(String validationLanguage) {
this.validationLanguage = validationLanguage;
}
}

View File

@ -244,6 +244,7 @@ public class ValidationEngine {
private boolean hintAboutNonMustSupport;
private boolean anyExtensionsAllowed = false;
private String version;
private String language;
private PackageCacheManager pcm;
private PrintWriter mapLog;
@ -323,6 +324,14 @@ public class ValidationEngine {
pcm = new PackageCacheManager(true, ToolsVersion.TOOLS_VERSION);
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
private void loadDefinitions(String src) throws Exception {
Map<String, byte[]> source = loadIgSource(src);
if (version == null)
@ -1062,6 +1071,7 @@ public class ValidationEngine {
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
validator.setNoInvariantChecks(isNoInvariantChecks());
validator.setValidationLanguage(language);
return validator;
}

View File

@ -165,6 +165,10 @@ public class Validator {
System.out.println(" * JSON: json.schema");
System.out.println(" * RDF: SHEX");
System.out.println(" Default: false");
System.out.println("-language: [lang]");
System.out.println(" The language to use when validating coding displays - same value as for xml:lang");
System.out.println(" Not used if the resource specifies language");
System.out.println(" Default: no specified language");
System.out.println("-strictExtensions: If present, treat extensions not defined within the specified FHIR version and any");
System.out.println(" referenced implementation guides or profiles as errors. (Default is to only raise information messages.)");
System.out.println("-hintAboutNonMustSupport: If present, raise hints if the instance contains data elements that are not");
@ -269,6 +273,7 @@ public class Validator {
String sv = null;
String txLog = null;
String mapLog = null;
String lang = null;
// load the parameters - so order doesn't matter
for (int i = 0; i < args.length; i++) {
@ -337,6 +342,11 @@ public class Validator {
throw new Error("Specified -log without indicating file");
else
mapLog = args[++i];
else if (args[i].equals("-language"))
if (i+1 == args.length)
throw new Error("Specified -language without indicating language");
else
lang = args[++i];
else if (args[i].equals("-ig"))
if (i+1 == args.length)
throw new Error("Specified -ig without indicating ig file");
@ -378,6 +388,7 @@ public class Validator {
validator.setNative(doNative);
validator.setHintAboutNonMustSupport(hintAboutNonMustSupport);
validator.setAnyExtensionsAllowed(anyExtensionsAllowed);
validator.setLanguage(lang);
IParser x;
if (output != null && output.endsWith(".json"))

View File

@ -94,8 +94,8 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
this.content = content;
}
private static final String DEF_TX = "http://tx.fhir.org";
private static final String DBG_TX = "http://local.fhir.org:960";
// private static final String DEF_TX = "http://tx.fhir.org";
private static final String DEF_TX = "http://local.fhir.org:960";
private static ValidationEngine ve;
@SuppressWarnings("deprecation")
@ -133,6 +133,10 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour
if (content.has("allowed-extension-domains"))
for (JsonElement a : content.getAsJsonArray("allowed-extension-domains"))
val.getExtensionDomains().add(a.getAsString());
if (content.has("language"))
val.setValidationLanguage(content.get("language").getAsString());
else
val.setValidationLanguage(null);
val.setFetcher(this);
if (content.has("questionnaire")) {
ve.getContext().cacheResource(loadResource(TestUtilities.resourceNameToFile("validation-examples", content.get("questionnaire").getAsString()), v));

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<Observation xmlns="http://hl7.org/fhir">
<status value="final"/>
<code>
<coding>
<system value="http://loinc.org" />
<code value="3151-8"/>
<display value="ingeademde O2" />
</coding>
</code>
<subject>
<reference value="Patient/example" />
<display value="Example Patient" />
</subject>
<effectiveDateTime value="2016-12-19T09:00:00Z" />
<valueQuantity>
<value value="8"/>
</valueQuantity>
</Observation>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<Observation xmlns="http://hl7.org/fhir">
<status value="final"/>
<code>
<coding>
<system value="http://loinc.org" />
<code value="3151-8"/>
<display value="ingeademde O2" />
</coding>
</code>
<subject>
<reference value="Patient/example" />
<display value="Example Patient" />
</subject>
<effectiveDateTime value="2016-12-19T09:00:00Z" />
<valueQuantity>
<value value="8"/>
</valueQuantity>
</Observation>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<Observation xmlns="http://hl7.org/fhir">
<language value="nl-NL"/>
<status value="final"/>
<code>
<coding>
<system value="http://loinc.org" />
<code value="3151-8"/>
<display value="ingeademde O2" />
</coding>
</code>
<subject>
<reference value="Patient/example" />
<display value="Example Patient" />
</subject>
<effectiveDateTime value="2016-12-19T09:00:00Z" />
<valueQuantity>
<value value="8"/>
</valueQuantity>
</Observation>

View File

@ -176,7 +176,7 @@
}
},
"medication-atc.json": {
"errorCount": 1,
"errorCount": 0,
"allowed-extension-domain": "https://api-v8-r4.hspconsortium.org/DrugFormulary0/open"
},
"bp.json": {
@ -768,6 +768,19 @@
},
"bad-bundle-reference-type.xml": {
"errorCount": 1
},
"loinc-lang-nl-1.xml" : {
"errorCount": 0,
"warningCount": 1
},
"loinc-lang-nl-2.xml" : {
"language" : "nl-NL",
"errorCount": 0,
"warningCount": 0
},
"loinc-lang-nl-3.xml" : {
"errorCount": 0,
"warningCount": 0
}
}
}