Module: FHIR::CommonStructureDefinitionCompare

Extended by:
Deprecate
Included in:
R4::StructureDefinitionCompare, R4B::StructureDefinitionCompare, R5::StructureDefinitionCompare
Defined in:
lib/fhir_models/fhir_ext/common_structure_definition_compare.rb

Overview

Extend StructureDefinition for profile comparison code

Instance Method Summary collapse

Methods included from Deprecate

deprecate

Instance Method Details

#compatible?(another_definition) ⇒ Boolean

Checks whether or not “another_definition” is compatible with this definition. If they have conflicting elements, restrictions, bindings, modifying extensions, etc.

Returns:

  • (Boolean)


12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/fhir_models/fhir_ext/common_structure_definition_compare.rb', line 12

def compatible?(another_definition)
  @errors = []
  @warnings = []

  @finding = FHIR::StructureDefinitionFinding.new
  @finding.resourceType = snapshot.element[0].path
  @finding.profileIdA = id
  @finding.profileIdB = another_definition.id if another_definition.respond_to?(:id)

  if !(another_definition.is_a? versioned_fhir_module::StructureDefinition)
    @errors << @finding.error('', '', 'Not a StructureDefinition', 'StructureDefinition', another_definition.class.name.to_s)
    return false
  elsif another_definition.snapshot.element[0].path != snapshot.element[0].path
    @errors << @finding.error('', '', 'Incompatible resourceType', @finding.resourceType, another_definition.snapshot.element[0].path.to_s)
    return false
  end

  left_elements = Array.new(snapshot.element)
  right_elements = Array.new(another_definition.snapshot.element)

  left_paths = left_elements.map(&:path)
  right_paths = right_elements.map(&:path)

  # StructureDefinitions don't always include all base attributes (for example, of a ContactPoint)
  # if nothing is modified from the base definition, so we have to add them in if they are missing.
  base_definition = versioned_fhir_module::Definitions.get_resource_definition(snapshot.element[0].path)
  base_elements = base_definition.snapshot.element

  left_missing = right_paths - left_paths
  # left_missing_roots = left_missing.map{|e| e.split('.')[0..-2].join('.') }.uniq
  add_missing_elements(id, left_missing, left_elements, base_elements)

  right_missing = left_paths - right_paths
  # right_missing_roots = right_missing.map{|e| e.split('.')[0..-2].join('.') }.uniq
  add_missing_elements(another_definition.id, right_missing, right_elements, base_elements)

  # update paths
  left_paths = left_elements.map(&:path)
  right_paths = right_elements.map(&:path)

  # recalculate the missing attributes
  left_missing = right_paths - left_paths
  right_missing = left_paths - right_paths

  # generate warnings for missing fields (ignoring extensions)
  left_missing.each do |e|
    next if e.include? 'extension'

    elem = get_element_by_path(e, right_elements)
    if !elem.min.nil? && elem.min.positive?
      @errors << @finding.error(e, 'min', 'Missing REQUIRED element', 'Missing', elem.min.to_s)
    elsif elem.isModifier == true
      @errors << @finding.error(e, 'isModifier', 'Missing MODIFIER element', 'Missing', elem.isModifier.to_s)
    else
      @warnings << @finding.warning(e, '', 'Missing element', 'Missing', 'Defined')
    end
  end
  right_missing.each do |e|
    next if e.include? 'extension'

    elem = get_element_by_path(e, left_elements)
    if !elem.min.nil? && elem.min.positive?
      @errors << @finding.error(e, 'min', 'Missing REQUIRED element', elem.min.to_s, 'Missing')
    elsif elem.isModifier == true
      @errors << @finding.error(e, 'isModifier', 'Missing MODIFIER element', elem.isModifier.to_s, 'Missing')
    else
      @warnings << @finding.warning(e, '', 'Missing element', 'Defined', 'Missing')
    end
  end

  left_extensions = []
  right_extensions = []

  # compare elements, starting with the elements in this definition
  left_elements.each do |x|
    if x.path.include? 'extension'
      # handle extensions separately
      left_extensions << x
    else
      y = get_element_by_path(x.path, right_elements)
      compare_element_definitions(x, y, another_definition)
    end
  end

  # now compare elements defined in the other definition, if we haven't already looked at them
  right_elements.each do |y|
    if y.path.include? 'extension'
      # handle extensions separately
      right_extensions << y
    elsif left_missing.include? y.path
      x = get_element_by_path(y.path, left_elements)
      compare_element_definitions(x, y, another_definition)
    end
  end

  # finally, compare the extensions.
  checked_extensions = []
  left_extensions.each do |x|
    y = get_extension(x.name, right_extensions)
    unless y.nil?
      # both profiles share an extension with the same name
      checked_extensions << x.name
      compare_extension_definition(x, y, another_definition)
    end
    y = get_extension(x.type[0].profile, right_extensions)
    next unless !y.nil? && x.name != y.name

    # both profiles share the same extension definition but with a different name
    checked_extensions << x.name
    checked_extensions << y.name
    compare_element_definitions(x, y, another_definition)
  end
  right_extensions.each do |y|
    next if checked_extensions.include?(y.name)

    x = get_extension(y.name, left_extensions)
    unless x.nil?
      # both profiles share an extension with the same name
      checked_extensions << y.name
      compare_extension_definition(x, y, another_definition)
    end
    x = get_extension(y.type[0].profile, left_extensions)
    next unless !x.nil? && x.name != y.name && !checked_extensions.include?(x.name)

    # both profiles share the same extension definition but with a different name
    checked_extensions << x.name
    checked_extensions << y.name
    compare_element_definitions(x, y, another_definition)
  end
  @errors.flatten!
  @warnings.flatten!
  @errors.size.zero?
end

#get_element_by_path(path, elements = snapshot.element) ⇒ Object



147
148
149
# File 'lib/fhir_models/fhir_ext/common_structure_definition_compare.rb', line 147

def get_element_by_path(path, elements = snapshot.element)
  elements.detect { |element| element.path == path }
end

#get_extension(extension, elements = snapshot.element) ⇒ Object



151
152
153
154
155
156
157
158
# File 'lib/fhir_models/fhir_ext/common_structure_definition_compare.rb', line 151

def get_extension(extension, elements = snapshot.element)
  elements.each do |element|
    if element.path.include?('extension') || element.type.map(&:code).include?('Extension')
      return element if element.name == extension || element.type.map(&:profile).include?(extension)
    end
  end
  nil
end