diff --git a/exec/pack/Messages.properties b/exec/pack/Messages.properties index bd9b46208..549d80511 100644 --- a/exec/pack/Messages.properties +++ b/exec/pack/Messages.properties @@ -481,7 +481,7 @@ UNKNOWN_CODE_IN_FRAGMENT = Unknown Code ''{0}'' in the system ''{1}'' version '' Code_found_in_expansion_however_ = Code found in expansion, however: {0} None_of_the_provided_codes_are_in_the_value_set_one = The provided code {2} was not found in the value set ''{1}'' None_of_the_provided_codes_are_in_the_value_set_other = None of the provided codes [{2}] are in the value set ''{1}'' -Coding_has_no_system__cannot_validate = Coding has no system - cannot validate +Coding_has_no_system__cannot_validate = Coding has no system. A code with no system has no defined meaning, and it cannot be validated. A system should be provided Unable_to_handle_system__concept_filter_with_op__ = Unable to handle system {0} concept filter with op = {1} UNABLE_TO_HANDLE_SYSTEM__PROPERTY_FILTER_WITH_OP__ = Unable to handle system {0} property filter with op = {1} Unable_to_handle_system__filter_with_property__ = Unable to handle system {0} filter with property = {1}, op = {2} @@ -926,8 +926,8 @@ SM_DEPENDENT_PARAM_TYPE_MISMATCH_DUPLICATE = The group {0} has already been used CONCEPTMAP_GROUP_SOURCE_INCOMPLETE = Source Code System {0} doesn''t have all content (content = {1}), so the source codes cannot be checked CONCEPTMAP_GROUP_TARGET_INCOMPLETE = Target Code System {0} doesn''t have all content (content = {1}), so the target codes cannot be checked SD_NO_TYPE_CODE_ON_CODE = Snapshot for {1} element {0} has type.code without a value -UNKNOWN_CODESYSTEM = The code system {0} could not be found -UNKNOWN_CODESYSTEM_VERSION = The code system {0} version {1} could not be found. Valid versions: {2} +UNKNOWN_CODESYSTEM = A definition for CodeSystem {0} could not be found, so the code cannot be validated +UNKNOWN_CODESYSTEM_VERSION = A definition for CodeSystem {0} version {1} could not be found, so the code cannot be validated. Valid versions: {2} UNABLE_TO_INFER_CODESYSTEM = The System URI could not be determined for the code {0} in the ValueSet {1} VALUESET_TOO_COSTLY = The value set {0} has too many codes to display ({1}) VALUESET_TOO_COSTLY_TIME = The value set {0} took too long to process (>{1}sec) diff --git a/library/fhir/fhir_common.pas b/library/fhir/fhir_common.pas index 06c998aed..456febfc2 100644 --- a/library/fhir/fhir_common.pas +++ b/library/fhir/fhir_common.pas @@ -61,13 +61,13 @@ interface TObservationStatus = (obssNull, obssRegistered, obssPreliminary, obssFinal, obssAmended, obssCorrected, obssCancelled, obssEnteredInError, obssUnknown); TTokenCategory = (tcClinical, tcData, tcMeds, tcSchedule, tcAudit, tcDocuments, tcFinancial, tcMedicationDefinition, tcOther); TIdentifierUse = (iuNull, iuUsual, iuOfficial, iuTemp, iuSecondary, iuOld); - TOpIssueCode = (oicVoid, oicNotInVS, oicThisNotInVS, oicInvalidCode, oicDisplay, oicNotFound, oicCodeRule, oicVSProcessing, oicInferFailed, oicStatusCheck); + TOpIssueCode = (oicVoid, oicNotInVS, oicThisNotInVS, oicInvalidCode, oicDisplay, oicNotFound, oicCodeRule, oicVSProcessing, oicInferFailed, oicStatusCheck, oicInvalidData); const CODES_TFhirFilterOperator: Array[TFilterOperator] of String = ('', '=', 'is-a', 'descendent-of', 'is-not-a', 'regex', 'in', 'not-in', 'generalizes', 'exists', 'child-of', 'descendent-leaf'); CODES_TPublicationStatus: Array[TPublicationStatus] of String = ('', 'draft', 'active', 'retired'); CODES_TTokenCategory : array [TTokenCategory] of String = ('Clinical', 'Data', 'Meds', 'Schedule', 'Audit', 'Documents', 'Financial', 'MedicationDefinitions', 'Other'); - CODES_TOpIssueCode : array [TOpIssueCode] of String = ('', 'not-in-vs', 'this-code-not-in-vs', 'invalid-code', 'invalid-display', 'not-found', 'code-rule', 'vs-invalid', 'cannot-infer', 'status-check'); + CODES_TOpIssueCode : array [TOpIssueCode] of String = ('', 'not-in-vs', 'this-code-not-in-vs', 'invalid-code', 'invalid-display', 'not-found', 'code-rule', 'vs-invalid', 'cannot-infer', 'status-check', 'invalid-data'); type diff --git a/library/fsl/tests/fsl_tests.pas b/library/fsl/tests/fsl_tests.pas index 1e5bc198f..fe5e99163 100644 --- a/library/fsl/tests/fsl_tests.pas +++ b/library/fsl/tests/fsl_tests.pas @@ -33,7 +33,8 @@ interface Uses - {$IFDEF WINDOWS} Windows, {$ENDIF} SysUtils, Classes, {$IFNDEF FPC}Soap.EncdDecd, System.NetEncoding, {$ENDIF} SyncObjs, zlib, + {$IFDEF WINDOWS} Windows, {$ENDIF} SysUtils, Classes, {$IFNDEF FPC}Soap.EncdDecd, System.NetEncoding, {$ENDIF} SyncObjs, + zlib, zstream, {$IFDEF FPC} FPCUnit, TestRegistry, RegExpr, {$ELSE} TestFramework, {$ENDIF} fsl_testing, IdGlobalProtocols, fsl_base, fsl_utilities, fsl_stream, fsl_threads, fsl_collections, fsl_fpc, fsl_versions, @@ -41,7 +42,7 @@ interface {$IFNDEF FPC} fsl_msxml, {$ENDIF} - fsl_json, fsl_turtle, fsl_comparisons; + fsl_json, fsl_turtle, fsl_comparisons, fsl_npm; Type TFslTestString = class (TFslObject) @@ -5264,39 +5265,44 @@ procedure TXmlUtilsTest.TestUnPretty; function TTarGZParserTests.load(filename : String) : TFslList; var + bs : TBytesStream; z : TZDecompressionStream; tar : TTarArchive; entry : TTarDirRec; - mem : TMemoryStream; + n : String; + b : TBytes; + bi : TBytesStream; item : TFslNameBuffer; - stream : TFileStream; -begin +begin result := TFslList.Create; try - stream := TFileStream.Create(filename, fmOpenRead); + bs := TBytesStream.create(readZLibHeader(TFileStream.create(filename, fmOpenRead))); try - z := TZDecompressionStream.Create(stream, false); // 15+16); + z := TZDecompressionStream.Create(bs, true); // 15+16); try tar := TTarArchive.Create(z); try + tar.Reset; while tar.FindNext(entry) do begin + n := String(entry.Name); + if (n.contains('..')) then + raise EFSLException.create('The package contains the file "'+n+'". Packages are not allowed to contain files with ".." in the name'); + bi := TBytesStream.Create; + try + tar.ReadFile(bi); + b := copy(bi.Bytes, 0, bi.size); + finally + bi.free; + end; item := TFslNameBuffer.Create; try - item.Name := String(entry.Name); - mem := TMemoryStream.Create; - try - tar.ReadFile(mem); - mem.position := 0; - item.loadFromStream(mem); - finally - mem.free; - end; + item.Name := n; + item.AsBytes := b; result.Add(item.link) finally item.free; end; - //break; end; finally tar.free; @@ -5305,8 +5311,8 @@ function TTarGZParserTests.load(filename : String) : TFslList; z.free; end; finally - stream.free; - end; + bs.free; + end; result.link; finally result.free; diff --git a/library/fsl/tests/fsl_tests_web.pas b/library/fsl/tests/fsl_tests_web.pas index 51775b188..096206a26 100644 --- a/library/fsl/tests/fsl_tests_web.pas +++ b/library/fsl/tests/fsl_tests_web.pas @@ -605,7 +605,6 @@ procedure TOpenSSLTests.testWebServer_110; begin assertTrue(TestSettings.SSLCertFile <> '', 'Must provide public key file for SSL test in '+TestSettings.filename+' ([ssl] cert=)'); assertTrue(TestSettings.SSLKeyFile <> '', 'Must provide private key file for SSL test in '+TestSettings.filename+' ([ssl] key=)'); - assertTrue(TestSettings.SSLPassword <> '', 'Must provide password for private key for SSL test in '+TestSettings.filename+' ([ssl] password=)'); assertTrue(TestSettings.SSLCAFile <> '', 'Must provide ca cert file for SSL test in '+TestSettings.filename+' ([ssl] cacert=)'); assertTrue(FileExists(TestSettings.SSLCertFile), 'SSL Certificate not found at '+TestSettings.SSLCertFile); diff --git a/library/ftx/fhir_valuesets.pas b/library/ftx/fhir_valuesets.pas index fd0600e75..321164ec4 100644 --- a/library/ftx/fhir_valuesets.pas +++ b/library/ftx/fhir_valuesets.pas @@ -258,7 +258,7 @@ TValueSetWorker = class (TFslObject) FValueSet : TFHIRValueSetW; FLangList : THTTPLanguageList; - function findInAdditionalResources(url, version, resourceType : String) : TFHIRMetadataResourceW; + function findInAdditionalResources(url, version, resourceType : String; error : boolean) : TFHIRMetadataResourceW; function findValueSet(url, version : String) : TFHIRValueSetW; function findCodeSystem(url, version : String; params : TFHIRExpansionParams; nullOk : boolean) : TCodeSystemProvider; function listVersions(url : String) : String; @@ -558,7 +558,7 @@ function isLaterVersion(test, base : String) : boolean; result := StringCompare(test, base) > 0; end; -function TValueSetWorker.findInAdditionalResources(url, version, resourceType : String) : TFHIRMetadataResourceW; +function TValueSetWorker.findInAdditionalResources(url, version, resourceType : String; error : boolean) : TFHIRMetadataResourceW; var r : TFHIRMetadataResourceW; matches : TFslMetadataResourceList; @@ -574,7 +574,10 @@ function TValueSetWorker.findInAdditionalResources(url, version, resourceType : if (url <> '') and ((r.url = url) or (r.vurl = url)) and ((version = '') or (version = r.version)) then begin if r.fhirType <> resourceType then - raise EFHIRException.Create('Attempt to reference '+url+' as a '+resourceType+' when it''s a '+r.fhirType); + if error then + raise EFHIRException.Create('Attempt to reference '+url+' as a '+resourceType+' when it''s a '+r.fhirType) + else + exit(nil); matches.add(r.link); end; end; @@ -607,7 +610,7 @@ function TValueSetWorker.findCodeSystem(url, version: String; params: TFHIRExpan result := nil; end; - cs := findInAdditionalResources(url, version, 'CodeSystem') as TFhirCodeSystemW; + cs := findInAdditionalResources(url, version, 'CodeSystem', not nullOk) as TFhirCodeSystemW; if (cs <> nil) and (cs.content = cscmComplete) then begin cse := TFHIRCodeSystemEntry.Create(cs.link); @@ -661,7 +664,7 @@ function TValueSetWorker.findValueSet(url, version: String): TFHIRValueSetW; if (url = '') then exit(nil); - r := findInAdditionalResources(url, '', 'ValueSet'); + r := findInAdditionalResources(url, '', 'ValueSet', false); if (r <> nil) then exit(r.link as TFHIRValueSetW); @@ -902,7 +905,7 @@ function TValueSetChecker.determineVersion(path, systemURI, versionVS, versionCo else begin message := 'The code system "'+systemUri+'" version "'+versionVS+'" in the ValueSet include is different to the one in the value ("'+versionCoding+'")'; - op.addIssue(isError, itNotFound, addToPath(path, 'version'), message, oicVSProcessing); + op.addIssue(isError, itInvalid, addToPath(path, 'version'), message, oicVSProcessing); exit(''); end; if result = '' then @@ -946,7 +949,7 @@ function TValueSetChecker.prepare(vs: TFHIRValueSetW; params : TFHIRExpansionPar cs := TFhirCodeSystemProvider.create(FLanguages.link, ffactory.link, TFHIRCodeSystemEntry.Create(FFactory.wrapCodeSystem(FValueSet.Resource.Link))); FOthers.Add(ics.systemUri, cs); if (FValueSet.version <> '') then - FOthers.Add(ics.systemUri+'|'+FValueSet.version, cs); + FOthers.Add(ics.systemUri+'|'+FValueSet.version, cs.link); finally ics.free; end; @@ -1108,7 +1111,7 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, cs : TCodeSystemProvider; ctxt : TCodeSystemProviderContext; cc : TFhirValueSetComposeIncludeW; - excluded, ok : boolean; + excluded, ok, bAdd : boolean; isabstract : boolean; checker : TValueSetChecker; s, v, msg : String; @@ -1116,7 +1119,16 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, ccl : TFhirCodeSystemConceptListW; ccc : TFhirValueSetExpansionContainsW; ts : TStringList; + vss : TFHIRValueSetW; begin + if (system = '') and not inferSystem then + begin + msg := FI18n.translate('Coding_has_no_system__cannot_validate', FParams.languages, []); + messages.add(msg); + op.addIssue(isError, itInvalid, path, msg, oicInvalidData); + exit(bFalse); + end; + ts := TStringList.create; try FLog := ''; @@ -1132,12 +1144,23 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, result := bUnknown; cause := itNotFound; FLog := 'Unknown code system'; - if (version <> '') then + vss := findValueSet(system, ''); + if (vss <> nil) then begin - msg := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [system, version]); + vss.free; + msg := FI18n.translate('Terminology_TX_System_ValueSet2', FParams.languages, [system]); messages.add(msg); - op.addIssue(isError, itNotFound, addToPath(path, 'system'), msg, oicNotFound); - unknownSystems.add(system+'|'+version); + op.addIssue(isError, itInvalid, addToPath(path, 'system'), msg, oicInvalidData); + end + else if (version <> '') then + begin + msg := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [system, version, '['+listVersions(system)+']']); + messages.add(msg); + if (unknownSystems.IndexOf(system+'|'+version) = -1) then + begin + op.addIssue(isError, itNotFound, addToPath(path, 'system'), msg, oicNotFound); + unknownSystems.add(system+'|'+version); + end; end else begin @@ -1196,7 +1219,7 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, result := bFalse; FLog := 'Inactive code when not allowed'; cause := itBusinessRule; - msg := FI18n.translate('INACTIVE_CODE_NOT_ALLOWED', FParams.languages, [system, code]); + msg := FI18n.translate('STATUS_CODE_WARNING_CODE', FParams.languages, ['not active', code]); messages.add(msg); op.addIssue(isError, itBusinessRule, addToPath(path, 'code'), msg, oicCodeRule); end @@ -1231,10 +1254,13 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, FLog := 'Unknown code system'; if (version <> '') then begin - msg := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [system, version]); + msg := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [system, version, '['+listVersions(system)+']']); messages.add(msg); - op.addIssue(isError, itNotFound, addToPath(path, 'system'), msg, oicNotFound); - unknownSystems.add(system+'|'+version); + if (unknownSystems.IndexOf(system+'|'+version) = -1) then + begin + op.addIssue(isError, itNotFound, addToPath(path, 'system'), msg, oicNotFound); + unknownSystems.add(system+'|'+version); + end; end else begin @@ -1281,7 +1307,7 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, result := bFalse; FLog := 'Abstract code when not allowed'; cause := itBusinessRule; - msg := FI18n.translate('ABSTRACT_CODE_NOT_ALLOWED', FParams.languages, [system, code]); + msg := FI18n.translate('STATUS_CODE_WARNING_CODE', FParams.languages, ['not active', code]); messages.add(msg); op.addIssue(isError, itBusinessRule, addToPath(path, 'code'), msg, oicCodeRule); end @@ -1290,7 +1316,7 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, result := bFalse; FLog := 'Inactive code when not allowed'; cause := itBusinessRule; - msg := FI18n.translate('INACTIVE_CODE_NOT_ALLOWED', FParams.languages, [system, code]); + msg := FI18n.translate('STATUS_CODE_WARNING_CODE', FParams.languages, ['not active', code]); messages.add(msg); op.addIssue(isError, itBusinessRule, addToPath(path, 'code'), msg, oicCodeRule); end @@ -1361,6 +1387,8 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, if result = bFalse then begin checker := TValueSetChecker(FOthers.matches[s]); + if (checker = nil) then + raise ETerminologyError.Create('No Match for '+s+' in '+FOthers.AsText, itUnknown); checkCanonicalStatus(path, op, checker.FValueSet, FValueSet); result := checker.check(path, system, version, code, abstractOk, inferSystem, displays, unknownSystems, message, ver, inactive, vstatus, cause, op, nil, params, contentMode, impliedSystem, unkCodes, messages); end; @@ -1382,6 +1410,7 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, begin if (not FParams.membershipOnly) then begin + bAdd := true; if (v = '') then begin message := FI18n.translate('UNKNOWN_CODESYSTEM', FParams.languages, [system]); @@ -1390,10 +1419,13 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, else begin message := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [system, v, '['+listVersions(system)+']']); - unknownSystems.add(system+'|'+v); + badd := unknownSystems.IndexOf(system+'|'+version) = -1; + if (bAdd) then + unknownSystems.add(system+'|'+v); end; messages.add(message); - op.addIssue(isError, itNotFound, addToPath(path, 'system'), message, oicNotFound); + if (bAdd) then + op.addIssue(isError, itNotFound, addToPath(path, 'system'), message, oicNotFound); exit(bUnknown); end else @@ -1419,7 +1451,7 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, begin checker := TValueSetChecker(FOthers.matches[s]); if checker = nil then - raise ETerminologyError.Create('No Match for '+s, itUnknown); + raise ETerminologyError.Create('No Match for '+s+' in '+FOthers.AsText, itUnknown); checkCanonicalStatus(path, op, checker.FValueSet, FValueSet); if (result = bTrue) then result := checker.check(path, system, version, code, abstractOk, inferSystem, displays, unknownSystems, message, ver, inactive, vstatus, cause, op, nil, params, contentMode, impliedSystem, unkCodes, messages); @@ -1437,7 +1469,13 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, if (cc.version = '') then cs := TCodeSystemProvider(FOthers.matches[cc.systemUri]) else + begin cs := TCodeSystemProvider(FOthers.matches[cc.systemUri+'|'+cc.version]); + if (cs = nil) then + cs := TCodeSystemProvider(FOthers.matches[cc.systemUri]) + end; + if (cs = nil) then + raise ETerminologyError.Create('No Match for '+cc.systemUri+'|'+cc.version+' in '+FOthers.AsText, itUnknown); checkCanonicalStatus(path, op, cs, FValueSet); checkSupplements(cs, cc); ver := cs.version(nil); @@ -1446,7 +1484,9 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, end; for s in cc.valueSets do begin - checker := TValueSetChecker(FOthers.matches[s]); + checker := TValueSetChecker(FOthers.matches[s]); + if (cs = nil) then + raise ETerminologyError.Create('No Match for '+cc.systemUri+'|'+cc.version+' in '+FOthers.AsText, itUnknown); checkCanonicalStatus(path, op, checker.FValueSet, FValueSet); excluded := excluded and (checker.check(path, system, version, code, abstractOk, inferSystem, displays, unknownSystems, message, ver, inactive, vstatus, cause, op, nil, params, contentMode, impliedSystem, unkCodes, messages) = bTrue); end; @@ -1485,6 +1525,7 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, begin if (not FParams.membershipOnly) then begin + bAdd := true; if (v = '') then begin message := FI18n.translate('UNKNOWN_CODESYSTEM', FParams.languages, [system]) ; @@ -1492,12 +1533,16 @@ function TValueSetChecker.check(path, system, version, code: String; abstractOk, end else begin - message := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [system, v, '['+listVersions(system)+']']); - unknownSystems.add(system+'|'+v); + badd := unknownSystems.IndexOf(system+'|'+version) = -1; + if (bAdd) then + begin + message := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [system, v, '['+listVersions(system)+']']); + unknownSystems.add(system+'|'+v); + end; end; messages.add(message); - - op.addIssue(isError, itNotFound, addToPath(path, 'system'), message, oicNotFound); + if bAdd then + op.addIssue(isError, itNotFound, addToPath(path, 'system'), message, oicNotFound); exit(bUnknown); end else @@ -1678,7 +1723,7 @@ function TValueSetChecker.check(issuePath : String; code: TFhirCodeableConceptW; ok, v : TTrueFalseUnknown; first : boolean; contentMode : TFhirCodeSystemContentMode; - cc, codelist, message, ver, pd, ws, impliedSystem, path, m, tsys, tcode, tver,vs: String; + cc, codelist, message, ver, pd, ws, impliedSystem, path, m, tsys, tcode, tver,vs, tdisp: String; prov, prov2 : TCodeSystemProvider; ctxt : TCodeSystemProviderContext; c : TFhirCodingW; @@ -1693,9 +1738,10 @@ function TValueSetChecker.check(issuePath : String; code: TFhirCodeableConceptW; vcc : TFHIRCodeableConceptW; severity : TIssueSeverity; diff : TDisplayDifference; - inactive : boolean; + inactive, bAdd : boolean; vstatus : String; mt, ts : TStringList; + vss : TFHIRValueSetW; procedure msg(s : String; clear : boolean = false); begin if (s = '') then @@ -1716,6 +1762,7 @@ function TValueSetChecker.check(issuePath : String; code: TFhirCodeableConceptW; tsys := ''; tcode := ''; tver := ''; + tdisp := ''; vcc := FFactory.wrapCodeableConcept(FFactory.makeCodeableConcept); vcc.text := code.text; unknownSystems := TStringList.create; @@ -1753,11 +1800,14 @@ function TValueSetChecker.check(issuePath : String; code: TFhirCodeableConceptW; tsys := c.systemUri; tcode := c.code; tver := c.version; + tdisp := c.display; end; if (c.version = '') then cc := ws+'#'+c.code else cc := ws+'|'+c.version+'#'+c.code; + if (c.display <> '') then + cc := cc + ' ('''+c.display+''')'; CommaAdd(codelist, ''''+cc+''''); if (v = bFalse) and not FAllValueSet and (mode = vcmCodeableConcept) then @@ -1816,39 +1866,65 @@ function TValueSetChecker.check(issuePath : String; code: TFhirCodeableConceptW; if (pdisp = '') then pdisp := list.preferredDisplay; end - else if (not FParams.membershipOnly) then + else if (not FParams.membershipOnly and (ws <> '')) then begin + if not isAbsoluteUrl(ws) then + begin + m := FI18n.translate('Terminology_TX_System_Relative', FParams.languages, []); + if mode = vcmCoding then + p := issuePath + '.system' + else if mode = vcmCodeableConcept then + p := issuePath + '.coding['+inttostr(i)+'].system' + else + p := issuePath; + op.addIssue(isError, itInvalid, p, m, oicInvalidData); + end; prov := findCodeSystem(ws, c.version, FParams, true); try if (prov = nil) then begin - prov2 := findCodeSystem(ws, '', FParams, true); - try - if (prov2 = nil) then - begin - m := FI18n.translate('UNKNOWN_CODESYSTEM', FParams.languages, [ws]); - //if (valueSetDependsOnCodeSystem(ws, '')) then - unknownSystems.add(ws); - end - else - begin - m := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [ws, c.version, '['+listVersions(c.systemUri)+']']); - //if (valueSetDependsOnCodeSystem(ws, c.version)) then - unknownSystems.add(ws+'|'+c.version); + vss := findValueSet(ws, ''); + if (vss <> nil) then + begin + vss.free; + m := FI18n.translate('Terminology_TX_System_ValueSet2', FParams.languages, [ws]); + msg(m); + op.addIssue(isError, itInvalid, addToPath(path, 'system'), m, oicInvalidData); + cause := itNotFound; + end + else + begin + prov2 := findCodeSystem(ws, '', FParams, true); + try + bAdd := true; + if (prov2 = nil) then + begin + m := FI18n.translate('UNKNOWN_CODESYSTEM', FParams.languages, [ws]); + //if (valueSetDependsOnCodeSystem(ws, '')) then + unknownSystems.add(ws); + end + else + begin + m := FI18n.translate('UNKNOWN_CODESYSTEM_VERSION', FParams.languages, [ws, c.version, '['+listVersions(c.systemUri)+']']); + badd := unknownSystems.IndexOf(ws+'|'+c.version) = -1; + if (bAdd) then + unknownSystems.add(ws+'|'+c.version); + end; + if (bAdd) then + op.addIssue(isError, itNotFound, addToPath(path, 'system'), m, oicNotFound); + if (valueSetDependsOnCodeSystem(ws, c.version)) then + begin + m := 'Unable to check whether the code is in the value set '+FValueSet.vurl+' because the code system '+ws+'|'+c.version+' was not found'; + msg(m); + op.addIssue(isWarning, itNotFound, issuepath, m, oicVSProcessing); + end + else + msg(m); + finally + prov2.free; end; - op.addIssue(isError, itNotFound, addToPath(path, 'system'), m, oicNotFound); - if (valueSetDependsOnCodeSystem(ws, c.version)) then - begin - m := 'Unable to check whether the code is in the value set '+FValueSet.vurl; - msg(m); - op.addIssue(isWarning, itNotFound, issuepath, m, oicVSProcessing); - end - else - msg(m); - finally - prov2.free; + cause := itNotFound; end; - cause := itNotFound; end else begin @@ -1879,6 +1955,11 @@ function TValueSetChecker.check(issuePath : String; code: TFhirCodeableConceptW; else begin listDisplays(list, prov, ctxt); + pd := list.preferredDisplay(FParams.languages); + if pd <> '' then + pdisp := pd; + if (pdisp = '') then + pdisp := list.preferredDisplay; severity := dispWarning(); if (c.display <> '') and (not list.hasDisplay(FParams.languages, c.display, dcsCaseInsensitive, diff)) then begin @@ -1965,6 +2046,9 @@ function TValueSetChecker.check(issuePath : String; code: TFhirCodeableConceptW; if pdisp <> '' then result.AddParamStr('display', pdisp); + //else if tdisp <> '' then + // result.AddParamStr('display', tdisp); + if inactive then begin result.addParamBool('inactive',inactive); @@ -2124,6 +2208,7 @@ function TValueSetChecker.checkConceptSet(path : String; cs: TCodeSystemProvider end else if FValueSet.excludeInactives and cs.IsInactive(loc) then begin + op.addIssue(isError, itBusinessRule, addToPath(path, 'code'), FI18n.translate('STATUS_CODE_WARNING_CODE', FParams.languages, ['not active', code]), oicCodeRule); result := false; if (not FParams.membershipOnly) then begin @@ -2132,11 +2217,20 @@ function TValueSetChecker.checkConceptSet(path : String; cs: TCodeSystemProvider vstatus := cs.getCodeStatus(loc); end; end + else if FParams.activeOnly and cs.IsInactive(loc) then + begin + result := false; + inactive := true; + vstatus := cs.getCodeStatus(loc); + op.addIssue(isError, itBusinessRule, addToPath(path, 'code'), FI18n.translate('STATUS_CODE_WARNING_CODE', FParams.languages, ['not active', code]), oicCodeRule); + end else begin result := true; listDisplays(displays, cs, loc); inactive := cs.IsInactive(loc); + + if (inactive) then vstatus := cs.getCodeStatus(loc); diff --git a/library/ftx/ftx_lang.pas b/library/ftx/ftx_lang.pas index 79ff8a3f2..75aa7fca9 100644 --- a/library/ftx/ftx_lang.pas +++ b/library/ftx/ftx_lang.pas @@ -180,7 +180,10 @@ procedure TIETFLanguageCodeServices.Designations(context: TCodeSystemProviderCon begin list.addDesignation(true, true, '', FLanguages.present(c.FInfo).Trim); if (c.FInfo.isLangRegion) then + begin list.addDesignation(false, true, '', FLanguages.present(c.FInfo, '{{lang}} ({{region}})').Trim); + list.addDesignation(false, true, '', FLanguages.present(c.FInfo, '{{lang}} (Region={{region}})').Trim); + end; end; end; end;