inferno_core 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/inferno/apps/cli/evaluate.rb +22 -12
- data/lib/inferno/config/boot/presets.rb +1 -1
- data/lib/inferno/dsl/fhir_client.rb +66 -0
- data/lib/inferno/dsl/fhir_evaluation/evaluation_context.rb +4 -2
- data/lib/inferno/dsl/fhir_evaluation/evaluator.rb +8 -3
- data/lib/inferno/dsl/fhir_evaluation/profile_conformance_helper.rb +66 -0
- data/lib/inferno/dsl/fhir_evaluation/reference_extractor.rb +61 -0
- data/lib/inferno/dsl/fhir_evaluation/rules/all_must_supports_present.rb +379 -0
- data/lib/inferno/dsl/fhir_evaluation/rules/all_references_resolve.rb +53 -0
- data/lib/inferno/dsl/fhir_evaluation/rules/all_resources_reachable.rb +63 -0
- data/lib/inferno/dsl/fhir_resource_navigation.rb +226 -0
- data/lib/inferno/dsl/must_support_metadata_extractor.rb +366 -0
- data/lib/inferno/dsl/primitive_type.rb +9 -0
- data/lib/inferno/dsl/value_extractor.rb +136 -0
- data/lib/inferno/entities/ig.rb +46 -24
- data/lib/inferno/public/bundle.js +16 -16
- data/lib/inferno/version.rb +1 -1
- data/spec/shared/test_kit_examples.rb +23 -1
- metadata +11 -2
@@ -0,0 +1,136 @@
|
|
1
|
+
module Inferno
|
2
|
+
module DSL
|
3
|
+
class ValueExtractor
|
4
|
+
attr_accessor :ig_resources, :resource, :profile_elements
|
5
|
+
|
6
|
+
def initialize(ig_resources, resource, profile_elements)
|
7
|
+
self.ig_resources = ig_resources
|
8
|
+
self.resource = resource
|
9
|
+
self.profile_elements = profile_elements
|
10
|
+
end
|
11
|
+
|
12
|
+
def values_from_fixed_codes(profile_element, type)
|
13
|
+
return [] unless type == 'CodeableConcept'
|
14
|
+
|
15
|
+
elements = profile_elements.select do |element|
|
16
|
+
element.path == "#{profile_element.path}.coding.code" && element.fixedCode.present?
|
17
|
+
end
|
18
|
+
|
19
|
+
elements.map(&:fixedCode)
|
20
|
+
end
|
21
|
+
|
22
|
+
def values_from_pattern_coding(profile_element, type)
|
23
|
+
return [] unless type == 'CodeableConcept'
|
24
|
+
|
25
|
+
elements = profile_elements.select do |element|
|
26
|
+
element.path == "#{profile_element.path}.coding" && element.patternCoding.present?
|
27
|
+
end
|
28
|
+
|
29
|
+
elements.map { |element| element.patternCoding.code }
|
30
|
+
end
|
31
|
+
|
32
|
+
def values_from_pattern_codeable_concept(profile_element, type)
|
33
|
+
return [] unless type == 'CodeableConcept'
|
34
|
+
|
35
|
+
elements = profile_elements.select do |element|
|
36
|
+
element.path == profile_element.path && element.patternCodeableConcept.present? && element.min.positive?
|
37
|
+
end
|
38
|
+
|
39
|
+
elements.map { |element| element.patternCodeableConcept.coding.first.code }
|
40
|
+
end
|
41
|
+
|
42
|
+
def value_set_binding(the_element)
|
43
|
+
the_element&.binding
|
44
|
+
end
|
45
|
+
|
46
|
+
def value_set(the_element)
|
47
|
+
ig_resources.value_set_by_url(value_set_binding(the_element)&.valueSet)
|
48
|
+
end
|
49
|
+
|
50
|
+
def bound_systems(the_element)
|
51
|
+
bound_systems_from_valueset(value_set(the_element))
|
52
|
+
end
|
53
|
+
|
54
|
+
def bound_systems_from_valueset(value_set)
|
55
|
+
value_set&.compose&.include&.map do |include_element|
|
56
|
+
bound_systems_from_valueset_include_element(include_element)
|
57
|
+
end&.flatten&.compact
|
58
|
+
end
|
59
|
+
|
60
|
+
def bound_systems_from_valueset_include_element(include_element)
|
61
|
+
if include_element.concept.present?
|
62
|
+
include_element
|
63
|
+
elsif include_element.system.present? && include_element.filter&.empty?
|
64
|
+
# Cannot process intensional value set with filters
|
65
|
+
ig_resources.code_system_by_url(include_element.system)
|
66
|
+
elsif include_element.valueSet.present?
|
67
|
+
include_element.valueSet.map do |vs|
|
68
|
+
a_value_set = ig_resources.value_set_by_url(vs)
|
69
|
+
bound_systems_from_valueset(a_value_set)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def codes_from_value_set_binding(the_element)
|
75
|
+
codes_from_system_code_pair(codings_from_value_set_binding(the_element))
|
76
|
+
end
|
77
|
+
|
78
|
+
def codes_from_system_code_pair(codings)
|
79
|
+
codings.present? ? codings.map { |coding| coding[:code] }.compact.uniq : []
|
80
|
+
end
|
81
|
+
|
82
|
+
def codings_from_value_set_binding(the_element)
|
83
|
+
return [] if the_element.nil?
|
84
|
+
|
85
|
+
bound_systems = bound_systems(the_element)
|
86
|
+
|
87
|
+
return codings_from_bound_systems(bound_systems) if bound_systems.present?
|
88
|
+
|
89
|
+
expansion_contains = value_set_expansion_contains(the_element)
|
90
|
+
|
91
|
+
return [] if expansion_contains.blank?
|
92
|
+
|
93
|
+
expansion_contains.map { |contains| { system: contains.system, code: contains.code } }.compact.uniq
|
94
|
+
end
|
95
|
+
|
96
|
+
def codings_from_bound_systems(bound_systems)
|
97
|
+
return [] unless bound_systems.present?
|
98
|
+
|
99
|
+
bound_systems.flat_map do |bound_system|
|
100
|
+
case bound_system
|
101
|
+
when FHIR::ValueSet::Compose::Include
|
102
|
+
bound_system.concept.map { |concept| { system: bound_system.system, code: concept.code } }
|
103
|
+
when FHIR::CodeSystem
|
104
|
+
bound_system.concept.map { |concept| { system: bound_system.url, code: concept.code } }
|
105
|
+
else
|
106
|
+
[]
|
107
|
+
end
|
108
|
+
end.uniq
|
109
|
+
end
|
110
|
+
|
111
|
+
def value_set_expansion_contains(element)
|
112
|
+
value_set(element)&.expansion&.contains
|
113
|
+
end
|
114
|
+
|
115
|
+
def fhir_metadata(current_path)
|
116
|
+
FHIR.const_get(resource)::METADATA[current_path]
|
117
|
+
end
|
118
|
+
|
119
|
+
def values_from_resource_metadata(paths)
|
120
|
+
values = []
|
121
|
+
|
122
|
+
paths.each do |current_path|
|
123
|
+
current_metadata = fhir_metadata(current_path)
|
124
|
+
|
125
|
+
next unless current_metadata&.dig('valid_codes').present?
|
126
|
+
|
127
|
+
values += current_metadata['valid_codes'].flat_map do |system, codes|
|
128
|
+
codes.map { |code| { system:, code: } }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
values
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/inferno/entities/ig.rb
CHANGED
@@ -14,10 +14,7 @@ module Inferno
|
|
14
14
|
class IG < Entity
|
15
15
|
ATTRIBUTES = [
|
16
16
|
:id,
|
17
|
-
:
|
18
|
-
:extensions,
|
19
|
-
:value_sets,
|
20
|
-
:search_params,
|
17
|
+
:resources_by_type,
|
21
18
|
:examples
|
22
19
|
].freeze
|
23
20
|
|
@@ -25,12 +22,8 @@ module Inferno
|
|
25
22
|
|
26
23
|
def initialize(**params)
|
27
24
|
super(params, ATTRIBUTES)
|
28
|
-
|
29
|
-
@profiles = []
|
30
|
-
@extensions = []
|
31
|
-
@value_sets = []
|
25
|
+
@resources_by_type ||= Hash.new { |hash, key| hash[key] = [] }
|
32
26
|
@examples = []
|
33
|
-
@search_params = []
|
34
27
|
end
|
35
28
|
|
36
29
|
def self.from_file(ig_path)
|
@@ -71,6 +64,9 @@ module Inferno
|
|
71
64
|
next
|
72
65
|
end
|
73
66
|
end
|
67
|
+
|
68
|
+
ig.id = extract_package_id(ig.ig_resource)
|
69
|
+
|
74
70
|
ig
|
75
71
|
end
|
76
72
|
|
@@ -91,6 +87,9 @@ module Inferno
|
|
91
87
|
next
|
92
88
|
end
|
93
89
|
end
|
90
|
+
|
91
|
+
ig.id = extract_package_id(ig.ig_resource)
|
92
|
+
|
94
93
|
ig
|
95
94
|
end
|
96
95
|
|
@@ -113,28 +112,51 @@ module Inferno
|
|
113
112
|
end
|
114
113
|
|
115
114
|
def handle_resource(resource, relative_path)
|
116
|
-
|
117
|
-
|
118
|
-
if resource.type == 'Extension'
|
119
|
-
extensions.push resource
|
120
|
-
else
|
121
|
-
profiles.push resource
|
122
|
-
end
|
123
|
-
when 'ValueSet'
|
124
|
-
value_sets.push resource
|
125
|
-
when 'SearchParameter'
|
126
|
-
search_params.push resource
|
127
|
-
when 'ImplementationGuide'
|
128
|
-
@id = extract_package_id(resource)
|
115
|
+
if relative_path.start_with? 'package/example'
|
116
|
+
examples << resource
|
129
117
|
else
|
130
|
-
|
118
|
+
resources_by_type[resource.resourceType] << resource
|
131
119
|
end
|
132
120
|
end
|
133
121
|
|
134
|
-
def extract_package_id(ig_resource)
|
122
|
+
def self.extract_package_id(ig_resource)
|
135
123
|
"#{ig_resource.id}##{ig_resource.version || 'current'}"
|
136
124
|
end
|
137
125
|
|
126
|
+
def profiles
|
127
|
+
resources_by_type['StructureDefinition'].filter { |sd| sd.type != 'Extension' }
|
128
|
+
end
|
129
|
+
|
130
|
+
def extensions
|
131
|
+
resources_by_type['StructureDefinition'].filter { |sd| sd.type == 'Extension' }
|
132
|
+
end
|
133
|
+
|
134
|
+
def capability_statement(mode = 'server')
|
135
|
+
resources_by_type['CapabilityStatement'].find do |capability_statement_resource|
|
136
|
+
capability_statement_resource.rest.any? { |r| r.mode == mode }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def ig_resource
|
141
|
+
resources_by_type['ImplementationGuide'].first
|
142
|
+
end
|
143
|
+
|
144
|
+
def profile_by_url(url)
|
145
|
+
profiles.find { |profile| profile.url == url }
|
146
|
+
end
|
147
|
+
|
148
|
+
def resource_for_profile(url)
|
149
|
+
profiles.find { |profile| profile.url == url }.type
|
150
|
+
end
|
151
|
+
|
152
|
+
def value_set_by_url(url)
|
153
|
+
resources_by_type['ValueSet'].find { |profile| profile.url == url }
|
154
|
+
end
|
155
|
+
|
156
|
+
def code_system_by_url(url)
|
157
|
+
resources_by_type['CodeSystem'].find { |system| system.url == url }
|
158
|
+
end
|
159
|
+
|
138
160
|
# @private
|
139
161
|
def add_self_to_repository
|
140
162
|
repository.insert(self)
|