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.
@@ -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
@@ -14,10 +14,7 @@ module Inferno
14
14
  class IG < Entity
15
15
  ATTRIBUTES = [
16
16
  :id,
17
- :profiles,
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
- case resource.resourceType
117
- when 'StructureDefinition'
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
- examples.push(resource) if relative_path.start_with? 'package/example'
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)