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

Commit

Permalink
Merge pull request #410 from onc-healthit/development
Browse files Browse the repository at this point in the history
Version 2.9.0
  • Loading branch information
Jammjammjamm authored Jan 3, 2020
2 parents 2e7775c + 5096942 commit aef309b
Show file tree
Hide file tree
Showing 198 changed files with 60,861 additions and 4,887 deletions.
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ cache: bundler
services:
- docker
before_install:
- gem update --system
- gem install bundler
- docker-compose build
before_script:
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ gem 'colorize'
gem 'data_mapper'
gem 'dm-sqlite-adapter'
gem 'fhir_client'
gem 'json-jwt'
gem 'jwt'
gem 'kramdown'
gem 'parser'
gem 'pry'
gem 'pry-byebug'
gem 'rake'
gem 'rb-readline'
gem 'rest-client'
gem 'rubyXL'
gem 'selenium-webdriver'
gem 'sinatra'
gem 'sinatra-contrib'
Expand Down
14 changes: 6 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ GEM
tzinfo (~> 1.1)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
aes_key_wrap (1.0.1)
ast (2.4.0)
backports (3.15.0)
base62-rb (0.3.1)
Expand All @@ -17,7 +16,6 @@ GEM
bcrypt (3.1.13)
bcrypt-ruby (3.1.5)
bcrypt (>= 3.1.3)
bindata (2.4.4)
bitarray (1.2.0)
bloomer (1.0.0)
bitarray
Expand Down Expand Up @@ -121,10 +119,6 @@ GEM
concurrent-ruby (~> 1.0)
jaro_winkler (1.5.3)
json (1.8.6)
json-jwt (1.11.0)
activesupport (>= 4.2)
aes_key_wrap
bindata
json_pure (1.8.6)
jwt (2.2.1)
kramdown (2.1.0)
Expand Down Expand Up @@ -158,7 +152,7 @@ GEM
byebug (~> 11.0)
pry (~> 0.10)
public_suffix (4.0.1)
rack (2.0.7)
rack (2.0.8)
rack-protection (2.0.7)
rack
rack-test (1.1.0)
Expand All @@ -179,6 +173,9 @@ GEM
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
ruby-progressbar (1.10.1)
rubyXL (3.4.9)
nokogiri (>= 1.4.4)
rubyzip (>= 1.3.0)
rubyzip (1.3.0)
safe_yaml (1.0.5)
selenium-webdriver (3.142.4)
Expand Down Expand Up @@ -235,7 +232,7 @@ DEPENDENCIES
data_mapper
dm-sqlite-adapter
fhir_client
json-jwt
jwt
kramdown
minitest
parser
Expand All @@ -246,6 +243,7 @@ DEPENDENCIES
rb-readline
rest-client
rubocop
rubyXL
selenium-webdriver
simplecov
sinatra
Expand Down
5 changes: 4 additions & 1 deletion config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,17 @@ include_extras: true

badge_text: Community

# Resource validator options: must be one of "internal" or "external". external_resource_validator_url is only used if resource_validator is set to external.
resource_validator: internal
external_resource_validator_url: http://validator_service:4567

# module options: one or more must be set. The first option in the list will be checked by default
modules:
- onc
- smart
- bdt
- argonaut
- uscore_v3.1.0
- bulk_data

# preset fhir servers: optional. Minimally requires name, uri, module, optional inferno_uri, client_id, client_secret, scopes, instructions link
presets:
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ services:
- bdt_service
bdt_service:
image: infernocommunity/inferno-bdt-service
validator_service:
image: infernocommunity/fhir-validator-wrapper
nginx_server:
image: nginx
volumes:
Expand Down
70 changes: 54 additions & 16 deletions generator/uscore/metadata_extractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ module Generator
module USCoreMetadataExtractor
PROFILE_URIS = Inferno::ValidationUtil::US_CORE_R4_URIS

