inferno_core 0.5.2 → 0.5.4
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 +4 -4
- data/lib/inferno/apps/cli/evaluate.rb +97 -0
- data/lib/inferno/apps/cli/main.rb +38 -0
- data/lib/inferno/apps/cli/templates/%library_name%.gemspec.tt +10 -3
- data/lib/inferno/apps/cli/templates/Dockerfile.tt +3 -2
- data/lib/inferno/apps/cli/templates/lib/%library_name%/metadata.rb.tt +18 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%/suite.rb.tt +59 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%/version.rb.tt +3 -0
- data/lib/inferno/apps/cli/templates/lib/%library_name%.rb.tt +1 -58
- data/lib/inferno/apps/web/serializers/input.rb +2 -1
- data/lib/inferno/apps/web/serializers/markdown_extractor.rb +16 -0
- data/lib/inferno/config/boot/presets.rb +18 -1
- data/lib/inferno/dsl/fhir_evaluation/config.rb +21 -0
- data/lib/inferno/dsl/fhir_evaluation/dataset_loader.rb +33 -0
- data/lib/inferno/dsl/fhir_evaluation/evaluation_context.rb +25 -0
- data/lib/inferno/dsl/fhir_evaluation/evaluation_result.rb +62 -0
- data/lib/inferno/dsl/fhir_evaluation/evaluator.rb +36 -0
- data/lib/inferno/dsl/fhir_evaluation/rule.rb +13 -0
- data/lib/inferno/dsl/suite_endpoint.rb +58 -58
- data/lib/inferno/dsl.rb +2 -0
- data/lib/inferno/entities/ig.rb +149 -0
- data/lib/inferno/entities/test_kit.rb +4 -2
- data/lib/inferno/entities/test_suite.rb +23 -3
- data/lib/inferno/entities.rb +2 -0
- data/lib/inferno/public/bundle.js +2 -2
- data/lib/inferno/repositories/igs.rb +9 -0
- data/lib/inferno/repositories/presets.rb +12 -6
- data/lib/inferno/result_summarizer.rb +2 -0
- data/lib/inferno/utils/ig_downloader.rb +3 -3
- data/lib/inferno/utils/named_thor_actions.rb +5 -1
- data/lib/inferno/version.rb +1 -1
- data/spec/extract_tgz_helper.rb +13 -0
- metadata +17 -5
- data/lib/inferno/public/175.bundle.js +0 -1
- data/lib/inferno/public/217.bundle.js +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e11d6b274778016418f7189d8ec9e724cf20861c6e03385dc9c302299f198d18
|
4
|
+
data.tar.gz: '09c65484d51b2f7be0a3985b374d51593d59c432ba11c21bd10d54c798ccafa4'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0daf93ce97d8a5a8e9b409200d7c001e4ac49107887db05073399a74795a94b449d3c1eb1d139214c1402e1ea10a954ee1271b80c642909c379cd7bb0ba7a1f0
|
7
|
+
data.tar.gz: 3a70bb18128af1494e0fcd1a023d8e18e8043dd535ae9c00f8471c8eb85e23678fa7c8bcb19591852c3593c6588ba92af206a90644cf6288659179fef98c1a41
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require_relative '../../../inferno/dsl/fhir_evaluation/evaluator'
|
2
|
+
require_relative '../../../inferno/entities'
|
3
|
+
require_relative '../../utils/ig_downloader'
|
4
|
+
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
module Inferno
|
8
|
+
module CLI
|
9
|
+
class Evaluate < Thor::Group
|
10
|
+
include Thor::Actions
|
11
|
+
include Inferno::Utils::IgDownloader
|
12
|
+
|
13
|
+
def evaluate(ig_path, data_path, _log_level)
|
14
|
+
validate_args(ig_path, data_path)
|
15
|
+
_ig = get_ig(ig_path)
|
16
|
+
|
17
|
+
# Rule execution, and result output below will be integrated soon.
|
18
|
+
|
19
|
+
# if data_path
|
20
|
+
# DatasetLoader.from_path(File.join(__dir__, data_path))
|
21
|
+
# else
|
22
|
+
# ig.examples
|
23
|
+
# end
|
24
|
+
|
25
|
+
# config = Config.new
|
26
|
+
# evaluator = Inferno::DSL::FHIREvaluation::Evaluator.new(data, config)
|
27
|
+
|
28
|
+
# results = evaluate()
|
29
|
+
# output_results(results, options[:output])
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_args(ig_path, data_path)
|
33
|
+
raise 'A path to an IG is required!' unless ig_path
|
34
|
+
|
35
|
+
return unless data_path && (!File.directory? data_path)
|
36
|
+
|
37
|
+
raise "Provided path '#{data_path}' is not a directory"
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_ig(ig_path)
|
41
|
+
if File.exist?(ig_path)
|
42
|
+
ig = Inferno::Entities::IG.from_file(ig_path)
|
43
|
+
elsif in_user_package_cache?(ig_path.sub('@', '#'))
|
44
|
+
# NPM syntax for a package identifier is id@version (eg, hl7.fhir.us.core@3.1.1)
|
45
|
+
# but in the cache the separator is # (hl7.fhir.us.core#3.1.1)
|
46
|
+
cache_directory = File.join(user_package_cache, ig_path.sub('@', '#'))
|
47
|
+
ig = Inferno::Entities::IG.from_file(cache_directory)
|
48
|
+
else
|
49
|
+
Tempfile.create('package.tgz') do |temp_file|
|
50
|
+
load_ig(ig_path, nil, { force: true }, temp_file.path)
|
51
|
+
ig = Inferno::Entities::IG.from_file(temp_file.path)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
ig.add_self_to_repository
|
55
|
+
ig
|
56
|
+
end
|
57
|
+
|
58
|
+
def user_package_cache
|
59
|
+
File.join(Dir.home, '.fhir', 'packages')
|
60
|
+
end
|
61
|
+
|
62
|
+
def in_user_package_cache?(ig_identifier)
|
63
|
+
File.directory?(File.join(user_package_cache, ig_identifier))
|
64
|
+
end
|
65
|
+
|
66
|
+
def output_results(results, output)
|
67
|
+
if output&.end_with?('json')
|
68
|
+
oo = FhirEvaluator::EvaluationResult.to_operation_outcome(results)
|
69
|
+
File.write(output, oo.to_json)
|
70
|
+
puts "Results written to #{output}"
|
71
|
+
else
|
72
|
+
counts = results.group_by(&:severity).transform_values(&:count)
|
73
|
+
print(counts, 'Result Count')
|
74
|
+
puts "\n"
|
75
|
+
puts results
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def print(output_fields, title)
|
80
|
+
puts("╔══════════════ #{title} ═══════════════╗")
|
81
|
+
puts('║ ╭────────────────┬──────────────────────╮ ║')
|
82
|
+
output_fields.each_with_index do |(key, value), i|
|
83
|
+
field_name = pad(key, 14)
|
84
|
+
field_value = pad(value.to_s, 20)
|
85
|
+
puts("║ │ #{field_name} │ #{field_value} │ ║")
|
86
|
+
puts('║ ├────────────────┼──────────────────────┤ ║') unless i == output_fields.length - 1
|
87
|
+
end
|
88
|
+
puts('║ ╰────────────────┴──────────────────────╯ ║')
|
89
|
+
puts('╚═══════════════════════════════════════════╝')
|
90
|
+
end
|
91
|
+
|
92
|
+
def pad(string, length)
|
93
|
+
format("%#{length}.#{length}s", string)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative 'console'
|
2
|
+
require_relative 'evaluate'
|
2
3
|
require_relative 'migration'
|
3
4
|
require_relative 'services'
|
4
5
|
require_relative 'suite'
|
@@ -10,6 +11,43 @@ require_relative 'execute'
|
|
10
11
|
module Inferno
|
11
12
|
module CLI
|
12
13
|
class Main < Thor
|
14
|
+
desc 'evaluate', 'Run a FHIR Data Evaluator.'
|
15
|
+
long_desc <<-LONGDESC
|
16
|
+
Evaluate FHIR data in the context of a given Implementation Guide,
|
17
|
+
by applying a set of predefined rules designed to check that datasets are comprehensive.
|
18
|
+
Issues identified will be printed to console or to a json file.
|
19
|
+
|
20
|
+
You must have background services running: `bundle exec inferno services start`
|
21
|
+
|
22
|
+
Run the evaluation CLI with
|
23
|
+
|
24
|
+
`bundle exec inferno evaluate ig_path`
|
25
|
+
|
26
|
+
Examples:
|
27
|
+
|
28
|
+
# Load the us core ig and evaluate the data in the provided example folder. If there are examples in the IG already, they will be ignored.
|
29
|
+
`bundle exec inferno evaluate ./uscore.tgz -d ./package/example`
|
30
|
+
|
31
|
+
# Loads the us core ig and evaluate the data included in the IG's example folder
|
32
|
+
`bundle exec inferno evaluate ./uscore.tgz`
|
33
|
+
|
34
|
+
# Loads the us core ig and evaluate the data included in the IG's example folder, with results redirected to outcome.json as an OperationOutcome
|
35
|
+
`bundle exec inferno evaluate ./uscore.tgz --output outcome.json`
|
36
|
+
LONGDESC
|
37
|
+
# TODO: Add options below as arguments
|
38
|
+
option :data_path,
|
39
|
+
aliases: ['-d'],
|
40
|
+
type: :string,
|
41
|
+
desc: 'Example FHIR data path'
|
42
|
+
# TODO: implement option of exporting result as OperationOutcome
|
43
|
+
option :output,
|
44
|
+
aliases: ['-o'],
|
45
|
+
type: :string,
|
46
|
+
desc: 'Export evaluation result to outcome.json as an OperationOutcome'
|
47
|
+
def evaluate(ig_path)
|
48
|
+
Evaluate.new.evaluate(ig_path, options[:data_path], Logger::INFO)
|
49
|
+
end
|
50
|
+
|
13
51
|
desc 'console', 'Start an interactive console session with Inferno'
|
14
52
|
def console
|
15
53
|
Migration.new.run(Logger::INFO)
|
@@ -1,11 +1,15 @@
|
|
1
|
+
require_relative 'lib/<%= library_name %>/version'
|
2
|
+
|
1
3
|
Gem::Specification.new do |spec|
|
2
4
|
spec.name = '<%= library_name %>'
|
3
|
-
spec.version =
|
5
|
+
spec.version = <%= module_name %>::VERSION
|
4
6
|
spec.authors = <%= authors %>
|
5
7
|
# spec.email = ['TODO']
|
6
8
|
spec.date = Time.now.utc.strftime('%Y-%m-%d')
|
7
|
-
spec.summary = '<%= title_name %>
|
8
|
-
spec.description =
|
9
|
+
spec.summary = '<%= title_name %>'
|
10
|
+
# spec.description = <<~DESCRIPTION
|
11
|
+
# This is a big markdown description of the test kit.
|
12
|
+
# DESCRIPTION
|
9
13
|
# spec.homepage = 'TODO'
|
10
14
|
spec.license = 'Apache-2.0'
|
11
15
|
spec.add_runtime_dependency 'inferno_core', '~> <%= Inferno::VERSION %>'
|
@@ -14,11 +18,14 @@ Gem::Specification.new do |spec|
|
|
14
18
|
spec.add_development_dependency 'rspec', '~> 3.10'
|
15
19
|
spec.add_development_dependency 'webmock', '~> 3.11'
|
16
20
|
spec.required_ruby_version = Gem::Requirement.new('>= 3.1.2')
|
21
|
+
spec.metadata['inferno_test_kit'] = 'true'
|
17
22
|
# spec.metadata['homepage_uri'] = spec.homepage
|
18
23
|
# spec.metadata['source_code_uri'] = 'TODO'
|
19
24
|
spec.files = [
|
20
25
|
Dir['lib/**/*.rb'],
|
21
26
|
Dir['lib/**/*.json'],
|
27
|
+
Dir['config/presets/*.json'],
|
28
|
+
Dir['config/presets/*.json.erb'],
|
22
29
|
'LICENSE'
|
23
30
|
].flatten
|
24
31
|
|
@@ -6,12 +6,13 @@ RUN mkdir -p $INSTALL_PATH
|
|
6
6
|
|
7
7
|
WORKDIR $INSTALL_PATH
|
8
8
|
|
9
|
+
ADD lib/<%= library_name %>/metadata.rb $INSTALL_PATH/lib/<%= library_name %>/metadata.rb
|
9
10
|
ADD *.gemspec $INSTALL_PATH
|
10
11
|
ADD Gemfile* $INSTALL_PATH
|
11
12
|
RUN gem install bundler
|
12
|
-
# The below RUN line is commented out for development purposes, because any change to the
|
13
|
+
# The below RUN line is commented out for development purposes, because any change to the
|
13
14
|
# required gems will break the dockerfile build process.
|
14
|
-
# If you want to run in Deploy mode, just run `bundle install` locally to update
|
15
|
+
# If you want to run in Deploy mode, just run `bundle install` locally to update
|
15
16
|
# Gemfile.lock, and uncomment the following line.
|
16
17
|
# RUN bundle config set --local deployment 'true'
|
17
18
|
RUN bundle install
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'version'
|
2
|
+
|
3
|
+
module <%= module_name %>
|
4
|
+
class Metadata < Inferno::TestKit
|
5
|
+
id :<%= test_kit_id %>
|
6
|
+
title '<%= title_name %>'
|
7
|
+
description <<~DESCRIPTION
|
8
|
+
This is a big markdown description of the test kit.
|
9
|
+
DESCRIPTION
|
10
|
+
suite_ids [:<%= test_suite_id %>]
|
11
|
+
# tags ['SMART App Launch', 'US Core']
|
12
|
+
# last_updated '2024-03-07'
|
13
|
+
version VERSION
|
14
|
+
maturity 'Low'
|
15
|
+
authors <%= authors %>
|
16
|
+
# repo 'TODO'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'metadata'
|
2
|
+
require_relative 'patient_group'
|
3
|
+
|
4
|
+
module <%= module_name %>
|
5
|
+
class Suite < Inferno::TestSuite
|
6
|
+
id :<%= test_suite_id %>
|
7
|
+
title '<%= title_name %> Test Suite'
|
8
|
+
description '<%= human_name %> test suite.'
|
9
|
+
|
10
|
+
# These inputs will be available to all tests in this suite
|
11
|
+
input :url,
|
12
|
+
title: 'FHIR Server Base Url'
|
13
|
+
|
14
|
+
input :credentials,
|
15
|
+
title: 'OAuth Credentials',
|
16
|
+
type: :oauth_credentials,
|
17
|
+
optional: true
|
18
|
+
|
19
|
+
# All FHIR requests in this suite will use this FHIR client
|
20
|
+
fhir_client do
|
21
|
+
url :url
|
22
|
+
oauth_credentials :credentials
|
23
|
+
end
|
24
|
+
|
25
|
+
# All FHIR validation requests will use this FHIR validator
|
26
|
+
fhir_resource_validator do
|
27
|
+
# igs 'identifier#version' # Use this method for published IGs/versions
|
28
|
+
# igs 'igs/filename.tgz' # Use this otherwise
|
29
|
+
|
30
|
+
exclude_message do |message|
|
31
|
+
message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Tests and TestGroups can be defined inline
|
36
|
+
group do
|
37
|
+
id :capability_statement
|
38
|
+
title 'Capability Statement'
|
39
|
+
description 'Verify that the server has a CapabilityStatement'
|
40
|
+
|
41
|
+
test do
|
42
|
+
id :capability_statement_read
|
43
|
+
title 'Read CapabilityStatement'
|
44
|
+
description 'Read CapabilityStatement from /metadata endpoint'
|
45
|
+
|
46
|
+
run do
|
47
|
+
fhir_get_capability_statement
|
48
|
+
|
49
|
+
assert_response_status(200)
|
50
|
+
assert_resource_type(:capability_statement)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Tests and TestGroups can be written in separate files and then included
|
56
|
+
# using their id
|
57
|
+
group from: :patient_group
|
58
|
+
end
|
59
|
+
end
|
@@ -1,58 +1 @@
|
|
1
|
-
require_relative '<%= library_name %>/
|
2
|
-
|
3
|
-
module <%= module_name %>
|
4
|
-
class Suite < Inferno::TestSuite
|
5
|
-
id :<%= test_suite_id %>
|
6
|
-
title '<%= title_name %> Test Suite'
|
7
|
-
description '<%= human_name %> test suite.'
|
8
|
-
|
9
|
-
# These inputs will be available to all tests in this suite
|
10
|
-
input :url,
|
11
|
-
title: 'FHIR Server Base Url'
|
12
|
-
|
13
|
-
input :credentials,
|
14
|
-
title: 'OAuth Credentials',
|
15
|
-
type: :oauth_credentials,
|
16
|
-
optional: true
|
17
|
-
|
18
|
-
# All FHIR requests in this suite will use this FHIR client
|
19
|
-
fhir_client do
|
20
|
-
url :url
|
21
|
-
oauth_credentials :credentials
|
22
|
-
end
|
23
|
-
|
24
|
-
# All FHIR validation requests will use this FHIR validator
|
25
|
-
fhir_resource_validator do
|
26
|
-
# igs 'identifier#version' # Use this method for published IGs/versions
|
27
|
-
# igs 'igs/filename.tgz' # Use this otherwise
|
28
|
-
|
29
|
-
exclude_message do |message|
|
30
|
-
message.message.match?(/\A\S+: \S+: URL value '.*' does not resolve/)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# Tests and TestGroups can be defined inline
|
35
|
-
group do
|
36
|
-
id :capability_statement
|
37
|
-
title 'Capability Statement'
|
38
|
-
description 'Verify that the server has a CapabilityStatement'
|
39
|
-
|
40
|
-
test do
|
41
|
-
id :capability_statement_read
|
42
|
-
title 'Read CapabilityStatement'
|
43
|
-
description 'Read CapabilityStatement from /metadata endpoint'
|
44
|
-
|
45
|
-
run do
|
46
|
-
fhir_get_capability_statement
|
47
|
-
|
48
|
-
assert_response_status(200)
|
49
|
-
assert_resource_type(:capability_statement)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
# Tests and TestGroups can be written in separate files and then included
|
55
|
-
# using their id
|
56
|
-
group from: :patient_group
|
57
|
-
end
|
58
|
-
end
|
1
|
+
require_relative '<%= library_name %>/suite'
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative 'markdown_extractor'
|
1
2
|
require_relative 'serializer'
|
2
3
|
|
3
4
|
module Inferno
|
@@ -7,7 +8,7 @@ module Inferno
|
|
7
8
|
identifier :name
|
8
9
|
|
9
10
|
field :title, if: :field_present?
|
10
|
-
field :description, if: :field_present?
|
11
|
+
field :description, extractor: MarkdownExtractor, if: :field_present?
|
11
12
|
field :type, if: :field_present?
|
12
13
|
field :default, if: :field_present?
|
13
14
|
field :optional, if: :field_present?
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'blueprinter'
|
2
|
+
require_relative '../../../utils/markdown_formatter'
|
3
|
+
|
4
|
+
module Inferno
|
5
|
+
module Web
|
6
|
+
module Serializers
|
7
|
+
class MarkdownExtractor < Blueprinter::Extractor
|
8
|
+
include Inferno::Utils::MarkdownFormatter
|
9
|
+
|
10
|
+
def extract(field_name, object, _local_options, _options = {})
|
11
|
+
format_markdown(object.send(field_name))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -4,9 +4,26 @@ Inferno::Application.register_provider(:presets) do
|
|
4
4
|
prepare do
|
5
5
|
target_container.start :suites
|
6
6
|
|
7
|
+
presets_repo = Inferno::Repositories::Presets.new
|
8
|
+
|
9
|
+
test_kit_gems =
|
10
|
+
Bundler
|
11
|
+
.definition
|
12
|
+
.specs
|
13
|
+
.select { |spec| spec.metadata.fetch('inferno_test_kit', 'false').casecmp? 'true' }
|
14
|
+
|
7
15
|
files_to_load = Dir.glob(['config/presets/*.json', 'config/presets/*.json.erb'])
|
16
|
+
files_to_load +=
|
17
|
+
test_kit_gems.flat_map do |gem|
|
18
|
+
[
|
19
|
+
Dir.glob([File.join(gem.full_gem_path, 'config', 'presets', '*.json')]),
|
20
|
+
Dir.glob([File.join(gem.full_gem_path, 'config', 'presets', '*.json.erb')])
|
21
|
+
].flatten
|
22
|
+
end
|
23
|
+
|
24
|
+
files_to_load.compact!
|
25
|
+
files_to_load.uniq!
|
8
26
|
files_to_load.map! { |path| File.realpath(path) }
|
9
|
-
presets_repo = Inferno::Repositories::Presets.new
|
10
27
|
|
11
28
|
files_to_load.each do |path|
|
12
29
|
presets_repo.insert_from_file(path)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Inferno
|
2
|
+
module DSL
|
3
|
+
module FHIREvaluation
|
4
|
+
class Config
|
5
|
+
DEFAULT_FILE = File.join(__dir__, 'default.yml')
|
6
|
+
attr_accessor :data
|
7
|
+
|
8
|
+
# To-do: add config_file as arguments
|
9
|
+
def initialize(config_file = nil)
|
10
|
+
@data = if config_file.nil?
|
11
|
+
YAML.load_file(File.absolute_path(DEFAULT_FILE))
|
12
|
+
else
|
13
|
+
YAML.load_file(File.absolute_path(config_file))
|
14
|
+
end
|
15
|
+
|
16
|
+
raise(TypeError, 'Malformed configuration') unless @data.is_a?(Hash)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Inferno
|
2
|
+
module DSL
|
3
|
+
module FHIREvaluation
|
4
|
+
module DatasetLoader
|
5
|
+
def self.from_contents(source_array)
|
6
|
+
dataset = []
|
7
|
+
|
8
|
+
source_array.each do |json|
|
9
|
+
resource = FHIR::Json.from_json(json)
|
10
|
+
next if resource.nil?
|
11
|
+
|
12
|
+
dataset.push resource
|
13
|
+
end
|
14
|
+
|
15
|
+
dataset
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from_path(path)
|
19
|
+
dataset = []
|
20
|
+
|
21
|
+
Dir["#{path}/*.json"].each do |f|
|
22
|
+
resource = FHIR::Json.from_json(File.read(f))
|
23
|
+
next if resource.nil?
|
24
|
+
|
25
|
+
dataset.push resource
|
26
|
+
end
|
27
|
+
|
28
|
+
dataset
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Inferno
|
2
|
+
module DSL
|
3
|
+
module FHIREvaluation
|
4
|
+
# EvaluationContext is a wrapper class around the concepts needed to perform an evaluation:
|
5
|
+
# - The IG used as the basis for evaluation
|
6
|
+
# - The data being evaluated
|
7
|
+
# - A summary/characterization of the data
|
8
|
+
# - Evaluation results
|
9
|
+
class EvaluationContext
|
10
|
+
attr_reader :ig, :data, :results, :config
|
11
|
+
|
12
|
+
def initialize(ig, data, config) # rubocop:disable Naming/MethodParameterName
|
13
|
+
@ig = ig
|
14
|
+
@data = data
|
15
|
+
@results = []
|
16
|
+
@config = config
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_result(result)
|
20
|
+
results.push result
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Inferno
|
2
|
+
module DSL
|
3
|
+
module FHIREvaluation
|
4
|
+
# The result of a Rule evaluating a data set.
|
5
|
+
class EvaluationResult
|
6
|
+
attr_accessor :message,
|
7
|
+
:severity, # fatal | error | warning | information | success
|
8
|
+
:issue_type, # https://www.hl7.org/fhir/valueset-issue-type.html
|
9
|
+
:threshold, # quantitative value that a rule checks for
|
10
|
+
:value, # actual observed value
|
11
|
+
:rule # Rule that produced this result
|
12
|
+
|
13
|
+
def initialize(message, severity: 'warning', issue_type: 'business-rule', threshold: nil, value: nil, rule: nil)
|
14
|
+
@message = message
|
15
|
+
@severity = severity
|
16
|
+
@issue_type = issue_type
|
17
|
+
@threshold = threshold
|
18
|
+
@value = value
|
19
|
+
@rule = rule
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"#{severity.upcase}: #{message}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_oo_issue
|
27
|
+
issue = {
|
28
|
+
severity:,
|
29
|
+
code: issue_type,
|
30
|
+
details: { text: message }
|
31
|
+
}
|
32
|
+
|
33
|
+
if threshold
|
34
|
+
issue[:extension] ||= []
|
35
|
+
issue[:extension].push({
|
36
|
+
# TODO: pick real extension for this
|
37
|
+
url: 'https://inferno-framework.github.io/fhir_evaluator/StructureDefinition/operationoutcome-issue-threshold',
|
38
|
+
valueDecimal: threshold
|
39
|
+
})
|
40
|
+
end
|
41
|
+
|
42
|
+
if value
|
43
|
+
issue[:extension] ||= []
|
44
|
+
issue[:extension].push({
|
45
|
+
# TODO: pick real extension for this
|
46
|
+
url: 'https://inferno-framework.github.io/fhir_evaluator/StructureDefinition/operationoutcome-issue-value',
|
47
|
+
valueDecimal: value
|
48
|
+
})
|
49
|
+
end
|
50
|
+
|
51
|
+
issue
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.to_operation_outcome(results)
|
55
|
+
FHIR::OperationOutcome.new({
|
56
|
+
issue: results.map(&:to_oo_issue)
|
57
|
+
})
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'config'
|
4
|
+
require_relative 'rule'
|
5
|
+
require_relative 'evaluation_context'
|
6
|
+
require_relative 'evaluation_result'
|
7
|
+
require_relative 'dataset_loader'
|
8
|
+
|
9
|
+
module Inferno
|
10
|
+
module DSL
|
11
|
+
module FHIREvaluation
|
12
|
+
class Evaluator
|
13
|
+
attr_accessor :ig
|
14
|
+
|
15
|
+
def initialize(ig) # rubocop:disable Naming/MethodParameterName
|
16
|
+
@ig = ig
|
17
|
+
end
|
18
|
+
|
19
|
+
def evaluate(data, config = Config.new)
|
20
|
+
context = EvaluationContext.new(@ig, data, config)
|
21
|
+
|
22
|
+
active_rules = []
|
23
|
+
config.data['Rule'].each do |rulename, rule_details|
|
24
|
+
active_rules << rulename if rule_details['Enabled']
|
25
|
+
end
|
26
|
+
|
27
|
+
Rule.descendants.each do |rule|
|
28
|
+
rule.new.check(context) if active_rules.include?(rule.name.demodulize)
|
29
|
+
end
|
30
|
+
|
31
|
+
context.results
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|