Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from fhir-crucible/profile_validations
Browse files Browse the repository at this point in the history
Profile validations
  • Loading branch information
jawalonoski authored Apr 25, 2017
2 parents 3eb1c3b + dd8d0bd commit 38252fb
Show file tree
Hide file tree
Showing 611 changed files with 354,888 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
language: ruby
rvm:
- "2.0.0"
- "2.3.3"
- "2.4.0"
script:
- bundle exec rake test
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ gemspec
gem 'rake'
gem 'pry'

# gem 'fhir_models', :path => '../fhir_models'
# gem 'fhir_models', :git => 'https://github.com/fhir-crucible/fhir_models.git', :branch => 'master'

group :test do
gem 'simplecov', :require => false
gem 'minitest', '~> 5.3'
Expand Down
22 changes: 11 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
fhir_scorecard (1.8.0)
fhir_scorecard (1.8.1)
fhir_models (~> 1.8)

GEM
Expand All @@ -11,35 +11,35 @@ GEM
awesome_print (1.7.0)
bcp47 (0.3.3)
i18n
builder (3.2.2)
builder (3.2.3)
coderay (1.1.1)
date_time_precision (0.8.1)
docile (1.1.5)
fhir_models (1.8.1)
fhir_models (1.8.3)
bcp47 (>= 0.3)
date_time_precision (>= 0.8)
mime-types (>= 1.16, < 3)
nokogiri (>= 1.6)
i18n (0.7.0)
json (2.0.2)
i18n (0.8.1)
json (2.0.3)
method_source (0.8.2)
mime-types (2.99.3)
mini_portile2 (2.1.0)
minitest (5.9.1)
minitest-reporters (1.1.12)
minitest (5.10.1)
minitest-reporters (1.1.14)
ansi
builder
minitest (>= 5.0)
ruby-progressbar
nokogiri (1.6.8.1)
nokogiri (1.7.1)
mini_portile2 (~> 2.1.0)
pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rake (11.3.0)
rake (12.0.0)
ruby-progressbar (1.8.1)
simplecov (0.12.0)
simplecov (0.14.1)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
Expand All @@ -59,4 +59,4 @@ DEPENDENCIES
simplecov

BUNDLED WITH
1.13.6
1.14.6
2 changes: 1 addition & 1 deletion fhir_scorecard.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Gem::Specification.new do |s|
s.email = "[email protected]"
s.homepage = "https://github.com/fhir-crucible/fhir_scorecard"
s.authors = ["Jason Walonoski"]
s.version = '1.8.0'
s.version = '1.8.1'

s.files = s.files = `git ls-files`.split("\n")

Expand Down
1 change: 1 addition & 0 deletions lib/fhir_scorecard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

require File.join(root,'lib','scorecard.rb')
require File.join(root,'lib','terminology.rb')
require File.join(root,'lib','implementation_guides.rb')
require File.join(root,'lib','rubrics.rb')

Dir.glob(File.join(root, 'lib','rubrics','**','*.rb')).each do |file|
Expand Down
184 changes: 184 additions & 0 deletions lib/implementation_guides.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
module FHIR
class ImplementationGuideLookup

@@resources = File.expand_path './resources', File.dirname(File.absolute_path(__FILE__))
@@loaded = false

@@us_core = {}
@@standard_health_record = {}
@@shr_indicators = {}

@@shr_defaults = {
'Practitioner' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-actor-Practitioner',
'Organization' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-actor-Organization',
'RelatedPerson' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-actor-RelatedPerson',
'AllergyIntolerance' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-allergy-AllergyIntolerance',
'BodySite' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-core-BodySite',
'Patient' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-demographics-PersonOfRecord',
'Coverage' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-demographics-HealthInsurance',
'Device' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-device-Device',
'DeviceUseStatement' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-device-DeviceUse',
'Encounter' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-encounter-Encounter',
'Immunization' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-immunization-Immunization',
'Medication' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-medication-Medication',
'MedicationStatement' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-medication-MedicationUse',
'MedicationRequest' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-medication-MedicationPrescription',
'DiagnosticReport' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-observation-Panel',
'Observation' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-observation-Observation',
'Condition' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-problem-Problem',
'Procedure' => 'http://standardhealthrecord.org/fhir/StructureDefinition/shr-procedure-Procedure'
}

