Class: AUCoreTestKit::Generator::SearchDefinitionMetadataExtractor

Inherits:
Object
  • Object
show all
Defined in:
lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, ig_resources, profile_elements, group_metadata) ⇒ SearchDefinitionMetadataExtractor

Returns a new instance of SearchDefinitionMetadataExtractor.



10
11
12
13
14
15
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 10

def initialize(name, ig_resources, profile_elements, )
  self.name = name
  self.ig_resources = ig_resources
  self.profile_elements = profile_elements
  self. = 
end

Instance Attribute Details

#group_metadataObject

Returns the value of attribute group_metadata.



8
9
10
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 8

def 
  @group_metadata
end

#ig_resourcesObject

Returns the value of attribute ig_resources.



8
9
10
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 8

def ig_resources
  @ig_resources
end

#nameObject

Returns the value of attribute name.



8
9
10
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 8

def name
  @name
end

#profile_elementsObject

Returns the value of attribute profile_elements.



8
9
10
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 8

def profile_elements
  @profile_elements
end

Instance Method Details

#chainObject



188
189
190
191
192
193
194
195
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 188

def chain
  return nil if param.chain.blank?

  target = param.target.first
  param.chain
       .zip(chain_expectations)
       .map { |chain, expectation| { chain:, expectation:, target: } }
end

#chain_expectationsObject



184
185
186
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 184

def chain_expectations
  chain_extensions.map { |extension| support_expectation(extension) }
end

#chain_extensionsObject



180
181
182
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 180

def chain_extensions
  param_hash['_chain']
end

#comparator_expectation(extension) ⇒ Object



127
128
129
130
131
132
133
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 127

def comparator_expectation(extension)
  if extension.nil?
    'MAY'
  else
    support_expectation(extension)
  end
end

#comparator_expectation_extensionsObject



119
120
121
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 119

def comparator_expectation_extensions
  @comparator_expectation_extensions ||= param_hash['_comparator'] || []
end

#comparatorsObject



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 135

def comparators
  # NOTE: Hard-coded values are used because the comparator expectation
  # does not exist in the machine-readable files, but it does exist in the narrative.
  # NOTE: https://github.com/hl7au/au-fhir-core-inferno/issues/48
  special_cases_resources = %w[Observation Condition Encounter Immunization MedicationRequest Patient Procedure]
  special_cases_comparators = %w[gt lt ge le]
  special_cases_param_ids = %w[clinical-date Condition-onset-date clinical-date MedicationRequest-authoredon individual-birthdate]

  {}.tap do |comparators|
    param.comparator&.each_with_index do |comparator, index|
      comparators[comparator.to_sym] = if (special_cases_resources.include? [:resource]) && (special_cases_comparators.include? comparator) && (special_cases_param_ids.include? param_hash['id'])
                                         'SHALL'
                                       else
                                         comparator_expectation(comparator_expectation_extensions[index])
                                       end
    end
  end
end

#contains_multiple?Boolean

Returns:

  • (Boolean)


164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 164

def contains_multiple?
  if profile_element.present?
    if profile_element.id.start_with?('Extension') && extension_definition.present?
      # Find the extension instance in a AU Core profile
      target_element = profile_elements.find do |element|
        element.type.any? { |type| type.code == 'Extension' && type.profile.include?(extension_definition.url) }
      end
      target_element&.max == '*'
    else
      profile_element.max == '*'
    end
  else
    false
  end
end

#extension_definitionObject



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 107

def extension_definition
  @extension_definition ||=
    begin
      ext_definition = nil
      extensions&.each do ||
        ext_definition = ig_resources.profile_by_url([:url])
        break if ext_definition.present?
      end
      ext_definition
    end
end

#extensionsObject



95
96
97
98
99
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 95

def extensions
  @extensions ||= full_paths.select { |a_path| a_path.include?('extension.where') }
                            .map { |a_path| { url: a_path[/(?<=extension.where\(url=').*(?='\))/] } }
                            .presence
end

#full_pathsObject



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 49

