inferno_core 0.6.7 → 0.6.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48f096486362742aa31ed9f7edec9df07786be3ac7e4f5dfdf121c65b61da1e5
4
- data.tar.gz: d6edfd6063f0ba5b83708b86ee5ccc9e0302a32e9a1f96473bbabeabc2d18184
3
+ metadata.gz: 6daa4c1864a7e7004ca9a166f3e516fcddc157c3f6b6e24722965e44330cc450
4
+ data.tar.gz: b158c90f63eaa8a157a27b92af68b7a3be7798e18aca2243ecbd1071c9d9bfdf
5
5
  SHA512:
6
- metadata.gz: e065c783d7def8ca873e964f61b9ab4cfcf01a352c99e63157d11eb5ca909f617ee031d5421e26b244caa4f36831ae6574160ac59e2d99647aa2d6d99a152fa0
7
- data.tar.gz: 68c8b17c1c635fc4e2986d1e56b06225aa858cf0c049447957c33982f16d0587a0f0407f24c4164ca8b9c08fdbbe6adae693f7a6b1abf0fd9ca258efc50c7db1
6
+ metadata.gz: 6298ba6c1f61a6ee3f5e50045ac58e1ebaa63a2ebc863755d1c610d3eed4b0e7ca76ea74ee12c506298624248c5f3b874582eaccc1c41600f731b33a601d72ce
7
+ data.tar.gz: 61f9b65616cf01dd42c838ab0b9be5c38490dea22bb2a3481a82c30a25c687e2aada97321498920f6219a62afe88445ad95e71b2300859fce5ed19af7cef1f78
@@ -1,7 +1,6 @@
1
1
  require_relative '../../dsl/fhir_evaluation/evaluator'
2
2
  require_relative '../../dsl/fhir_evaluation/config'
3
3
  require_relative '../../entities'
4
- require_relative '../../repositories'
5
4
  require_relative '../../utils/ig_downloader'
6
5
 
7
6
  require 'tempfile'
@@ -10,6 +9,12 @@ module Inferno
10
9
  module CLI
11
10
  class Evaluate < Thor::Group
12
11
  def evaluate(ig_path, data_path, _log_level)
12
+ # NOTE: repositories is required here rather than at the top of the file because
13
+ # the tree of requires means that this file and its requires get required by every CLI app.
14
+ # Sequel::Model, used in some repositories, fetches the table schema at instantiation.
15
+ # This breaks the `migrate` task by fetching a table before the task runs/creates it.
16
+ require_relative '../../repositories'
17
+
13
18
  validate_args(ig_path, data_path)
14
19
  ig = Inferno::Repositories::IGs.new.find_or_load(ig_path)
