health-data-standards 3.5.3 → 3.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -3
- data/README.md +10 -2
- data/lib/health-data-standards.rb +5 -28
- data/lib/health-data-standards/export/cat_1.rb +3 -2
- data/lib/health-data-standards/export/cat_1_r2.rb +11 -0
- data/lib/health-data-standards/ext/node.rb +1 -2
- data/lib/health-data-standards/import/bulk_record_importer.rb +4 -4
- data/lib/health-data-standards/import/bundle/importer.rb +62 -0
- data/lib/health-data-standards/import/c32/insurance_provider_importer.rb +10 -8
- data/lib/health-data-standards/import/cat1/patient_importer.rb +3 -3
- data/lib/health-data-standards/import/cat1/procedure_importer.rb +42 -0
- data/lib/health-data-standards/import/cda/medication_importer.rb +11 -11
- data/lib/health-data-standards/import/cda/section_importer.rb +13 -7
- data/lib/health-data-standards/models/cqm/aggregate_objects.rb +5 -1
- data/lib/health-data-standards/models/cqm/bundle.rb +5 -3
- data/lib/health-data-standards/models/cqm/measure.rb +1 -1
- data/lib/health-data-standards/models/entry.rb +5 -1
- data/lib/health-data-standards/models/record.rb +10 -0
- data/lib/health-data-standards/models/reference.rb +23 -0
- data/lib/health-data-standards/models/svs/value_set.rb +4 -3
- data/lib/health-data-standards/railtie.rb +1 -1
- data/lib/health-data-standards/tasks.rb +1 -0
- data/lib/health-data-standards/tasks/bundle.rake +84 -2
- data/lib/health-data-standards/util/vs_api.rb +40 -7
- data/lib/health-data-standards/validate/base_validator.rb +23 -0
- data/lib/health-data-standards/validate/data_validator.rb +85 -0
- data/lib/health-data-standards/validate/measure_validator.rb +127 -0
- data/lib/health-data-standards/validate/performance_rate_validator.rb +94 -0
- data/lib/health-data-standards/validate/reported_result_extractor.rb +170 -0
- data/lib/health-data-standards/validate/schema_validator.rb +24 -0
- data/lib/health-data-standards/validate/schematron/c_processor.rb +28 -0
- data/lib/health-data-standards/validate/schematron/java_processor.rb +93 -0
- data/lib/health-data-standards/validate/schematron_validator.rb +34 -0
- data/lib/health-data-standards/validate/validation_error.rb +10 -0
- data/lib/health-data-standards/validate/validators.rb +80 -0
- data/lib/hqmf-generator/fulfills.xml.erb +7 -0
- data/lib/hqmf-generator/hqmf-generator.rb +8 -3
- data/lib/hqmf-model/data_criteria.rb +3 -0
- data/lib/hqmf-model/types.rb +29 -0
- data/lib/hqmf-parser/2.0/data_criteria.rb +7 -0
- data/lib/hqmf-parser/2.0/types.rb +24 -0
- data/resources/schema/infrastructure/cda/CDA_SDTC.xsd +44 -0
- data/resources/schema/infrastructure/cda/POCD_MT000040_SDTC.xsd +1500 -0
- data/resources/schema/infrastructure/cda/SDTC.xsd +210 -0
- data/resources/schema/processable/coreschemas/NarrativeBlock.xsd +557 -0
- data/resources/schema/processable/coreschemas/datatypes-base_SDTC.xsd +1850 -0
- data/resources/schema/processable/coreschemas/datatypes.xsd +1375 -0
- data/resources/schema/processable/coreschemas/infrastructureRoot.xsd +27 -0
- data/resources/schema/processable/coreschemas/voc.xsd +2124 -0
- data/resources/schematron/iso-schematron-xslt1/ExtractSchFromRNG.xsl +75 -0
- data/resources/schematron/iso-schematron-xslt1/ExtractSchFromXSD.xsl +77 -0
- data/resources/schematron/iso-schematron-xslt1/iso_abstract_expand.xsl +297 -0
- data/resources/schematron/iso-schematron-xslt1/iso_dsdl_include.xsl +1509 -0
- data/resources/schematron/iso-schematron-xslt1/iso_schematron_message.xsl +55 -0
- data/resources/schematron/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1844 -0
- data/resources/schematron/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +605 -0
- data/resources/schematron/iso-schematron-xslt1/readme.txt +101 -0
- data/resources/schematron/iso-schematron-xslt1/schematron-skeleton-api.htm +723 -0
- data/resources/schematron/iso-schematron-xslt2/ExtractSchFromRNG-2.xsl +75 -0
- data/resources/schematron/iso-schematron-xslt2/ExtractSchFromXSD-2.xsl +77 -0
- data/resources/schematron/iso-schematron-xslt2/iso_abstract_expand.xsl +297 -0
- data/resources/schematron/iso-schematron-xslt2/iso_dsdl_include.xsl +1508 -0
- data/resources/schematron/iso-schematron-xslt2/iso_schematron_message_xslt2.xsl +55 -0
- data/resources/schematron/iso-schematron-xslt2/iso_schematron_skeleton_for_saxon.xsl +2299 -0
- data/resources/schematron/iso-schematron-xslt2/iso_svrl_for_xslt2.xsl +684 -0
- data/resources/schematron/iso-schematron-xslt2/readme.txt +100 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-cs.xhtml +56 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-de.xhtml +57 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-en.xhtml +57 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-fr.xhtml +54 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-nl.xhtml +58 -0
- data/resources/schematron/iso-schematron-xslt2/schematron-skeleton-api.htm +723 -0
- data/resources/schematron/qrda/cat_1/CDAR2_QRDA_I_R1_D3_2015MAY_Schematron.sch +4676 -0
- data/resources/schematron/qrda/cat_1/voc.xml +1177 -0
- data/resources/schematron/qrda/cat_1_r2/QRDA Category I Release 2.sch +4069 -0
- data/resources/schematron/qrda/cat_1_r2/voc.xml +1065 -0
- data/resources/schematron/qrda/cat_3/QRDA Category III.sch +675 -0
- data/resources/schematron/qrda/cat_3/voc.xml +21 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.26.cat1.erb +18 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.32.cat1.erb +4 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.38.cat1.erb +5 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.4.cat1.erb +1 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.64.cat1.erb +20 -0
- data/templates/cat1/_fulfills.cat1.erb +14 -0
- data/templates/cat1/_organization.cat1.erb +2 -1
- data/templates/cat1/_patient_data.cat1.erb +1 -1
- data/templates/cat1/show.cat1.erb +5 -4
- data/templates/cat3/_performance_rate.cat3.erb +5 -1
- data/templates/cat3/show.cat3.erb +1 -1
- metadata +128 -109
- data/lib/health-data-standards/export/ccr.rb +0 -417
- data/lib/health-data-standards/export/green_c32/entry.rb +0 -18
- data/lib/health-data-standards/export/green_c32/export_generator.rb +0 -23
- data/lib/health-data-standards/export/green_c32/record.rb +0 -18
- data/lib/health-data-standards/export/helper/gc32_view_helper.rb +0 -39
- data/lib/health-data-standards/import/ccr/patient_importer.rb +0 -238
- data/lib/health-data-standards/import/ccr/product_importer.rb +0 -60
- data/lib/health-data-standards/import/ccr/provider_importer.rb +0 -49
- data/lib/health-data-standards/import/ccr/result_importer.rb +0 -49
- data/lib/health-data-standards/import/ccr/section_importer.rb +0 -135
- data/lib/health-data-standards/import/ccr/simple_importer.rb +0 -30
- data/lib/health-data-standards/import/green_c32/advance_directive_importer.rb +0 -14
- data/lib/health-data-standards/import/green_c32/allergy_importer.rb +0 -20
- data/lib/health-data-standards/import/green_c32/care_goal_importer.rb +0 -26
- data/lib/health-data-standards/import/green_c32/condition_importer.rb +0 -38
- data/lib/health-data-standards/import/green_c32/encounter_importer.rb +0 -33
- data/lib/health-data-standards/import/green_c32/immunization_importer.rb +0 -23
- data/lib/health-data-standards/import/green_c32/medical_equipment_importer.rb +0 -24
- data/lib/health-data-standards/import/green_c32/medication_importer.rb +0 -68
- data/lib/health-data-standards/import/green_c32/patient_importer.rb +0 -14
- data/lib/health-data-standards/import/green_c32/procedure_importer.rb +0 -27
- data/lib/health-data-standards/import/green_c32/result_importer.rb +0 -43
- data/lib/health-data-standards/import/green_c32/section_importer.rb +0 -186
- data/lib/health-data-standards/import/green_c32/social_history_importer.rb +0 -13
- data/lib/health-data-standards/import/green_c32/support_importer.rb +0 -22
- data/lib/health-data-standards/import/green_c32/vital_sign_importer.rb +0 -21
- data/templates/gc32/_address.gc32.erb +0 -9
- data/templates/gc32/_advance_directive.gc32.erb +0 -5
- data/templates/gc32/_allergy.gc32.erb +0 -12
- data/templates/gc32/_care_goal.gc32.erb +0 -8
- data/templates/gc32/_condition.gc32.erb +0 -10
- data/templates/gc32/_encounter.gc32.erb +0 -28
- data/templates/gc32/_entry.gc32.erb +0 -3
- data/templates/gc32/_entry_attributes.gc32.erb +0 -10
- data/templates/gc32/_immunization.gc32.erb +0 -9
- data/templates/gc32/_insurance_provider.gc32.erb +0 -28
- data/templates/gc32/_medical_equipment.gc32.erb +0 -6
- data/templates/gc32/_medication.gc32.erb +0 -91
- data/templates/gc32/_name.gc32.erb +0 -11
- data/templates/gc32/_organization.gc32.erb +0 -10
- data/templates/gc32/_person_attributes.gc32.erb +0 -7
- data/templates/gc32/_procedure.gc32.erb +0 -9
- data/templates/gc32/_provider.gc32.erb +0 -9
- data/templates/gc32/_result.gc32.erb +0 -12
- data/templates/gc32/_social_history.gc32.erb +0 -6
- data/templates/gc32/_support.gc32.erb +0 -15
- data/templates/gc32/_telecom.gc32.erb +0 -1
- data/templates/gc32/_vital_sign.gc32.erb +0 -4
- data/templates/gc32/record.gc32.erb +0 -97
@@ -45,7 +45,7 @@ module HealthDataStandards
|
|
45
45
|
extract_codes(entry_element, entry)
|
46
46
|
extract_dates(entry_element, entry)
|
47
47
|
if @value_xpath
|
48
|
-
|
48
|
+
extract_values(entry_element, entry)
|
49
49
|
end
|
50
50
|
entry.description = entry_element.at_xpath("./cda:text").try("text")
|
51
51
|
if @status_xpath
|
@@ -100,21 +100,26 @@ module HealthDataStandards
|
|
100
100
|
|
101
101
|
def extract_dates(parent_element, entry, element_name="effectiveTime")
|
102
102
|
if parent_element.at_xpath("cda:#{element_name}/@value")
|
103
|
-
entry
|
103
|
+
entry[:time] = HL7Helper.timestamp_to_integer(parent_element.at_xpath("cda:#{element_name}")['value'])
|
104
104
|
end
|
105
105
|
if parent_element.at_xpath("cda:#{element_name}/cda:low")
|
106
|
-
entry
|
106
|
+
entry[:start_time] = HL7Helper.timestamp_to_integer(parent_element.at_xpath("cda:#{element_name}/cda:low")['value'])
|
107
107
|
end
|
108
108
|
if parent_element.at_xpath("cda:#{element_name}/cda:high")
|
109
|
-
entry
|
109
|
+
entry[:end_time] = HL7Helper.timestamp_to_integer(parent_element.at_xpath("cda:#{element_name}/cda:high")['value'])
|
110
110
|
end
|
111
111
|
if parent_element.at_xpath("cda:#{element_name}/cda:center")
|
112
|
-
entry
|
112
|
+
entry[:time] = HL7Helper.timestamp_to_integer(parent_element.at_xpath("cda:#{element_name}/cda:center")['value'])
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
116
|
-
def
|
117
|
-
|
116
|
+
def extract_values(parent_element, entry)
|
117
|
+
parent_element.xpath(@value_xpath).each do |elem|
|
118
|
+
extract_value(parent_element, elem, entry)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def extract_value(parent_element, value_element, entry)
|
118
123
|
if value_element
|
119
124
|
value = value_element['value']
|
120
125
|
if value.present?
|
@@ -123,6 +128,7 @@ module HealthDataStandards
|
|
123
128
|
elsif value_element['code'].present?
|
124
129
|
crv = CodedResultValue.new
|
125
130
|
add_code_if_present(value_element, crv)
|
131
|
+
extract_dates(parent_element, crv)
|
126
132
|
entry.values << crv
|
127
133
|
else
|
128
134
|
value = value_element.text
|
@@ -79,7 +79,11 @@ module HealthDataStandards
|
|
79
79
|
attr_accessor :populations
|
80
80
|
def performance_rate
|
81
81
|
numerator_count.to_f /
|
82
|
-
(
|
82
|
+
(performance_rate_denominator)
|
83
|
+
end
|
84
|
+
|
85
|
+
def performance_rate_denominator
|
86
|
+
denominator_count - denominator_exclusions_count - denominator_exceptions_count
|
83
87
|
end
|
84
88
|
|
85
89
|
def is_cv?
|
@@ -19,6 +19,8 @@ module HealthDataStandards
|
|
19
19
|
|
20
20
|
validates_presence_of :version
|
21
21
|
|
22
|
+
has_many :value_sets, class_name: "HealthDataStandards::SVS::ValueSet", inverse_of: :bundle
|
23
|
+
|
22
24
|
scope :active, -> {where(active: true)}
|
23
25
|
|
24
26
|
def self.latest_bundle_id
|
@@ -33,9 +35,9 @@ module HealthDataStandards
|
|
33
35
|
Record.where(bundle_id: self._id, test_id: nil).order_by([["last", :asc]])
|
34
36
|
end
|
35
37
|
|
36
|
-
def value_sets
|
37
|
-
|
38
|
-
end
|
38
|
+
# def value_sets
|
39
|
+
# HealthDataStandards::SVS::ValueSet.in(bundle_id: self.id)
|
40
|
+
# end
|
39
41
|
|
40
42
|
def delete
|
41
43
|
self.measures.destroy
|
@@ -9,7 +9,7 @@ class Entry
|
|
9
9
|
embedded_in :record
|
10
10
|
embeds_one :cda_identifier, class_name: "CDAIdentifier", as: :cda_identifiable
|
11
11
|
embeds_many :values, class_name: "ResultValue"
|
12
|
-
|
12
|
+
embeds_many :references
|
13
13
|
field :description, type: String
|
14
14
|
field :specifics, type: String
|
15
15
|
field :time, type: Integer
|
@@ -27,6 +27,10 @@ class Entry
|
|
27
27
|
attr_protected :created_at
|
28
28
|
attr_protected :updated_at
|
29
29
|
|
30
|
+
def add_reference(entry, type)
|
31
|
+
references.build(type: type, referenced_type: entry.class, referenced_id: entry.id)
|
32
|
+
end
|
33
|
+
|
30
34
|
def times_to_s(nil_string='UNK')
|
31
35
|
if start_time.present? || end_time.present?
|
32
36
|
start_string = start_time ? Entry.time_to_s(start_time) : nil_string
|
@@ -25,6 +25,9 @@ class Record
|
|
25
25
|
# HITSP C32
|
26
26
|
|
27
27
|
index "last" => 1
|
28
|
+
index medical_record_number: 1
|
29
|
+
index test_id: 1
|
30
|
+
index bundle_id: 1
|
28
31
|
embeds_many :allergies
|
29
32
|
embeds_many :care_goals, class_name: "Entry" # This can be any number of different entry types
|
30
33
|
embeds_many :conditions
|
@@ -103,6 +106,9 @@ class Record
|
|
103
106
|
# based on clinical content
|
104
107
|
def dedup_section_ignoring_content!(section)
|
105
108
|
unique_entries = self.send(section).uniq do |entry|
|
109
|
+
entry.references.each do |ref|
|
110
|
+
ref.resolve_referenced_id
|
111
|
+
end
|
106
112
|
entry.identifier
|
107
113
|
end
|
108
114
|
self.send("#{section}=", unique_entries)
|
@@ -110,12 +116,16 @@ class Record
|
|
110
116
|
def dedup_section_merging_codes_and_values!(section)
|
111
117
|
unique_entries = {}
|
112
118
|
self.send(section).each do |entry|
|
119
|
+
entry.references.each do |ref|
|
120
|
+
ref.resolve_referenced_id
|
121
|
+
end
|
113
122
|
if unique_entries[entry.identifier]
|
114
123
|
unique_entries[entry.identifier].codes = unique_entries[entry.identifier].codes.deep_merge(entry.codes){ |key, old, new| Array.wrap(old) + Array.wrap(new) }
|
115
124
|
unique_entries[entry.identifier].values.concat(entry.values)
|
116
125
|
else
|
117
126
|
unique_entries[entry.identifier] = entry
|
118
127
|
end
|
128
|
+
|
119
129
|
end
|
120
130
|
self.send("#{section}=", unique_entries.values)
|
121
131
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Reference
|
2
|
+
include Mongoid::Document
|
3
|
+
include Mongoid::Attributes::Dynamic
|
4
|
+
embedded_in :entry
|
5
|
+
field :type, type: String
|
6
|
+
field :referenced_type, type: String
|
7
|
+
field :referenced_id
|
8
|
+
|
9
|
+
def resolve_reference
|
10
|
+
entry.record.entries.find do |e|
|
11
|
+
e.class.to_s == referenced_type &&
|
12
|
+
e.identifier.to_s == referenced_id.to_s
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def resolve_referenced_id
|
17
|
+
resolved_reference = entry.record.entries.find do |e|
|
18
|
+
e.class.to_s == referenced_type &&
|
19
|
+
e.identifier == referenced_id
|
20
|
+
end
|
21
|
+
self.referenced_id = resolved_reference.id.to_s
|
22
|
+
end
|
23
|
+
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
module HealthDataStandards
|
2
2
|
module SVS
|
3
3
|
class ValueSet
|
4
|
-
|
5
4
|
include Mongoid::Document
|
6
|
-
include Mongoid::Attributes::Dynamic
|
7
5
|
field :oid, type: String
|
8
6
|
field :display_name, type: String
|
9
7
|
field :version, type: String
|
8
|
+
field :user_id, type: String # Eventually we need to delete this from bundles when exporting
|
9
|
+
|
10
|
+
belongs_to :bundle, class_name: "HealthDataStandards::CQM::Bundle", inverse_of: :value_sets
|
10
11
|
|
11
12
|
index({oid: 1})
|
12
13
|
index({display_name: 1})
|
@@ -43,7 +44,7 @@ module HealthDataStandards
|
|
43
44
|
code_system_name = HealthDataStandards::Util::CodeSystemHelper::CODE_SYSTEMS[con["codeSystem"]] || con["codeSystemName"]
|
44
45
|
Concept.new(code: con["code"],
|
45
46
|
code_system_name: code_system_name,
|
46
|
-
code_system_version: con["
|
47
|
+
code_system_version: con["codeSystemVersion"],
|
47
48
|
display_name: con["displayName"], code_system: con["codeSystem"])
|
48
49
|
end
|
49
50
|
vs.concepts = concepts
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__),'tasks/*.rake')].each { |f| load f }
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'health-data-standards'
|
2
|
+
require 'highline/import'
|
3
|
+
require 'open-uri'
|
2
4
|
|
3
5
|
db_name = ENV['DB_NAME'] || 'test'
|
4
6
|
|
@@ -20,6 +22,86 @@ namespace :bundle do
|
|
20
22
|
|
21
23
|
end
|
22
24
|
|
25
|
+
desc %{ Download measure/test deck bundle.
|
26
|
+
options
|
27
|
+
nlm_user - the nlm username to authenticate to the server - will prompt is not supplied
|
28
|
+
nlm_passwd - the nlm password for authenticating to the server - will prompt if not supplied
|
29
|
+
version - the version of the bundle to download. This will default to the version
|
30
|
+
|
31
|
+
example usage:
|
32
|
+
rake bundle:download nlm_name=username nlm_passwd=password version=2.1.0-latest
|
33
|
+
}
|
34
|
+
task :download => :environment do
|
35
|
+
nlm_user = ENV["nlm_user"]
|
36
|
+
nlm_passwd = ENV["nlm_pass"]
|
37
|
+
measures_dir = File.join(Dir.pwd, "bundles")
|
38
|
+
|
39
|
+
while nlm_user.nil? || nlm_user == ""
|
40
|
+
nlm_user = ask("NLM Username?: "){ |q| q.readline = true }
|
41
|
+
end
|
42
|
+
|
43
|
+
while nlm_passwd.nil? || nlm_passwd == ""
|
44
|
+
nlm_passwd = ask("NLM Password?: "){ |q| q.echo = false
|
45
|
+
q.readline = true }
|
46
|
+
end
|
47
|
+
|
48
|
+
bundle_version = ENV["version"] || "latest"
|
49
|
+
@bundle_name = "bundle-#{bundle_version}.zip"
|
50
|
+
|
51
|
+
puts "Downloading and saving #{@bundle_name} to #{measures_dir}"
|
52
|
+
# Pull down the list of bundles and download the version we're looking for
|
53
|
+
bundle_uri = "https://demo.projectcypress.org/bundles/#{@bundle_name}"
|
54
|
+
bundle = nil
|
55
|
+
|
56
|
+
tries = 0
|
57
|
+
max_tries = 10
|
58
|
+
last_error = nil
|
59
|
+
while bundle.nil? && tries < max_tries do
|
60
|
+
tries = tries + 1
|
61
|
+
begin
|
62
|
+
bundle = open(bundle_uri, :proxy => ENV["http_proxy"],:http_basic_authentication=>[nlm_user, nlm_passwd] )
|
63
|
+
rescue OpenURI::HTTPError => oe
|
64
|
+
last_error = oe
|
65
|
+
if oe.message == "401 Unauthorized"
|
66
|
+
puts "Please check your credentials and try again"
|
67
|
+
break
|
68
|
+
end
|
69
|
+
rescue => e
|
70
|
+
last_error = e
|
71
|
+
sleep 0.5
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if bundle.nil?
|
76
|
+
puts "An error occured while downloading the bundle"
|
77
|
+
raise last_error if last_error
|
78
|
+
end
|
79
|
+
# Save the bundle to the measures directory
|
80
|
+
FileUtils.mkdir_p measures_dir
|
81
|
+
FileUtils.mv(bundle.path, File.join(measures_dir, @bundle_name))
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
desc %{ Download and install the measure/test deck bundle. This is essientally delegating to the bundle_download and bundle:import tasks
|
86
|
+
options
|
87
|
+
nlm_user - the nlm username to authenticate to the server - will prompt is not supplied
|
88
|
+
nlm_passwd - the nlm password for authenticating to the server - will prompt if not supplied
|
89
|
+
version - the version of the bundle to download. This will default to the version
|
90
|
+
delete_existing - delete any existing bundles with the same version and reinstall - default is false - will cause error if same version already exists
|
91
|
+
update_measures - update any existing measures with the same hqmf_id to those contained in this bundle.
|
92
|
+
Will only work for bundle versions greater than that of the installed version - default is false
|
93
|
+
type - type of measures to be installed from bundle. A bundle may have measures of different types such as ep or eh. This will constrain the types installed, defautl is all types
|
94
|
+
example usage:
|
95
|
+
rake budnle:download_and_install nlm_name=username nlm_passwd=password version=2.1.0-latest type=ep
|
96
|
+
}
|
97
|
+
task :download_and_install => [:download] do
|
98
|
+
de = ENV['delete_existing'] || false
|
99
|
+
um = ENV['update_measures'] || false
|
100
|
+
puts "Importing bundle #{@bundle_name} delete_existing: #{de} update_measures: #{um} type: #{ENV['type'] || 'ALL'}"
|
101
|
+
task("bundle:import").invoke("bundles/#{@bundle_name}",de, um , ENV['type'], 'true')
|
102
|
+
end
|
103
|
+
|
104
|
+
|
23
105
|
|
24
106
|
desc 'List bundles'
|
25
107
|
task :list => [:environment] do
|
@@ -30,7 +112,7 @@ namespace :bundle do
|
|
30
112
|
|
31
113
|
|
32
114
|
desc 'Import a quality bundle into the database.'
|
33
|
-
task :import, [:bundle_path, :delete_existing, :update_measures, :type, :create_indexes, :exclude_results] =>
|
115
|
+
task :import, [:bundle_path, :delete_existing, :update_measures, :type, :create_indexes, :exclude_results] => :environment do |task, args|
|
34
116
|
raise "The path to the measures zip file must be specified" unless args.bundle_path
|
35
117
|
options = {:delete_existing => (args.delete_existing == "true"),
|
36
118
|
:type => args.type,
|
@@ -48,7 +130,7 @@ namespace :bundle do
|
|
48
130
|
value_sets: bundle_contents.value_sets.count}
|
49
131
|
|
50
132
|
if (args.create_indexes != 'false')
|
51
|
-
::Rails.application.eager_load!
|
133
|
+
::Rails.application.eager_load! if defined? Rails
|
52
134
|
::Mongoid::Tasks::Database.create_indexes
|
53
135
|
end
|
54
136
|
|
@@ -2,7 +2,10 @@ require 'rest_client'
|
|
2
2
|
require 'uri'
|
3
3
|
module HealthDataStandards
|
4
4
|
module Util
|
5
|
-
|
5
|
+
class VSNotFoundError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
class VSApi
|
6
9
|
attr_accessor :api_url, :ticket_url, :username, :password
|
7
10
|
|
8
11
|
def initialize(ticket_url, api_url, username, password)
|
@@ -12,7 +15,7 @@ module HealthDataStandards
|
|
12
15
|
@password = password
|
13
16
|
end
|
14
17
|
|
15
|
-
|
18
|
+
def get_valueset(oid, effective_date=nil, include_draft=false, &block)
|
16
19
|
params = {id: oid, ticket: get_ticket}
|
17
20
|
params[:effectiveDate] = effective_date if effective_date
|
18
21
|
params[:includeDraft] = 'yes' if include_draft
|
@@ -23,24 +26,54 @@ module HealthDataStandards
|
|
23
26
|
|
24
27
|
def process_valuesets(oids, effective_date=nil, &block)
|
25
28
|
oids.each do |oid|
|
26
|
-
|
27
|
-
|
29
|
+
vs = get_valueset(oid,effective_date)
|
30
|
+
yield oid,vs
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
31
34
|
def proxy_ticket
|
32
|
-
|
35
|
+
@proxy_ticket ||= get_proxy_ticket
|
33
36
|
end
|
34
37
|
|
35
38
|
def get_proxy_ticket
|
36
39
|
# the content type is set and the body is a string becuase the NLM service does not support urlencoded content and
|
37
40
|
# throws an error on that contnet type
|
38
|
-
|
41
|
+
RestClient.post ticket_url, {username: username, password: password}
|
39
42
|
end
|
40
|
-
|
43
|
+
|
41
44
|
def get_ticket
|
42
45
|
RestClient.post "#{ticket_url}/#{proxy_ticket}", {service: "http://umlsks.nlm.nih.gov"}
|
43
46
|
end
|
44
47
|
end
|
48
|
+
|
49
|
+
class VSApiV2 < VSApi
|
50
|
+
def initialize(ticket_url, api_url, username, password)
|
51
|
+
super(ticket_url, api_url, username, password)
|
52
|
+
end
|
53
|
+
|
54
|
+
def get_valueset(oid, options = {}, &block)
|
55
|
+
version = options.fetch(:version, nil)
|
56
|
+
include_draft = options.fetch(:include_draft, false)
|
57
|
+
params = {id: oid, ticket: get_ticket}
|
58
|
+
params[:version] = version if version
|
59
|
+
params[:includeDraft] = 'yes' if include_draft
|
60
|
+
begin
|
61
|
+
vs = RestClient.get api_url, {:params=>params}
|
62
|
+
rescue RestClient::ResourceNotFound
|
63
|
+
raise VSNotFoundError, "No ValueSet found for oid '#{oid}'"
|
64
|
+
end
|
65
|
+
yield oid,vs if block_given?
|
66
|
+
vs
|
67
|
+
end
|
68
|
+
|
69
|
+
def process_valuesets(oids, options = {}, &block)
|
70
|
+
version = options.fetch(:version, nil)
|
71
|
+
include_draft = options.fetch(:include_draft, false)
|
72
|
+
oids.each do |oid|
|
73
|
+
vs = get_valueset(oid, version: version, include_draft: include_draft)
|
74
|
+
yield oid,vs
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
45
78
|
end
|
46
79
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Validate
|
3
|
+
module BaseValidator
|
4
|
+
|
5
|
+
def build_error(msg, loc, file_name)
|
6
|
+
ValidationError.new(message: msg, location: loc, file_name: file_name, validator: @name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_document(input)
|
10
|
+
doc = case input
|
11
|
+
when File
|
12
|
+
input.read
|
13
|
+
when Nokogiri::XML::Document
|
14
|
+
return input
|
15
|
+
else
|
16
|
+
input
|
17
|
+
end
|
18
|
+
Nokogiri::XML(doc.to_s) { |conf| conf.strict.nonet.noblanks } #grumble, grumble nokogiri java @SS
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Validate
|
3
|
+
class DataValidator
|
4
|
+
include BaseValidator
|
5
|
+
|
6
|
+
HL7_QRDA_OIDS = ["2.16.840.1.113883.3.221.5",
|
7
|
+
"2.16.840.1.113883.3.88.12.3221.8.7",
|
8
|
+
"2.16.840.1.113883.3.88.12.3221.8.9",
|
9
|
+
"2.16.840.1.113883.1.11.12839",
|
10
|
+
"2.16.840.1.113883.3.88.12.3221.8.11",
|
11
|
+
"2.16.840.1.113883.3.88.12.3221.6.2",
|
12
|
+
"2.16.840.1.113883.11.20.9.40",
|
13
|
+
"2.16.840.1.113883.11.20.9.23",
|
14
|
+
"2.16.840.1.113883.3.88.12.3221.7.4",
|
15
|
+
"2.16.840.1.113883.11.20.9.18",
|
16
|
+
"2.16.840.1.113883.11.20.9.22",
|
17
|
+
"2.16.840.1.113883.1.11.16866",
|
18
|
+
"2.16.840.1.113883.1.11.20275",
|
19
|
+
"2.16.840.1.113883.11.20.9.34",
|
20
|
+
"2.16.840.1.113883.3.88.12.3221.7.2",
|
21
|
+
"2.16.840.1.113883.3.88.12.80.17",
|
22
|
+
"2.16.840.1.113883.3.88.12.80.22",
|
23
|
+
"2.16.840.1.113883.3.88.12.80.64",
|
24
|
+
"2.16.840.1.113883.3.88.12.3221.6.8",
|
25
|
+
"2.16.840.1.113883.1.11.78",
|
26
|
+
"2.16.840.1.113883.11.20.9.25",
|
27
|
+
"2.16.840.1.113883.11.20.9.39",
|
28
|
+
"2.16.840.1.113883.3.88.12.80.32",
|
29
|
+
"2.16.840.1.113883.11.20.9.21",
|
30
|
+
"2.16.840.1.113883.3.88.12.80.68",
|
31
|
+
"2.16.840.1.113883.1.11.20.12",
|
32
|
+
"2.16.840.1.113883.11.20.9.24",
|
33
|
+
"2.16.840.1.113883.11.20.9.41",
|
34
|
+
"2.16.840.1.113883.1.11.16926",
|
35
|
+
"2.16.840.1.113883.1.11.12212",
|
36
|
+
"2.16.840.1.113883.1.11.19185",
|
37
|
+
"2.16.840.1.113883.1.11.14914",
|
38
|
+
"2.16.840.1.114222.4.11.837",
|
39
|
+
"2.16.840.1.113883.1.11.19563",
|
40
|
+
"2.16.840.1.113883.1.11.11526",
|
41
|
+
"2.16.840.1.113883.11.20.9.20",
|
42
|
+
"2.16.840.1.113883.3.88.12.80.2",
|
43
|
+
"2.16.840.1.113883.3.88.12.80.63",
|
44
|
+
"2.16.840.1.113883.1.11.12249",
|
45
|
+
"2.16.840.1.113883.1.11.1",
|
46
|
+
"2.16.840.1.113883.1.11.12199",
|
47
|
+
"2.16.840.1.113883.11.20.9.33",
|
48
|
+
"2.16.840.1.114222.4.11.1066",
|
49
|
+
"2.16.840.1.113883.1.11.19579"]
|
50
|
+
|
51
|
+
def initialize(bundle, measure_ids)
|
52
|
+
@bundle = bundle
|
53
|
+
measures = @bundle.measures.in(hqmf_id: measure_ids)
|
54
|
+
|
55
|
+
@oids = measures.collect{|m| m.oids}.flatten.uniq + HL7_QRDA_OIDS
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate(file, options={})
|
59
|
+
doc = get_document(file)
|
60
|
+
|
61
|
+
doc.xpath("//*[@sdtc:valueSet]").inject([]) do |errors, node|
|
62
|
+
oid = node.at_xpath("@sdtc:valueSet")
|
63
|
+
vs = @bundle.value_sets.where({"oid" => oid}).first
|
64
|
+
code = node.at_xpath("@code")
|
65
|
+
code_system = node.at_xpath("@codeSystem")
|
66
|
+
null_flavor = node.at_xpath("@nullFlavor")
|
67
|
+
if !vs
|
68
|
+
errors << build_error("The valueset #{oid} declared in the document cannot be found", node.path, options[:file_name])
|
69
|
+
elsif !@oids.include?(oid.value)
|
70
|
+
errors << build_error("File appears to contain data criteria outside that required by the measures. Valuesets in file not in measures tested #{oid}'",
|
71
|
+
node.path, options[:file_name])
|
72
|
+
elsif vs.concepts.where({"code" => code, "code_system"=>code_system}).count() == 0
|
73
|
+
if !null_flavor
|
74
|
+
errors << build_error("The code #{code} in codeSystem #{code_system} cannot be found in the declared valueset #{oid}",
|
75
|
+
node.path, options[:file_name])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
errors
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|