def self.guess_shr_profile(resource)
load_guides
if resource && !@@shr_indicators.nil? && !@@shr_indicators.empty?
# if the profile is given, we don't need to guess
if resource.meta && resource.meta.profile && !resource.meta.profile.empty?
resource.meta.profile.each do |uri|
return @@standard_health_record[uri] if @@standard_health_record[uri]
end
end
# if the profile is not named, then we need to guess using some heuristic indicators
@@shr_indicators.each do |url, data|
if data['resource'] == resource.resourceType
indicators = true
resource_hash = resource.to_hash
data['fixedValues'].each do |requirement|
# indicators = false if requirement not met
steps = requirement['path'].split('.')
steps.delete(data['resource'])
actual_values = select(steps, resource_hash)
expected_value = requirement['value']
if actual_values.nil? || (actual_values.is_a?(Array) && actual_values.empty?)
indicators = false
elsif actual_values.is_a?(Array)
actual_values.each do |onevalue|
if onevalue.is_a?(Hash)
if onevalue['coding']
actual_code = onevalue['coding'].map{|x|x['code']}
expected_code = expected_value['coding'].map{|x|x['code']}
any_match = false
actual_code.each do |x|
any_match = true if expected_code.include?(x)
end
indicators = false unless any_match
else
expected_value.each do |key, value|
indicators = false unless onevalue[key] == value
end
end
end
end
elsif actual_values.is_a?(Hash)
if actual_values['coding']
actual_code = actual_values['coding'].map{|x|x['code']}
expected_code = expected_value['coding'].map{|x|x['code']}
any_match = false
actual_code.each do |x|
any_match = true if expected_code.include?(x)
end
indicators = false unless any_match
else
expected_value.each do |key, value|
indicators = false unless actual_values[key] == value
end
end
elsif actual_values.is_a?(TrueClass) || actual_values.is_a?(FalseClass)
indicators = false unless actual_values == expected_value
end
end
return @@standard_health_record[uri] if indicators
end
end
# if the heuristics do not help, use a default if possible
default = @@shr_defaults[resource.resourceType]
return @@standard_health_record[default] if default
end
nil
end

def self.select(steps=[], obj)
return obj if steps.nil? || steps.empty?
copysteps = steps.clone
if obj.is_a?(Hash)
step = copysteps.delete_at(0)
obj = obj[step]
select(copysteps, obj)
elsif obj.is_a?(Array)
obj.map do |x|
select(copysteps, x)
end
end
end

def self.guess_uscore_profile(resource)
load_guides
if resource
# if the profile is given, we don't need to guess
if resource.meta && resource.meta.profile && !resource.meta.profile.empty?
resource.meta.profile.each do |uri|
return @@us_core[uri] if @@us_core[uri]
end
end
# TODO consider VitalSigns/Observations
# Otherwise, guess the first profile that matches on resource type
@@us_core.each do |url, thing|
if thing.is_a?(FHIR::StructureDefinition)
return thing if thing.type == resource.resourceType
end
end
end
nil
end

def self.lookup(url)
load_guides
@@us_core[url] || @@standard_health_record[url] || nil
end

def self.load_guides
if !@@loaded
begin
# load all the FHIR resources in the US Core Implementation Guide
Dir.glob(File.join(@@resources,'us_core','**','*.json')).each do |filename|
raw = File.open(filename,'r:UTF-8',&:read)
resource = FHIR.from_contents(raw)
if resource
@@us_core[resource.id] = resource if resource.id
@@us_core[resource.url] = resource if resource.url
end
end
rescue => error
FHIR.logger.error error
end

begin
# load all the FHIR resources in the Standard Health Record Implementation Guide
Dir.glob(File.join(@@resources,'standard_health_record','**','*.json')).each do |filename|
raw = File.open(filename,'r:UTF-8',&:read)
resource = FHIR.from_contents(raw)
if resource
@@standard_health_record[resource.id] = resource if resource.id
@@standard_health_record[resource.url] = resource if resource.url
end
end
rescue => error
FHIR.logger.error error
end

begin
# load some indicators to help guess which SHR profile to use
filename = File.join(@@resources,'profile_indicators.json')
raw = File.open(filename,'r:UTF-8',&:read)
@@shr_indicators = JSON.parse(raw)
rescue => error
@@shr_indicators = {}
FHIR.logger.error error
end

@@loaded = true
end
end

end
end
Loading

0 comments on commit 38252fb

Please sign in to comment.