def full_paths
  @full_paths ||=
    begin
      full_paths = param.expression.split('|').map do |expr|
        expr.strip.gsub(/.where\(resolve\((.*)/, '').gsub(/url = '/,
                                                          'url=\'').gsub(/\.ofType\(([^)]+)\)/) do |_match|
          type_name = ::Regexp.last_match(1)
          "#{type_name[0].upcase}#{type_name[1..]}"
        end
      end.filter { |path| path.split('.').first == resource }

      full_paths.map do |path|
        path.scan(/[. ]as[( ]([^)]*)[)]?/).flatten.map do |as_type|
          path.gsub!(/[. ]as[( ](#{as_type}[^)]*)[)]?/, as_type.upcase_first) if as_type.present?
        end
      end

      # path = param.expression.gsub(/.where\(resolve\((.*)/, '').gsub(/url = '/, 'url=\'')
      # path = path[1..-2] if path.start_with?('(') && path.end_with?(')')
      # path.scan(/[. ]as[( ]([^)]*)[)]?/).flatten.map do |as_type|
      #   path.gsub!(/[. ]as[( ](#{as_type}[^)]*)[)]?/, as_type.upcase_first) if as_type.present?
      # end

      # full_paths = path.split('|')
      # # There is a problem with whitespaces in paths
      # full_paths = full_paths.map(&:strip)

      # There is a bug in AU Core 5 asserted-date search parameter. See FHIR-40573
      remove_additional_extension_from_asserted_date(full_paths) if param.respond_to?(:version) && param.version == '5.0.1' && name == 'asserted-date'

      full_paths
    end
end

#multiple_and_expectationObject



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 225

def multiple_and_expectation
  # NOTE: Hard-coded values are used because the multipleAnd attributes
  # do not exist in the machine-readable files, but they do exist in the narrative.
  # NOTE: https://github.com/hl7au/au-fhir-core-inferno/issues/62
  case [:resource]
  when 'Observation'
    return 'SHOULD' if param_hash['id'] == 'clinical-date'
  when 'Condition'
    return 'SHOULD' if param_hash['id'] == 'Condition-onset-date'
  when 'Encounter'
    return 'SHOULD' if param_hash['id'] == 'clinical-date'
  when 'Immunization'
    return 'SHOULD' if param_hash['id'] == 'clinical-date'
  when 'MedicationRequest'
    return 'SHOULD' if param_hash['id'] == 'MedicationRequest-authoredon'
  when 'Patient'
    return 'MAY' if param_hash['id'] == 'individual-birthdate'
  when 'Procedure'
    return 'MAY' if param_hash['id'] == 'clinical-date'
  end
  return unless param_hash['_multipleAnd']

  param_hash['_multipleAnd']['extension'].first['valueCode']
end

#multiple_or_expectationObject



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 197

def multiple_or_expectation
  # NOTE: Hard-coded values are used because the multipleOr attributes
  # do not exist in the machine-readable files, but they do exist in the narrative.
  # NOTE: https://github.com/hl7au/au-fhir-core-inferno/issues/61
  # NOTE: https://github.com/hl7au/au-fhir-core-inferno/issues/63
  case [:resource]
  when 'Procedure'
    return 'SHALL' if param_hash['id'] == 'Procedure-status'
    return 'SHOULD' if param_hash['id'] == 'clinical-code'
  when 'Observation'
    return 'SHALL' if param_hash['id'] == 'Observation-status'
    return 'SHOULD' if param_hash['id'] == 'clinical-code'
  when 'MedicationRequest'
    return 'SHALL' if param_hash['id'] == 'medications-status'
    return 'SHOULD' if param_hash['id'] == 'MedicationRequest-intent'
  when 'Immunization'
    return 'SHOULD' if param_hash['id'] == 'Immunization-vaccine-code'
  when 'Condition'
    return 'MAY' if param_hash['id'] == 'clinical-code'
  when 'Encounter'
    return 'MAY' if param_hash['id'] == 'Encounter-status'
  end

  return unless param_hash['_multipleOr']

  param_hash['_multipleOr']['extension'].first['valueCode']
end

#paramObject



37
38
39
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 37

def param
  @param ||= ig_resources.search_param_by_resource_and_name(resource, name)
end

#param_hashObject



41
42
43
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 41

def param_hash
  param.source_hash
end

#pathsObject



91
92
93
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 91

def paths
  @paths ||= full_paths.map { |a_path| a_path.gsub("#{resource}.", '') }
end

#profile_elementObject



101
102
103
104
105
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 101

def profile_element
  @profile_element ||=
    profile_elements.find { |element| full_paths.include?(element.id) } ||
    extension_definition&.differential&.element&.find { |element| element.id == 'Extension.value[x]' }
end

#remove_additional_extension_from_asserted_date(full_paths) ⇒ Object



83
84
85
86
87
88
89
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 83

def remove_additional_extension_from_asserted_date(full_paths)
  full_paths.each do |full_path|
    next unless full_path.include?('http://hl7.org/fhir/StructureDefinition/condition-assertedDate')

    full_path.gsub!(/\).extension./, ').')
  end
end

#resourceObject



33
34
35
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 33

def resource
  [:resource]
end

#search_definitionObject



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 17

def search_definition
  @search_definition ||=
    {
      paths:,
      full_paths:,
      comparators:,
      values:,
      type:,
      contains_multiple: contains_multiple?,
      multiple_or: multiple_or_expectation,
      multiple_and: multiple_and_expectation,
      chain:,
      target_resource:
    }.compact
end

#support_expectation(extension) ⇒ Object



123
124
125
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 123

def support_expectation(extension)
  extension['extension'].first['valueCode']
end

#target_resourceObject



45
46
47
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 45

def target_resource
  param.target.first
end

#typeObject



154
155
156
157
158
159
160
161
162
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 154

def type
  if profile_element.present?
    profile_element.type.first.code
  else
    # search is a variable type, eg. Condition.onsetDateTime - element
    # in profile def is Condition.onset[x]
    param.type
  end
end

#value_extractorObject



334
335
336
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 334

def value_extractor
  @value_extractor ||= ValueExactor.new(ig_resources, resource, profile_elements)
end

#valuesObject



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 250

def values
  fixed_diagnostic_result_values = %w[251739003 24701-5]
  fixed_date_value = %w[ge1950-01-01 le2050-01-01 gt1950-01-01 lt2050-01-01]
  fixed_datetime_value = %w[ge1950-01-01T00:00:00.000Z le2050-01-01T23:59:59.999Z gt1950-01-01T00:00:00.000Z lt2050-01-01T23:59:59.999Z]
  # NOTE: In the current step we don't need to check the correct content of the response.
  # We should care about the correct structure of the request. In this current case we use dates just
  # to check that server can make a response for the request.
  case [:resource]
  when 'Observation'
    return fixed_datetime_value if param_hash['id'] == 'clinical-date'
    return fixed_diagnostic_result_values if param_hash['id'] =='clinical-code' && [:profile_url] == 'http://hl7.org.au/fhir/core/StructureDefinition/au-core-diagnosticresult'
  when 'Condition'
    return fixed_datetime_value if param_hash['id'] == 'Condition-onset-date'
  when 'Encounter'
    return fixed_datetime_value if param_hash['id'] == 'clinical-date'
  when 'Immunization'
    return fixed_datetime_value if param_hash['id'] == 'clinical-date'
  when 'MedicationRequest'
    return fixed_datetime_value if param_hash['id'] == 'MedicationRequest-authoredon'
  when 'Patient'
    return fixed_date_value if param_hash['id'] == 'individual-birthdate'
  when 'Procedure'
    return fixed_datetime_value if param_hash['id'] == 'clinical-date'
  end

  values_from_fixed_codes = value_extractor.values_from_fixed_codes(profile_element, type).presence
  values_from_pattern_coding = value_extractor.values_from_pattern_coding(profile_element, type).presence
  merged_values = Array(values_from_fixed_codes) + Array(values_from_pattern_coding)

  values_from_must_supports(profile_element).presence || merged_values.presence ||
    # value_extractor.values_from_required_binding(profile_element).presence ||
    value_extractor.values_from_value_set_binding(profile_element).presence ||
    (paths).presence ||
    []
end

#values_from_must_support_elements(short_path) ⇒ Object



320
321
322
323
324
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 320

def values_from_must_support_elements(short_path)
  [:must_supports][:elements]
    .select { |element| element[:path] == "#{short_path}.coding.code" }
    .map { |element| element[:fixed_value] }
end

#values_from_must_support_slices(profile_element, short_path, mandatory_slice_only) ⇒ Object



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 297

def values_from_must_support_slices(profile_element, short_path, mandatory_slice_only)
  return unless [:must_supports][:slices].compact.length.positive?

  [:must_supports][:slices]
    .select { |slice| [short_path, "#{short_path}.coding"].include?(slice[:path]) }
    .map do |slice|
      slice_element = profile_elements.find { |element| slice[:slice_id] == element.id }
      next if profile_element.min.positive? && slice_element.min.zero? && mandatory_slice_only

      case slice[:discriminator][:type]
      when 'patternCoding', 'patternCodeableConcept'
        slice[:discriminator][:code]
      when 'requiredBinding'
        slice[:discriminator][:values]
      when 'value'
        slice[:discriminator][:values]
          .select { |value| value[:path] == 'coding.code' }
          .map { |value| value[:value] }
      end
    end
    .compact.flatten
end

#values_from_must_supports(profile_element) ⇒ Object



286
287
288
289
290
291
292
293
294
295
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 286

def values_from_must_supports(profile_element)
  return if profile_element.nil?

  short_path = profile_element.path.split('.', 2)[1]

  values_from_must_support_slices(profile_element, short_path, true).presence ||
    values_from_must_support_slices(profile_element, short_path, false).presence ||
    values_from_must_support_elements(short_path).presence ||
    []
end

#values_from_resource_metadata(paths) ⇒ Object



326
327
328
329
330
331
332
# File 'lib/au_core_test_kit/generator/search_definition_metadata_extractor.rb', line 326

def (paths)
  if multiple_or_expectation == 'SHALL' || paths.any? { |path| path.downcase.include?('status') }
    value_extractor.(paths)
  else
    []
  end
end