auscvdrisk_inferno 0.1.0
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 +7 -0
- data/lib/auscvdrisk/context.rb +195 -0
- data/lib/auscvdrisk/helpers.rb +125 -0
- data/lib/auscvdrisk/igs/.keep +0 -0
- data/lib/auscvdrisk/igs/README.md +21 -0
- data/lib/auscvdrisk/igs/auscvdrisk-Iteration3.tgz +0 -0
- data/lib/auscvdrisk/igs/put_ig_package_dot_tgz_here +0 -0
- data/lib/auscvdrisk/metadata.rb +18 -0
- data/lib/auscvdrisk/prepop/demographics.rb +58 -0
- data/lib/auscvdrisk/prepop/diagnoses.rb +281 -0
- data/lib/auscvdrisk/prepop/medications.rb +372 -0
- data/lib/auscvdrisk/prepop/observations.rb +433 -0
- data/lib/auscvdrisk/suite.rb +57 -0
- data/lib/auscvdrisk/version.rb +3 -0
- data/lib/auscvdrisk/write_back.rb +89 -0
- data/lib/auscvdrisk.rb +1 -0
- metadata +65 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bcbe60d262e2e5db84dbd5d0c5dd5d89ecbe1a80ce5a30db952f7c1c96c60a29
|
|
4
|
+
data.tar.gz: 9938beb41fa23bf852e852f31d7015506b17b9a559853a01396b00357863c0f7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: bef956f3427cef93e9b595018a7d74e028d27705dd7e6aec017769fc75d0ed49c9806cb2ef3dd2fe7f6aa67179451546cdf0af32b1edadd51deb76e44c112539
|
|
7
|
+
data.tar.gz: 06adbbce609d183edb91bd6e2d450f34f86b80b6178f5746324f21bff229ffea58a830c0246188fcf40fc55507433b9acd65eac48a18dfeb85f460e0977622f6
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require_relative 'helpers'
|
|
2
|
+
|
|
3
|
+
module AusCVDRisk
|
|
4
|
+
class Context < Inferno::TestGroup
|
|
5
|
+
include AusCVDRisk::Helpers
|
|
6
|
+
|
|
7
|
+
title 'Context'
|
|
8
|
+
id :context
|
|
9
|
+
|
|
10
|
+
test do
|
|
11
|
+
title 'Server declares support for reading Patient resources'
|
|
12
|
+
|
|
13
|
+
run do
|
|
14
|
+
fhir_get_capability_statement
|
|
15
|
+
|
|
16
|
+
assert_response_status(200)
|
|
17
|
+
assert_resource_type(:capability_statement)
|
|
18
|
+
capability_statement = resource
|
|
19
|
+
|
|
20
|
+
resource_type = 'Patient'
|
|
21
|
+
interaction = 'read'
|
|
22
|
+
|
|
23
|
+
rest_component = capability_statement.rest&.find { |r| r.mode == 'server' }
|
|
24
|
+
assert rest_component.present?, 'CapabilityStatement does not have a REST component with mode "server"'
|
|
25
|
+
|
|
26
|
+
resource_component = rest_component.resource&.find { |r| r.type == resource_type }
|
|
27
|
+
assert resource_component.present?, "CapabilityStatement does not declare support for #{resource_type} resource"
|
|
28
|
+
|
|
29
|
+
has_interaction = resource_component.interaction&.any? { |i| i.code == interaction }
|
|
30
|
+
assert has_interaction, "CapabilityStatement does not declare support for #{interaction} interaction on #{resource_type} resource"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test do
|
|
35
|
+
title 'Server declares support for reading Practitioner resources'
|
|
36
|
+
|
|
37
|
+
run do
|
|
38
|
+
fhir_get_capability_statement
|
|
39
|
+
|
|
40
|
+
assert_response_status(200)
|
|
41
|
+
assert_resource_type(:capability_statement)
|
|
42
|
+
capability_statement = resource
|
|
43
|
+
|
|
44
|
+
resource_type = 'Practitioner'
|
|
45
|
+
interaction = 'read'
|
|
46
|
+
|
|
47
|
+
rest_component = capability_statement.rest&.find { |r| r.mode == 'server' }
|
|
48
|
+
assert rest_component.present?, 'CapabilityStatement does not have a REST component with mode "server"'
|
|
49
|
+
|
|
50
|
+
resource_component = rest_component.resource&.find { |r| r.type == resource_type }
|
|
51
|
+
assert resource_component.present?, "CapabilityStatement does not declare support for #{resource_type} resource"
|
|
52
|
+
|
|
53
|
+
has_interaction = resource_component.interaction&.any? { |i| i.code == interaction }
|
|
54
|
+
assert has_interaction, "CapabilityStatement does not declare support for #{interaction} interaction on #{resource_type} resource"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
test do
|
|
59
|
+
title 'Server declares support for AU Core Patient profile'
|
|
60
|
+
|
|
61
|
+
run do
|
|
62
|
+
fhir_get_capability_statement
|
|
63
|
+
|
|
64
|
+
assert_response_status(200)
|
|
65
|
+
assert_resource_type(:capability_statement)
|
|
66
|
+
capability_statement = resource
|
|
67
|
+
|
|
68
|
+
resource_type = 'Patient'
|
|
69
|
+
profile_url = 'http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient'
|
|
70
|
+
|
|
71
|
+
has_profile = check_capability_statement_for_profile(capability_statement, resource_type, profile_url)
|
|
72
|
+
|
|
73
|
+
assert has_profile, "CapabilityStatement does not declare support for AU Core Patient profile"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
test do
|
|
78
|
+
title 'Server declares support for AU Core Practitioner profile'
|
|
79
|
+
|
|
80
|
+
run do
|
|
81
|
+
fhir_get_capability_statement
|
|
82
|
+
|
|
83
|
+
assert_response_status(200)
|
|
84
|
+
assert_resource_type(:capability_statement)
|
|
85
|
+
capability_statement = resource
|
|
86
|
+
|
|
87
|
+
resource_type = 'Practitioner'
|
|
88
|
+
profile_url = 'http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitioner'
|
|
89
|
+
|
|
90
|
+
has_profile = check_capability_statement_for_profile(capability_statement, resource_type, profile_url)
|
|
91
|
+
|
|
92
|
+
assert has_profile, "CapabilityStatement does not declare support for AU Core Practitioner profile"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
test do
|
|
97
|
+
title 'Test Patient conforms to AU Core profile'
|
|
98
|
+
|
|
99
|
+
input :patient_id,
|
|
100
|
+
title: 'Patient ID',
|
|
101
|
+
description: 'ID of a patient resource on the server'
|
|
102
|
+
|
|
103
|
+
run do
|
|
104
|
+
skip_if patient_id.blank?, 'No patient ID provided'
|
|
105
|
+
|
|
106
|
+
fhir_read(:patient, patient_id)
|
|
107
|
+
|
|
108
|
+
assert_response_status(200)
|
|
109
|
+
assert_resource_type(:patient)
|
|
110
|
+
|
|
111
|
+
assert_valid_resource(profile_url: 'https://www.cvdcheck.org.au/fhir/StructureDefinition/CVDRiskPatient')
|
|
112
|
+
assert_valid_resource(profile_url: 'http://hl7.org.au/fhir/core/StructureDefinition/au-core-patient')
|
|
113
|
+
|
|
114
|
+
pass 'Test Patient has no validation errors'
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
test do
|
|
119
|
+
title 'Test Practitioner conforms to AU Core profile'
|
|
120
|
+
|
|
121
|
+
input :practitioner_id,
|
|
122
|
+
title: 'Practitioner ID',
|
|
123
|
+
description: 'ID of a practitioner resource on the server'
|
|
124
|
+
|
|
125
|
+
run do
|
|
126
|
+
skip_if practitioner_id.blank?, 'No practitioner ID provided'
|
|
127
|
+
|
|
128
|
+
fhir_read(:practitioner, practitioner_id)
|
|
129
|
+
|
|
130
|
+
assert_response_status(200)
|
|
131
|
+
assert_resource_type(:practitioner)
|
|
132
|
+
|
|
133
|
+
assert_valid_resource(profile_url: 'http://hl7.org.au/fhir/core/StructureDefinition/au-core-practitioner')
|
|
134
|
+
|
|
135
|
+
pass 'Test Practitioner has no validation errors'
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
test do
|
|
140
|
+
optional
|
|
141
|
+
title 'Server declares support for reading Encounter resources'
|
|
142
|
+
|
|
143
|
+
run do
|
|
144
|
+
fhir_get_capability_statement
|
|
145
|
+
|
|
146
|
+
assert_response_status(200)
|
|
147
|
+
assert_resource_type(:capability_statement)
|
|
148
|
+
capability_statement = resource
|
|
149
|
+
|
|
150
|
+
resource_type = 'Encounter'
|
|
151
|
+
interaction = 'read'
|
|
152
|
+
|
|
153
|
+
rest_component = capability_statement.rest&.find { |r| r.mode == 'server' }
|
|
154
|
+
assert rest_component.present?, 'CapabilityStatement does not have a REST component with mode "server"'
|
|
155
|
+
|
|
156
|
+
resource_component = rest_component.resource&.find { |r| r.type == resource_type }
|
|
157
|
+
|
|
158
|
+
if resource_component.present?
|
|
159
|
+
has_interaction = resource_component.interaction&.any? { |i| i.code == interaction }
|
|
160
|
+
|
|
161
|
+
if has_interaction
|
|
162
|
+
pass "Server declares support for reading Encounter resources in its CapabilityStatement"
|
|
163
|
+
else
|
|
164
|
+
warning "CapabilityStatement declares support for Encounter resource but not the read interaction"
|
|
165
|
+
end
|
|
166
|
+
else
|
|
167
|
+
warning "CapabilityStatement does not declare support for Encounter resource, but this is a SHOULD requirement"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
test do
|
|
173
|
+
optional
|
|
174
|
+
title 'Test Encounter conforms to AU Core profile'
|
|
175
|
+
|
|
176
|
+
input :encounter_id,
|
|
177
|
+
title: 'Encounter ID',
|
|
178
|
+
description: 'ID of an encounter resource on the server',
|
|
179
|
+
optional: true
|
|
180
|
+
|
|
181
|
+
run do
|
|
182
|
+
assert encounter_id.present?, 'No encounter ID provided'
|
|
183
|
+
|
|
184
|
+
fhir_read(:encounter, encounter_id)
|
|
185
|
+
|
|
186
|
+
assert_response_status(200)
|
|
187
|
+
assert_resource_type(:encounter)
|
|
188
|
+
|
|
189
|
+
assert_valid_resource(profile_url: 'http://hl7.org.au/fhir/core/StructureDefinition/au-core-encounter')
|
|
190
|
+
|
|
191
|
+
pass 'Test Encounter has no validation errors'
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
module AusCVDRisk
|
|
2
|
+
module Helpers
|
|
3
|
+
def fhir_get_capability_statement
|
|
4
|
+
fhir_client.capabilities.resource
|
|
5
|
+
@resource = fhir_client.reply.resource
|
|
6
|
+
@response = fhir_client.reply.response
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def check_capability_statement_for_profile(capability_statement, resource_type, profile_url)
|
|
10
|
+
rest_component = capability_statement.rest&.find { |r| r.mode == 'server' }
|
|
11
|
+
return false unless rest_component.present?
|
|
12
|
+
|
|
13
|
+
resource_component = rest_component.resource&.find { |r| r.type == resource_type }
|
|
14
|
+
return false unless resource_component.present?
|
|
15
|
+
|
|
16
|
+
# Check if the profile is supported
|
|
17
|
+
has_profile = resource_component.supportedProfile&.any? { |p| p.include?(profile_url) } ||
|
|
18
|
+
resource_component.profile&.include?(profile_url)
|
|
19
|
+
|
|
20
|
+
has_profile
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def check_search_parameter_support(capability_statement, resource_type, parameter_name)
|
|
24
|
+
rest_component = capability_statement.rest&.find { |r| r.mode == 'server' }
|
|
25
|
+
return false unless rest_component.present?
|
|
26
|
+
|
|
27
|
+
resource_component = rest_component.resource&.find { |r| r.type == resource_type }
|
|
28
|
+
return false unless resource_component.present?
|
|
29
|
+
|
|
30
|
+
# Check if the search parameter is supported
|
|
31
|
+
has_parameter = resource_component.searchParam&.any? { |p| p.name == parameter_name }
|
|
32
|
+
|
|
33
|
+
has_parameter
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def check_global_search_parameter_support(capability_statement, parameter_name)
|
|
37
|
+
rest_component = capability_statement.rest&.find { |r| r.mode == 'server' }
|
|
38
|
+
return false unless rest_component.present?
|
|
39
|
+
|
|
40
|
+
# Check if the search parameter is supported
|
|
41
|
+
has_parameter = rest_component.searchParam&.any? { |p| p.name == parameter_name }
|
|
42
|
+
|
|
43
|
+
has_parameter
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def check_search_combination_support(capability_statement, resource_type, parameter_names)
|
|
47
|
+
rest_component = capability_statement.rest&.find { |r| r.mode == 'server' }
|
|
48
|
+
return false unless rest_component.present?
|
|
49
|
+
|
|
50
|
+
resource_component = rest_component.resource&.find { |r| r.type == resource_type }
|
|
51
|
+
return false unless resource_component.present?
|
|
52
|
+
|
|
53
|
+
# Check if all individual parameters are supported
|
|
54
|
+
all_params_supported = parameter_names.all? do |param|
|
|
55
|
+
resource_component.searchParam&.any? { |p| p.name == param }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Check if the combination is explicitly supported
|
|
59
|
+
combination_supported = resource_component.searchInclude&.any? do |combo|
|
|
60
|
+
combo == parameter_names.join('+')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
all_params_supported && combination_supported
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def check_include_support(capability_statement, resource_type, include_param)
|
|
67
|
+
rest_component = capability_statement.rest&.find { |r| r.mode == 'server' }
|
|
68
|
+
return false unless rest_component.present?
|
|
69
|
+
|
|
70
|
+
resource_component = rest_component.resource&.find { |r| r.type == resource_type }
|
|
71
|
+
return false unless resource_component.present?
|
|
72
|
+
|
|
73
|
+
# Check if the _include parameter is supported
|
|
74
|
+
has_include = resource_component.searchInclude&.any? { |p| p == include_param }
|
|
75
|
+
|
|
76
|
+
has_include
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Verify that all search parameters in the original query are present in the self link
|
|
80
|
+
def verify_search_parameters_in_self_link(bundle, original_params)
|
|
81
|
+
assert bundle.key?('link'), 'No link found in the search results bundle'
|
|
82
|
+
self_link = bundle['link'].find { |link| link['relation'] == 'self' }
|
|
83
|
+
assert self_link.present?, 'Self link not found in the search results bundle'
|
|
84
|
+
|
|
85
|
+
# Extract the query part from the self link URL
|
|
86
|
+
query_string = self_link['url'].split('?').last
|
|
87
|
+
|
|
88
|
+
# Parse the original parameters and the self link parameters
|
|
89
|
+
original_params_hash = parse_query_string(original_params)
|
|
90
|
+
self_link_params_hash = parse_query_string(query_string)
|
|
91
|
+
|
|
92
|
+
# Check that all original parameters are in the self link
|
|
93
|
+
original_params_hash.each do |key, value|
|
|
94
|
+
return false unless self_link_params_hash.key?(key)
|
|
95
|
+
|
|
96
|
+
# For parameters that can have multiple values (comma-separated)
|
|
97
|
+
if value.include?(',')
|
|
98
|
+
original_values = value.split(',').map(&:strip)
|
|
99
|
+
self_values = if self_link_params_hash[key].include?(',')
|
|
100
|
+
self_link_params_hash[key].split(',').map(&:strip)
|
|
101
|
+
else
|
|
102
|
+
[self_link_params_hash[key]]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
original_values.each do |val|
|
|
106
|
+
return false unless self_values.include?(val)
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
return false unless value == self_link_params_hash[key]
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
true
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Parse a query string into a hash of parameters
|
|
116
|
+
def parse_query_string(query_string)
|
|
117
|
+
result = {}
|
|
118
|
+
query_string.split('&').each do |param|
|
|
119
|
+
key, value = param.split('=')
|
|
120
|
+
result[key] = URI.decode_www_form_component(value) if key && value
|
|
121
|
+
end
|
|
122
|
+
result
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Note on this IGs folder
|
|
2
|
+
|
|
3
|
+
There are three reasons why it would be necessary to put an IG package.tgz in this folder. If none of these apply, you do not need to put any files here, or can consider removing any existing files to make it clear they are unused.
|
|
4
|
+
|
|
5
|
+
## 1. Generated Test Suites
|
|
6
|
+
Some test kits use a "generator" to automatically generate the contents of a test suite for an IG. The IG files are required every time the test suites need to be regenerated. Examples of test kits that use this approach are the US Core Test Kit and CARIN IG for Blue Button® Test Kit.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## 2. Non-published IG
|
|
10
|
+
If your IG, or the specific version of the IG you want to test against, is not published, then the validator service needs to load the IG from file in order to be able to validate resources with it. The IG must be referenced in the `fhir_resource_validator` block in the test suite definition by filename, ie:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
fhir_resource_validator do
|
|
14
|
+
igs 'igs/filename.tgz'
|
|
15
|
+
|
|
16
|
+
...
|
|
17
|
+
end
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 3. Inferno Validator UI
|
|
21
|
+
The Inferno Validator UI is configured to auto-load any IGs present in the igs folder and include them in all validations. In general, the Inferno team is currently leaving IGs in this folder even if not otherwise necessary to make it easy to re-enable the validator UI.
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require_relative 'version'
|
|
2
|
+
|
|
3
|
+
module AusCVDRisk
|
|
4
|
+
class Metadata < Inferno::TestKit
|
|
5
|
+
id :auscvdrisk
|
|
6
|
+
title 'Aus CVD Risk-i Test Kit'
|
|
7
|
+
description <<~DESCRIPTION
|
|
8
|
+
Provides tests to aid in the integration with the Aus CVD Risk-i Calculator application.
|
|
9
|
+
DESCRIPTION
|
|
10
|
+
suite_ids [:auscvdrisk]
|
|
11
|
+
# tags ['SMART App Launch', 'US Core']
|
|
12
|
+
# last_updated '2024-03-07'
|
|
13
|
+
version VERSION
|
|
14
|
+
maturity 'Low'
|
|
15
|
+
authors ['Australian e-Health Research Centre, CSIRO']
|
|
16
|
+
# repo 'TODO'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require_relative '../helpers'
|
|
2
|
+
|
|
3
|
+
module AusCVDRisk
|
|
4
|
+
module PrePopulation
|
|
5
|
+
class Demographics < Inferno::TestGroup
|
|
6
|
+
include AusCVDRisk::Helpers
|
|
7
|
+
|
|
8
|
+
title 'Pre-population: Demographics'
|
|
9
|
+
id :prepop_demographics
|
|
10
|
+
|
|
11
|
+
test do
|
|
12
|
+
optional
|
|
13
|
+
title 'Server declares support for CVD Risk Patient profile'
|
|
14
|
+
|
|
15
|
+
run do
|
|
16
|
+
fhir_get_capability_statement
|
|
17
|
+
|
|
18
|
+
assert_response_status(200)
|
|
19
|
+
assert_resource_type(:capability_statement)
|
|
20
|
+
capability_statement = resource
|
|
21
|
+
|
|
22
|
+
resource_type = 'Patient'
|
|
23
|
+
profile_url = 'https://www.cvdcheck.org.au/fhir/StructureDefinition/CVDRiskPatient'
|
|
24
|
+
|
|
25
|
+
has_profile = check_capability_statement_for_profile(capability_statement, resource_type, profile_url)
|
|
26
|
+
|
|
27
|
+
if has_profile
|
|
28
|
+
pass "Server declares support for CVD Risk Patient profile"
|
|
29
|
+
else
|
|
30
|
+
assert false, "CapabilityStatement does not declare support for CVD Risk Patient profile"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
test do
|
|
36
|
+
optional
|
|
37
|
+
title 'Test Patient conforms to CVD Risk Patient profile'
|
|
38
|
+
|
|
39
|
+
input :patient_id,
|
|
40
|
+
title: 'Patient ID',
|
|
41
|
+
description: 'ID of a patient resource on the server'
|
|
42
|
+
|
|
43
|
+
run do
|
|
44
|
+
skip_if patient_id.blank?, 'No patient ID provided'
|
|
45
|
+
|
|
46
|
+
fhir_read(:patient, patient_id)
|
|
47
|
+
|
|
48
|
+
assert_response_status(200)
|
|
49
|
+
assert_resource_type(:patient)
|
|
50
|
+
|
|
51
|
+
assert_valid_resource(profile_url: 'https://www.cvdcheck.org.au/fhir/StructureDefinition/CVDRiskPatient')
|
|
52
|
+
|
|
53
|
+
pass 'Test Patient has no validation errors'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|