-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FI-3376 Migrate Evaluator CLI into inferno core CLI #557
Merged
Merged
Changes from 36 commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
cdc5dfb
Copied evaluator files
jhlee-mitre c8618cf
Rubocop
jhlee-mitre 18c0c06
Robocop
jhlee-mitre 58446f2
Rubocop temporarily addressed
jhlee-mitre 674983d
Merge branch 'main' into FI-3376-evaluator-firststep
jhlee-mitre 57b6e0c
Remove old comment
jhlee-mitre f363efc
Removed internal valiadator method
jhlee-mitre be8db9f
Leave out DataSummary
jhlee-mitre b73eaf3
Remove evaluator version
jhlee-mitre c7ddf37
Long description
jhlee-mitre 2a81a63
rubocop
jhlee-mitre 1d4662d
WIP
jhlee-mitre bd9aaa1
WIP
jhlee-mitre d1f2e83
Data set loader
jhlee-mitre e3b2d3a
IG path resolved
jhlee-mitre 2120d40
WIP
jhlee-mitre 7e35a76
WIP
jhlee-mitre 4a983f2
rubocop
jhlee-mitre 7f3d0e3
Rubocop
jhlee-mitre 5d1ef09
Update lib/inferno/dsl/fhir_evaluator/evaluator.rb
jhlee-mitre 9569ff9
Remove rule files
jhlee-mitre 936cafb
Remove data_summary
jhlee-mitre fd78251
WIP
jhlee-mitre 6b7bb8b
Clean up
jhlee-mitre e372fcb
config
jhlee-mitre fb239df
Rename evaluator
jhlee-mitre f5f76c8
Args
jhlee-mitre f1a95d9
Update lib/inferno/dsl/fhir_evaluator/evaluator.rb
jhlee-mitre 3314367
ig initialize, missing_method
jhlee-mitre 2c08f76
Merge branch 'main' into FI-3376-evaluator-firststep
jhlee-mitre 3d1feff
WIP
jhlee-mitre 34e46ed
the_ig
jhlee-mitre 58830ae
Disable rubocop for IG
jhlee-mitre a0fd36b
Remove print
jhlee-mitre a5dcc19
Folder file names
jhlee-mitre 013a42c
Removed config folder
jhlee-mitre 1758e64
rubocop:disable Naming/MethodParameterName
jhlee-mitre 796f383
Merge branch 'main' into FI-3376-evaluator-firststep
jhlee-mitre File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
require_relative '../../../inferno/dsl/fhir_evaluation/evaluator' | ||
|
||
module Inferno | ||
module CLI | ||
class Evaluate | ||
def run(ig_path, data_path, _log_level) | ||
validate_args(ig_path, data_path) | ||
|
||
# IG Import, rule execution, and result output below will be integrated at phase 2 and 3. | ||
|
||
# @ig = File.join(__dir__, 'ig', ig_path) | ||
# if data_path | ||
# DatasetLoader.from_path(File.join(__dir__, data_path)) | ||
# else | ||
# ig.examples | ||
# end | ||
|
||
# config = Config.new | ||
# evaluator = Inferno::DSL::FHIREvaluation::Evaluator.new(data, config) | ||
|
||
# results = evaluate() | ||
# output_results(results, options[:output]) | ||
end | ||
|
||
def validate_args(ig_path, data_path) | ||
raise 'A path to an IG is required!' unless ig_path | ||
|
||
return unless data_path && (!File.directory? data_path) | ||
|
||
raise "Provided path '#{data_path}' is not a directory" | ||
end | ||
|
||
def output_results(results, output) | ||
if output&.end_with?('json') | ||
oo = FhirEvaluator::EvaluationResult.to_operation_outcome(results) | ||
File.write(output, oo.to_json) | ||
puts "Results written to #{output}" | ||
else | ||
counts = results.group_by(&:severity).transform_values(&:count) | ||
print(counts, 'Result Count') | ||
puts "\n" | ||
puts results | ||
end | ||
end | ||
|
||
def print(output_fields, title) | ||
puts("╔══════════════ #{title} ═══════════════╗") | ||
puts('║ ╭────────────────┬──────────────────────╮ ║') | ||
output_fields.each_with_index do |(key, value), i| | ||
field_name = pad(key, 14) | ||
field_value = pad(value.to_s, 20) | ||
puts("║ │ #{field_name} │ #{field_value} │ ║") | ||
puts('║ ├────────────────┼──────────────────────┤ ║') unless i == output_fields.length - 1 | ||
end | ||
puts('║ ╰────────────────┴──────────────────────╯ ║') | ||
puts('╚═══════════════════════════════════════════╝') | ||
end | ||
|
||
def pad(string, length) | ||
format("%#{length}.#{length}s", string) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
module Inferno | ||
module DSL | ||
module FHIREvaluation | ||
class Config | ||
DEFAULT_FILE = File.join(__dir__, 'default.yml') | ||
attr_accessor :data | ||
|
||
# To-do: add config_file as arguments | ||
def initialize(config_file = nil) | ||
@data = if config_file.nil? | ||
YAML.load_file(File.absolute_path(DEFAULT_FILE)) | ||
else | ||
YAML.load_file(File.absolute_path(config_file)) | ||
end | ||
|
||
raise(TypeError, 'Malformed configuration') unless @data.is_a?(Hash) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
module Inferno | ||
module DSL | ||
module FHIREvaluation | ||
module DatasetLoader | ||
def self.from_contents(source_array) | ||
dataset = [] | ||
|
||
source_array.each do |json| | ||
resource = FHIR::Json.from_json(json) | ||
next if resource.nil? | ||
|
||
dataset.push resource | ||
end | ||
|
||
dataset | ||
end | ||
|
||
def self.from_path(path) | ||
dataset = [] | ||
|
||
Dir["#{path}/*.json"].each do |f| | ||
resource = FHIR::Json.from_json(File.read(f)) | ||
next if resource.nil? | ||
|
||
dataset.push resource | ||
end | ||
|
||
dataset | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
Environment: | ||
ExternalValidator: | ||
Enabled: false | ||
Url: '' | ||
VSAC: | ||
Apikey: '' | ||
Url: 'https://cts.nlm.nih.gov' | ||
|
||
Rule: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
module Inferno | ||
module DSL | ||
module FHIREvaluation | ||
# EvaluationContext is a wrapper class around the concepts needed to perform an evaluation: | ||
# - The IG used as the basis for evaluation | ||
# - The data being evaluated | ||
# - A summary/characterization of the data | ||
# - Evaluation results | ||
class EvaluationContext | ||
attr_reader :ig, :data, :results, :config | ||
|
||
# rubocop:disable Naming/MethodParameterName | ||
def initialize(ig, data, config) | ||
@ig = ig | ||
@data = data | ||
@results = [] | ||
@config = config | ||
end | ||
# rubocop:enable Naming/MethodParameterName | ||
|
||
def add_result(result) | ||
results.push result | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
module Inferno | ||
module DSL | ||
module FHIREvaluation | ||
# The result of a Rule evaluating a data set. | ||
class EvaluationResult | ||
attr_accessor :message, | ||
:severity, # fatal | error | warning | information | success | ||
:issue_type, # https://www.hl7.org/fhir/valueset-issue-type.html | ||
:threshold, # quantitative value that a rule checks for | ||
:value, # actual observed value | ||
:rule # Rule that produced this result | ||
|
||
def initialize(message, severity: 'warning', issue_type: 'business-rule', threshold: nil, value: nil, rule: nil) | ||
@message = message | ||
@severity = severity | ||
@issue_type = issue_type | ||
@threshold = threshold | ||
@value = value | ||
@rule = rule | ||
end | ||
|
||
def to_s | ||
"#{severity.upcase}: #{message}" | ||
end | ||
|
||
def to_oo_issue | ||
issue = { | ||
severity:, | ||
code: issue_type, | ||
details: { text: message } | ||
} | ||
|
||
if threshold | ||
issue[:extension] ||= [] | ||
issue[:extension].push({ | ||
# TODO: pick real extension for this | ||
url: 'https://inferno-framework.github.io/fhir_evaluator/StructureDefinition/operationoutcome-issue-threshold', | ||
valueDecimal: threshold | ||
}) | ||
end | ||
|
||
if value | ||
issue[:extension] ||= [] | ||
issue[:extension].push({ | ||
# TODO: pick real extension for this | ||
url: 'https://inferno-framework.github.io/fhir_evaluator/StructureDefinition/operationoutcome-issue-value', | ||
valueDecimal: value | ||
}) | ||
end | ||
|
||
issue | ||
end | ||
|
||
def self.to_operation_outcome(results) | ||
FHIR::OperationOutcome.new({ | ||
issue: results.map(&:to_oo_issue) | ||
}) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative 'config' | ||
require_relative 'rule' | ||
require_relative 'evaluation_context' | ||
require_relative 'evaluation_result' | ||
require_relative 'dataset_loader' | ||
|
||
module Inferno | ||
module DSL | ||
module FHIREvaluation | ||
class Evaluator | ||
attr_accessor :ig | ||
|
||
# rubocop:disable Naming/MethodParameterName | ||
def initialize(ig) | ||
@ig = ig | ||
end | ||
# rubocop:enable Naming/MethodParameterName | ||
|
||
def evaluate(data, config = Config.new) | ||
context = EvaluationContext.new(@ig, data, config) | ||
|
||
active_rules = [] | ||
config.data['Rule'].each do |rulename, rule_details| | ||
active_rules << rulename if rule_details['Enabled'] | ||
end | ||
|
||
Rule.descendants.each do |rule| | ||
rule.new.check(context) if active_rules.include?(rule.name.demodulize) | ||
end | ||
|
||
context.results | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# frozen_string_literal: true | ||
|
||
module Inferno | ||
module DSL | ||
module FHIREvaluation | ||
class Rule | ||
def check(_context) | ||
raise 'not implemented' | ||
end | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using a disable and an enable, just put the disable at the end of the relevant line, and then it will only affect that line and you don't have to reenable the rule.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the tips. Modified.