def profile_uri(profile)
"http://hl7.org/fhir/us/core/StructureDefinition/#{profile}"
end

def search_param_path(resource, param)
param = 'id' if param == '_id'
"SearchParameter/us-core-#{resource.downcase}-#{param}"
Expand All @@ -27,6 +23,7 @@ def extract_metadata
capability_statement_json = capability_statement('server')
add_metadata_from_ig(metadata, ig_resource)
add_metadata_from_resources(metadata, capability_statement_json['rest'][0]['resource'])
fix_metadata_errors(metadata)
add_special_cases(metadata)
end

Expand All @@ -53,8 +50,16 @@ def generate_unique_test_id_prefix(title)
test_id_prefix
end

def get_base_path(profile)
if profile.include? 'us/core/'
profile.split('us/core/').last
else
profile.split('fhir/').last
end
end

def build_new_sequence(resource, profile)
base_path = profile.split('us/core/').last
base_path = get_base_path(profile)
base_name = profile.split('StructureDefinition/').last
profile_json = @resource_by_path[base_path]
reformatted_version = ig_resource['version'].delete('.')
Expand All @@ -64,15 +69,15 @@ def build_new_sequence(resource, profile)

# In case the profile doesn't start with US Core
class_name = "USCore#{reformatted_version}#{class_name}" unless class_name.start_with? 'USCore'