15
20
 
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Inferno
4
+ module DSL
5
+ module FHIREvaluation
6
+ module Rules
7
+ class AllDefinedExtensionsHaveExamples < Rule
8
+ attr_accessor :used_extensions, :unused_extensions
9
+
10
+ def check(context)
11
+ @used_extensions = context.data.map { |e| extension_urls(e) }.flatten.uniq
12
+ @unused_extensions = []
13
+
14
+ get_unused_extensions(context.ig.extensions) do |extension|
15
+ next true if extension.context.any? do |ctx|
16
+ # Skip extensions that are defined for definitional artifacts.
17
+ # For example, US Core's uscdi-requirement extension is applied to
18
+ # the profiles and extensions of the IG, not data that conforms to the IG.
19
+ # There may eventually be cases where SD/ED are data, so this may become configurable.
20
+ ctx.expression == 'StructureDefinition' || ctx.expression == 'ElementDefinition'
21
+ end
22
+
23
+ versioned_url = "#{extension.url}|#{extension.version}"
24
+ used_extensions.include?(extension.url) || used_extensions.include?(versioned_url)
25
+ end
26
+
27
+ if unused_extensions.any?
28
+ message = "Found unused extensions defined in the IG: \n\t #{unused_extensions.join(', ')}"
29
+ result = EvaluationResult.new(message, rule: self)
30
+ else
31
+ message = 'All defined extensions are represented in examples'
32
+ result = EvaluationResult.new(message, severity: 'success', rule: self)
33
+ end
34
+
35
+ context.add_result result
36
+ end
37
+
38
+ def extension_urls(resource)
39
+ urls = []
40
+ resource.each_element do |value, _metadata, path|
41
+ path_elements = path.split('.')
42
+ next unless path_elements.length > 1
43
+
44
+ urls << value if path_elements[-2].include?('extension') && path_elements[-1] == 'url'
45
+ end
46
+ urls.uniq
47
+ end
48
+
49
+ def get_unused_extensions(extensions, &extension_filter)
50
+ extensions.each do |extension|
51
+ unused_extensions.push extension.url unless extension_filter.call(extension)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,49 @@
1
+ module Inferno
2
+ module DSL
3
+ module FHIREvaluation
4
+ module Rules
5
+ class AllProfilesHaveExamples < Rule
6
+ include ProfileConformanceHelper
7
+
8
+ attr_accessor :context, :unused_profile_urls, :all_resources
9
+
10
+ def check(context)
11
+ @context = context
12
+ @unused_profile_urls = []
13
+ @all_resources = []
14
+ options = context.config.data['Rule']['AllProfilesHaveExamples']['ConformanceOptions'].to_options
15
+
16
+ context.data.map { |entry| extract_resources(entry) }
17
+ all_resources.uniq!
18
+
19
+ context.ig.profiles.each do |profile|
20
+ profile_used = all_resources.any? do |resource|
21
+ conforms_to_profile?(resource, profile, options, context.validator)
22
+ end
23
+ unused_profile_urls << profile.url unless profile_used
24
+ end
25
+
26
+ unused_profile_urls.uniq!
27
+
28
+ if unused_profile_urls.any?
29
+ message = "Found profiles without examples: \n\t #{unused_profile_urls.join(', ')}"
30
+ result = EvaluationResult.new(message, rule: self)
31
+ else
32
+ message = 'All profiles have example instances.'
33
+ result = EvaluationResult.new(message, severity: 'success', rule: self)
34
+ end
35
+
36
+ context.add_result result
37
+ end
38
+
39
+ def extract_resources(resource)
40
+ all_resources << resource
41
+ return unless resource.resourceType == 'Bundle'
42
+
43
+ resource.entry.map { |entry| extract_resources(entry.resource) }.flatten
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,79 @@
1
+ require_relative '../../fhirpath_evaluation'
2
+
3
+ module Inferno
4
+ module DSL
5
+ module FHIREvaluation
6
+ module Rules
7
+ class AllSearchParametersHaveExamples < Rule
8
+ include FhirpathEvaluation
9
+
10
+ def check(context)
11
+ unless ENV['FHIRPATH_URL']
12
+ message = 'FHIRPATH_URL is not found. Skipping rule AllSearchParametersHaveExamples.'
13
+ result = EvaluationResult.new(message, severity: 'warning', rule: self)
14
+ context.add_result result
15
+ return
16
+ end
17
+
18
+ unused_resource_urls = []
19
+ search_params = context.ig.resources_by_type['SearchParameter']
20
+
21
+ search_params.each do |search_param|
22
+ unused_resource_urls.push search_param.url unless param_is_used?(search_param, context)
23
+ end
24
+
25
+ if unused_resource_urls.any?
26
+ message = "Found SearchParameters with no searchable data: \n\t#{unused_resource_urls.join(' ,')}"
27
+ result = EvaluationResult.new(message, rule: self)
28
+ elsif !search_params.empty?
29
+ message = 'All SearchParameters have examples'
30
+ result = EvaluationResult.new(message, severity: 'success', rule: self)
31
+ else
32
+ message = 'IG contains no SearchParameter'
33
+ result = EvaluationResult.new(message, severity: 'information', rule: self)
34
+ end
35
+
36
+ context.add_result result
37
+ end
38
+
39
+ def param_is_used?(param, context)
40
+ # Assume that all params have an expression (fhirpath)
41
+ # This is not guaranteed since the field is 0..1
42
+ # but without it there's no other way to select a value
43
+ # Return warning if params don't include expression
44
+ unless param.expression
45
+ message = "Search parameter #{param.url} doesn't have an expression."
46
+ result = EvaluationResult.new(message, severity: 'warning', rule: self)
47
+ context.add_result result
48
+ return false
49
+ end
50
+
51
+ used = false
52
+
53
+ context.data.each do |resource|
54
+ next unless param.base.include? resource.resourceType
55
+
56
+ begin
57
+ result = evaluate_fhirpath(resource: resource, path: param.expression)
58
+ rescue StandardError => e
59
+ message = "SearchParameter #{param.url} failed to evaluate due to an error. " \
60
+ "Expression: #{param.expression}. #{e}"
61
+ result = EvaluationResult.new(message)
62
+ context.add_result result
63
+
64
+ used = true
65
+ break
66
+ end
67
+
68
+ if result.present?
69
+ used = true
70
+ break
71
+ end
72
+ end
73
+ used
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end