Module: Inferno::DSL::FHIRResourceNavigation
- Defined in:
- lib/inferno/dsl/fhir_resource_navigation.rb
Overview
The FHIRResourceNavigation module is used to pick values from a FHIR resource, based on a profile.
Originally intended for use for verifying the presence of Must Support elements on a resource.
This module expects pre-processed metadata defining the elements of the profile
to be present in the attribute metadata
in the including class.
Constant Summary collapse
- DAR_EXTENSION_URL =
'http://hl7.org/fhir/StructureDefinition/data-absent-reason'.freeze
- PRIMITIVE_DATA_TYPES =
FHIR::PRIMITIVES.keys
Instance Method Summary collapse
-
#current_and_child_values_match?(el_found, value_definitions_for_path) ⇒ Boolean
-
#find_a_value_at(given_element, path, include_dar: false, &block) ⇒ Array<FHIR::Model>
Get a value from the given FHIR element(s), by navigating through the resource to the given path.
-
#find_in_elements(elements, include_dar: false) ⇒ Object
-
#find_slice_via_discriminator(element, property) ⇒ Object
-
#flatten_bundles(resources) ⇒ Object
-
#get_next_value(element, property) ⇒ Object
-
#get_primitive_type_value(element, property, value) ⇒ Object
-
#local_field_name(field_name) ⇒ Object
-
#matching_pattern_codeable_concept_slice?(slice, discriminator) ⇒ Boolean
-
#matching_pattern_coding_slice?(slice, discriminator) ⇒ Boolean
-
#matching_pattern_identifier_slice?(slice, discriminator) ⇒ Boolean
-
#matching_required_binding_slice?(slice, discriminator) ⇒ Boolean
-
#matching_slice?(slice, discriminator) ⇒ Boolean
-
#matching_type_slice?(slice, discriminator) ⇒ Boolean
-
#matching_value_slice?(slice, discriminator) ⇒ Boolean
-
#resolve_path(elements, path) ⇒ Array<FHIR::Model>
Get a value from the given FHIR element(s) by walking the given path through the element.
-
#verify_slice_by_values(element, value_definitions) ⇒ Object
Instance Method Details
#current_and_child_values_match?(el_found, value_definitions_for_path) ⇒ Boolean
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 197 def current_and_child_values_match?(el_found, value_definitions_for_path) child_element_value_definitions, current_element_value_definitions = value_definitions_for_path.partition { |value_definition| value_definition[:path].present? } current_element_values_match = current_element_value_definitions .all? { |value_definition| value_definition[:value] == el_found } child_element_values_match = if child_element_value_definitions.present? verify_slice_by_values(el_found, child_element_value_definitions) else true end current_element_values_match && child_element_values_match end |
#find_a_value_at(given_element, path, include_dar: false, &block) ⇒ Array<FHIR::Model>
Get a value from the given FHIR element(s), by navigating through the resource to the given path. Fields with a DataAbsentReason extension present may be selected if include_dar is true. To filter the resulting elements, a block may be passed in.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 39 def find_a_value_at(given_element, path, include_dar: false, &block) return nil if given_element.nil? elements = Array.wrap(given_element) return find_in_elements(elements, include_dar:, &block) if path.empty? path_segments = path.split(/(?<!hl7)\./) segment = path_segments.shift.delete_suffix('[x]').gsub(/^class$/, 'local_class').gsub('[x]:', ':').to_sym remaining_path = path_segments.join('.') elements.each do |element| child = get_next_value(element, segment) element_found = find_a_value_at(child, remaining_path, include_dar:, &block) return element_found if element_found.present? || element_found == false end nil end |
#find_in_elements(elements, include_dar: false) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 59 def find_in_elements(elements, include_dar: false, &) unless include_dar elements = elements.reject do |el| el.respond_to?(:extension) && el.extension.any? { |ext| ext.url == DAR_EXTENSION_URL } end end return elements.find(&) if block_given? elements.first end |
#find_slice_via_discriminator(element, property) ⇒ Object
108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 108 def find_slice_via_discriminator(element, property) return unless .present? element_name = local_field_name(property.to_s.split(':')[0]) slice_name = local_field_name(property.to_s.split(':')[1]) slice_by_name = .must_supports[:slices].find { |slice| slice[:slice_name] == slice_name } discriminator = slice_by_name[:discriminator] slices = Array.wrap(element.send(element_name)) slices.find { |slice| matching_slice?(slice, discriminator) } end |
#flatten_bundles(resources) ⇒ Object
214 215 216 217 218 219 220 221 222 223 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 214 def flatten_bundles(resources) resources.flat_map do |resource| if resource&.resourceType == 'Bundle' # Recursive to consider that Bundles may contain Bundles flatten_bundles(resource.entry.map(&:resource)) else resource end end end |
#get_next_value(element, property) ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 71 def get_next_value(element, property) extension_url = property[/(?<=where\(url=').*(?='\))/] if extension_url.present? element.url == extension_url ? element : nil elsif property.to_s.include?(':') && !property.to_s.include?('url') find_slice_via_discriminator(element, property) else local_name = local_field_name(property) value = element.send(local_name) primitive_value = get_primitive_type_value(element, property, value) primitive_value.present? ? primitive_value : value end rescue NoMethodError nil end |
#get_primitive_type_value(element, property, value) ⇒ Object
88 89 90 91 92 93 94 95 96 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 88 def get_primitive_type_value(element, property, value) source_value = element.source_hash["_#{property}"] return nil unless source_value.present? primitive_value = PrimitiveType.new(source_value) primitive_value.value = value primitive_value end |
#local_field_name(field_name) ⇒ Object
98 99 100 101 102 103 104 105 106 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 98 def local_field_name(field_name) # fhir_models prepends fields whose names are reserved in ruby with "local_" # This should be used before `x.send(field_name)` if ['method', 'class'].include?(field_name.to_s) "local_#{field_name}" else field_name end end |
#matching_pattern_codeable_concept_slice?(slice, discriminator) ⇒ Boolean
137 138 139 140 141 142 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 137 def matching_pattern_codeable_concept_slice?(slice, discriminator) slice_value = discriminator[:path].present? ? slice.send((discriminator[:path]).to_s)&.coding : slice.coding slice_value&.any? do |coding| coding.code == discriminator[:code] && coding.system == discriminator[:system] end end |
#matching_pattern_coding_slice?(slice, discriminator) ⇒ Boolean
144 145 146 147 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 144 def matching_pattern_coding_slice?(slice, discriminator) slice_value = discriminator[:path].present? ? slice.send(discriminator[:path]) : slice slice_value&.code == discriminator[:code] && slice_value&.system == discriminator[:system] end |
#matching_pattern_identifier_slice?(slice, discriminator) ⇒ Boolean
149 150 151 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 149 def matching_pattern_identifier_slice?(slice, discriminator) slice.identifier.system == discriminator[:system] end |
#matching_required_binding_slice?(slice, discriminator) ⇒ Boolean
179 180 181 182 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 179 def matching_required_binding_slice?(slice, discriminator) discriminator[:path].present? ? slice.send((discriminator[:path]).to_s).coding : slice.coding slice_value { |coding| discriminator[:values].include?(coding.code) } end |
#matching_slice?(slice, discriminator) ⇒ Boolean
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 120 def matching_slice?(slice, discriminator) case discriminator[:type] when 'patternCodeableConcept' matching_pattern_codeable_concept_slice?(slice, discriminator) when 'patternCoding' matching_pattern_coding_slice?(slice, discriminator) when 'patternIdentifier' matching_pattern_identifier_slice?(slice, discriminator) when 'value' matching_value_slice?(slice, discriminator) when 'type' matching_type_slice?(slice, discriminator) when 'requiredBinding' matching_required_binding_slice?(slice, discriminator) end end |
#matching_type_slice?(slice, discriminator) ⇒ Boolean
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 158 def matching_type_slice?(slice, discriminator) case discriminator[:code] when 'Date' begin Date.parse(slice) rescue ArgumentError false end when 'DateTime' begin DateTime.parse(slice) rescue ArgumentError false end when 'String' slice.is_a? String else slice.is_a? FHIR.const_get(discriminator[:code]) end end |
#matching_value_slice?(slice, discriminator) ⇒ Boolean
153 154 155 156 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 153 def matching_value_slice?(slice, discriminator) values = discriminator[:values].map { |value| value.merge(path: value[:path].split('.')) } verify_slice_by_values(slice, values) end |
#resolve_path(elements, path) ⇒ Array<FHIR::Model>
Get a value from the given FHIR element(s) by walking the given path through the element.
18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 18 def resolve_path(elements, path) elements = Array.wrap(elements) return elements if path.blank? paths = path.split(/(?<!hl7)\./) segment = paths.first remaining_path = paths.drop(1).join('.') elements.flat_map do |element| child = get_next_value(element, segment) resolve_path(child, remaining_path) end.compact end |
#verify_slice_by_values(element, value_definitions) ⇒ Object
184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/inferno/dsl/fhir_resource_navigation.rb', line 184 def verify_slice_by_values(element, value_definitions) path_prefixes = value_definitions.map { |value_definition| value_definition[:path].first }.uniq path_prefixes.all? do |path_prefix| value_definitions_for_path = value_definitions .select { |value_definition| value_definition[:path].first == path_prefix } .each { |value_definition| value_definition[:path].shift } find_a_value_at(element, path_prefix) do |el_found| current_and_child_values_match?(el_found, value_definitions_for_path) end end end |