{
name: base_name.tr('-', '_'),
class_name: class_name,
test_id_prefix: test_id_prefix,
resource: resource['type'],
profile: profile_uri(base_name), # link in capability statement is incorrect,
profile: profile,
title: profile_title,
interactions: [],
operations: [],
searches: [],
search_param_descriptions: {},
element_descriptions: {},
Expand All @@ -94,10 +99,11 @@ def add_metadata_from_resources(metadata, resources)
add_basic_searches(resource, new_sequence)
add_combo_searches(resource, new_sequence)
add_interactions(resource, new_sequence)
add_operations(resource, new_sequence)
add_include_search(resource, new_sequence)
add_revinclude_targets(resource, new_sequence)

base_path = new_sequence[:profile].split('us/core/').last
base_path = get_base_path(supported_profile)
profile_definition = @resource_by_path[base_path]
add_must_support_elements(profile_definition, new_sequence)
add_search_param_descriptions(profile_definition, new_sequence)
Expand Down Expand Up @@ -152,6 +158,17 @@ def add_interactions(resource, sequence)
end
end

def add_operations(resource, sequence)
operations = resource['operation']
operations&.each do |operation|
new_operation = {
operation: operation['name'],
expectation: operation['extension'][0]['valueCode']
}
sequence[:operations] << new_operation
end
end

def add_include_search(resource, sequence)
sequence[:include_params] = resource['searchInclude'] || []
end
Expand Down Expand Up @@ -180,7 +197,7 @@ def add_must_support_elements(profile_definition, sequence)
sequence[:must_supports] <<
{
type: 'element',
path: path.gsub('[x]', type['code'].slice(0).capitalize + type['code'].slice(1..-1))
path: path.gsub('[x]', capitalize_first_letter(type['code']))
}
end
else
Expand All @@ -196,13 +213,10 @@ def add_must_support_elements(profile_definition, sequence)
def add_search_param_descriptions(profile_definition, sequence)
sequence[:search_param_descriptions].each_key do |param|
search_param_definition = @resource_by_path[search_param_path(sequence[:resource], param.to_s)]
path_parts = search_param_definition['xpath'].split('/f:')
if param.to_s != '_id'
path_parts[0] = sequence[:resource]
path = path_parts.join('.')
else
path = path_parts[0]
end
path = search_param_definition['expression']
path = path.gsub(/.where\((.*)/, '')
as_type = path.scan(/.as\((.*?)\)/).flatten.first
path = path.gsub(/.as\((.*?)\)/, capitalize_first_letter(as_type)) if as_type.present?
profile_element = profile_definition['snapshot']['element'].select { |el| el['id'] == path }.first
param_metadata = {
path: path,
Expand All @@ -224,6 +238,8 @@ def add_search_param_descriptions(profile_definition, sequence)
expectation = expectation_extension[index]['extension'].first['valueCode'] unless expectation_extension.nil?
param_metadata[:comparators][comparator.to_sym] = expectation
end
multiple_or_expectation = search_param_definition['_multipleOr']['extension'].first['valueCode']
param_metadata[:multiple_or] = multiple_or_expectation
sequence[:search_param_descriptions][param] = param_metadata
end
end
Expand Down Expand Up @@ -285,6 +301,24 @@ def add_element_definitions(profile_definition, sequence)
end
end

def fix_metadata_errors(metadata)
# Procedure's date search param definition says Procedure.occurenceDateTime even though Procedure doesn't have an occurenceDateTime
procedure_sequence = metadata[:sequences].find { |sequence| sequence[:resource] == 'Procedure' }
procedure_sequence[:search_param_descriptions][:date][:path] = 'Procedure.performed'

goal_sequence = metadata[:sequences].find { |sequence| sequence[:resource] == 'Goal' }
goal_sequence[:search_param_descriptions][:'target-date'][:path] = 'Goal.target.dueDate'
goal_sequence[:search_param_descriptions][:'target-date'][:type] = 'date'

# add the ge comparator - the metadata is missing it for some reason
metadata[:sequences].each do |sequence|
sequence[:search_param_descriptions].each do |_param, description|
param_comparators = description[:comparators]
param_comparators[:ge] = param_comparators[:le] if param_comparators.key? :le
end
end
end

def add_special_cases(metadata)
category_first_profiles = [
PROFILE_URIS[:lab_results]
Expand Down Expand Up @@ -320,6 +354,10 @@ def set_first_search(sequence, params)
sequence[:searches].delete(search)
sequence[:searches].unshift(search)
end

def capitalize_first_letter(str)
str.slice(0).capitalize + str.slice(1..-1)
end
end
end
end
13 changes: 5 additions & 8 deletions generator/uscore/templates/module.yml.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,18 @@ test_sets:
ad_hoc_testing:
view: default
tests:
- name: Discovery
- name: SMART App Launch
sequences:
- UsCoreR4CapabilityStatementSequence
- SMARTDiscoverySequence
run_all: true
- name: Authorization and Authentication
sequences:
- DynamicRegistrationSequence
- ManualRegistrationSequence
- StandaloneLaunchSequence
- EHRLaunchSequence
- name: US Core R4 Patient Based Profiles
- name: US Core v3.1.0 Profiles
run_all: true
sequences:<% non_delayed_sequences.each do |sequence| %>
sequences:
- UsCoreR4CapabilityStatementSequence
- USCore310PatientSequence<% non_delayed_sequences.each do |sequence| %>
- <%=sequence[:class_name]%><% end %>
- R4ProvenanceSequence
- USCoreR4ClinicalNotesSequence<% delayed_sequences.each do |sequence| %>
- <%=sequence[:class_name]%><% end %>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ describe 'unauthorized search test' do

it 'skips if the <%= resource_type %> search interaction is not supported' do
@instance.server_capabilities.destroy
Inferno::Models::ServerCapabilities.create(
testing_instance_id: @instance.id,
capabilities: FHIR::CapabilityStatement.new.to_json
)
@instance.reload
exception = assert_raises(Inferno::SkipException) { @sequence.run_test(@test) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ describe '<%= resource_type %> read test' do

it 'skips if the <%= resource_type %> read interaction is not supported' do
@instance.server_capabilities.destroy
Inferno::Models::ServerCapabilities.create(
testing_instance_id: @instance.id,
capabilities: FHIR::CapabilityStatement.new.to_json
)
@instance.reload
exception = assert_raises(Inferno::SkipException) { @sequence.run_test(@test) }

Expand Down
Loading

0 comments on commit aef309b

Please sign in to comment.