quality-measure-engine 1.0.4 → 1.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.
@@ -2,8 +2,6 @@ require "bundler/setup"
2
2
 
3
3
  require 'resque/job_with_status'
4
4
 
5
- require_relative 'qme/ext/string'
6
-
7
5
  require_relative 'qme/database_access'
8
6
  require_relative 'qme/quality_measure'
9
7
 
@@ -13,20 +11,19 @@ require_relative 'qme/map/measure_calculation_job'
13
11
 
14
12
  require_relative 'qme/quality_report'
15
13
 
14
+ require_relative 'qme/randomizer/random_patient_creator'
16
15
  require_relative 'qme/randomizer/patient_randomizer'
17
16
  require_relative 'qme/randomizer/patient_randomization_job'
18
17
 
19
18
  require 'singleton'
19
+ require 'health-data-standards'
20
20
 
21
- require_relative 'qme/importer/entry'
22
- require_relative 'qme/importer/property_matcher'
23
- require_relative 'qme/importer/patient_importer'
24
- require_relative 'qme/importer/code_system_helper'
25
- require_relative 'qme/importer/hl7_helper'
21
+ require_relative 'qme/ext/record'
26
22
 
27
- require_relative 'qme/importer/section_importer'
23
+ require_relative 'qme/importer/property_matcher'
28
24
  require_relative 'qme/importer/generic_importer'
29
25
  require_relative 'qme/importer/provider_importer'
26
+ require_relative 'qme/importer/measure_properties_generator'
30
27
 
31
28
  require 'json'
32
29
  require 'mongo'
@@ -28,7 +28,7 @@ namespace :patient do
28
28
  QME::QualityMeasure.all.each_value do |measure_def|
29
29
  measure_id = measure_def['id']
30
30
  if !processed_measures[measure_id]
31
- QME::Importer::PatientImporter.instance.add_measure(measure_id, QME::Importer::GenericImporter.new(measure_def))
31
+ QME::Importer::MeasurePropertiesGenerator.instance.add_measure(measure_id, QME::Importer::GenericImporter.new(measure_def))
32
32
  processed_measures[measure_id]=true
33
33
  end
34
34
  end
@@ -37,8 +37,8 @@ namespace :patient do
37
37
  template = templates[rand(templates.length)]
38
38
  generator = QME::Randomizer::Patient.new(template)
39
39
  json = JSON.parse(generator.get())
40
- patient_record = QME::Importer::PatientImporter.instance.parse_hash(json)
41
- loader.save('records', patient_record)
40
+ patient_record = QME::Randomizer::RandomPatientCreator.parse_hash(json)
41
+ patient_record.save!
42
42
  end
43
43
  end
44
44
 
metadata CHANGED
@@ -1,171 +1,165 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: quality-measure-engine
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
4
5
  prerelease:
5
- version: 1.0.4
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Marc Hadley
9
9
  - Andy Gregorowicz
10
10
  - Rob Dingwell
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
-
15
- date: 2011-12-16 00:00:00 -05:00
14
+ date: 2012-01-31 00:00:00.000000000 -05:00
16
15
  default_executable:
17
- dependencies:
18
- - !ruby/object:Gem::Dependency
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
19
18
  name: mongo
20
- prerelease: false
21
- requirement: &id001 !ruby/object:Gem::Requirement
19
+ requirement: &2151835920 !ruby/object:Gem::Requirement
22
20
  none: false
23
- requirements:
21
+ requirements:
24
22
  - - ~>
25
- - !ruby/object:Gem::Version
26
- version: "1.3"
23
+ - !ruby/object:Gem::Version
24
+ version: '1.3'
27
25
  type: :runtime
28
- version_requirements: *id001
29
- - !ruby/object:Gem::Dependency
30
- name: rubyzip
31
26
  prerelease: false
32
- requirement: &id002 !ruby/object:Gem::Requirement
27
+ version_requirements: *2151835920
28
+ - !ruby/object:Gem::Dependency
29
+ name: rubyzip
30
+ requirement: &2151831040 !ruby/object:Gem::Requirement
33
31
  none: false
34
- requirements:
32
+ requirements:
35
33
  - - ~>
36
- - !ruby/object:Gem::Version
34
+ - !ruby/object:Gem::Version
37
35
  version: 0.9.4
38
36
  type: :runtime
39
- version_requirements: *id002
40
- - !ruby/object:Gem::Dependency
41
- name: nokogiri
42
37
  prerelease: false
43
- requirement: &id003 !ruby/object:Gem::Requirement
38
+ version_requirements: *2151831040
39
+ - !ruby/object:Gem::Dependency
40
+ name: nokogiri
41
+ requirement: &2151826060 !ruby/object:Gem::Requirement
44
42
  none: false
45
- requirements:
43
+ requirements:
46
44
  - - ~>
47
- - !ruby/object:Gem::Version
45
+ - !ruby/object:Gem::Version
48
46
  version: 1.4.4
49
47
  type: :runtime
50
- version_requirements: *id003
51
- - !ruby/object:Gem::Dependency
52
- name: resque
53
48
  prerelease: false
54
- requirement: &id004 !ruby/object:Gem::Requirement
49
+ version_requirements: *2151826060
50
+ - !ruby/object:Gem::Dependency
51
+ name: resque
52
+ requirement: &2151821480 !ruby/object:Gem::Requirement
55
53
  none: false
56
- requirements:
54
+ requirements:
57
55
  - - ~>
58
- - !ruby/object:Gem::Version
56
+ - !ruby/object:Gem::Version
59
57
  version: 1.15.0
60
58
  type: :runtime
61
- version_requirements: *id004
62
- - !ruby/object:Gem::Dependency
63
- name: resque-status
64
59
  prerelease: false
65
- requirement: &id005 !ruby/object:Gem::Requirement
60
+ version_requirements: *2151821480
61
+ - !ruby/object:Gem::Dependency
62
+ name: resque-status
63
+ requirement: &2151818440 !ruby/object:Gem::Requirement
66
64
  none: false
67
- requirements:
65
+ requirements:
68
66
  - - ~>
69
- - !ruby/object:Gem::Version
67
+ - !ruby/object:Gem::Version
70
68
  version: 0.2.3
71
69
  type: :runtime
72
- version_requirements: *id005
73
- - !ruby/object:Gem::Dependency
74
- name: jsonschema
75
70
  prerelease: false
76
- requirement: &id006 !ruby/object:Gem::Requirement
71
+ version_requirements: *2151818440
72
+ - !ruby/object:Gem::Dependency
73
+ name: jsonschema
74
+ requirement: &2151815160 !ruby/object:Gem::Requirement
77
75
  none: false
78
- requirements:
76
+ requirements:
79
77
  - - ~>
80
- - !ruby/object:Gem::Version
78
+ - !ruby/object:Gem::Version
81
79
  version: 2.0.0
82
80
  type: :development
83
- version_requirements: *id006
84
- - !ruby/object:Gem::Dependency
85
- name: rspec
86
81
  prerelease: false
87
- requirement: &id007 !ruby/object:Gem::Requirement
82
+ version_requirements: *2151815160
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: &2151812380 !ruby/object:Gem::Requirement
88
86
  none: false
89
- requirements:
87
+ requirements:
90
88
  - - ~>
91
- - !ruby/object:Gem::Version
89
+ - !ruby/object:Gem::Version
92
90
  version: 2.5.0
93
91
  type: :development
94
- version_requirements: *id007
95
- - !ruby/object:Gem::Dependency
96
- name: awesome_print
97
92
  prerelease: false
98
- requirement: &id008 !ruby/object:Gem::Requirement
93
+ version_requirements: *2151812380
94
+ - !ruby/object:Gem::Dependency
95
+ name: awesome_print
96
+ requirement: &2151808480 !ruby/object:Gem::Requirement
99
97
  none: false
100
- requirements:
98
+ requirements:
101
99
  - - ~>
102
- - !ruby/object:Gem::Version
103
- version: "0.3"
100
+ - !ruby/object:Gem::Version
101
+ version: '0.3'
104
102
  type: :development
105
- version_requirements: *id008
106
- - !ruby/object:Gem::Dependency
107
- name: roo
108
103
  prerelease: false
109
- requirement: &id009 !ruby/object:Gem::Requirement
104
+ version_requirements: *2151808480
105
+ - !ruby/object:Gem::Dependency
106
+ name: roo
107
+ requirement: &2151800000 !ruby/object:Gem::Requirement
110
108
  none: false
111
- requirements:
109
+ requirements:
112
110
  - - ~>
113
- - !ruby/object:Gem::Version
111
+ - !ruby/object:Gem::Version
114
112
  version: 1.9.3
115
113
  type: :development
116
- version_requirements: *id009
117
- - !ruby/object:Gem::Dependency
118
- name: builder
119
114
  prerelease: false
120
- requirement: &id010 !ruby/object:Gem::Requirement
115
+ version_requirements: *2151800000
116
+ - !ruby/object:Gem::Dependency
117
+ name: builder
118
+ requirement: &2151803680 !ruby/object:Gem::Requirement
121
119
  none: false
122
- requirements:
120
+ requirements:
123
121
  - - ~>
124
- - !ruby/object:Gem::Version
122
+ - !ruby/object:Gem::Version
125
123
  version: 3.0.0
126
124
  type: :development
127
- version_requirements: *id010
128
- - !ruby/object:Gem::Dependency
129
- name: spreadsheet
130
125
  prerelease: false
131
- requirement: &id011 !ruby/object:Gem::Requirement
126
+ version_requirements: *2151803680
127
+ - !ruby/object:Gem::Dependency
128
+ name: spreadsheet
129
+ requirement: &2151912800 !ruby/object:Gem::Requirement
132
130
  none: false
133
- requirements:
131
+ requirements:
134
132
  - - ~>
135
- - !ruby/object:Gem::Version
133
+ - !ruby/object:Gem::Version
136
134
  version: 0.6.5.2
137
135
  type: :development
138
- version_requirements: *id011
139
- - !ruby/object:Gem::Dependency
140
- name: google-spreadsheet-ruby
141
136
  prerelease: false
142
- requirement: &id012 !ruby/object:Gem::Requirement
137
+ version_requirements: *2151912800
138
+ - !ruby/object:Gem::Dependency
139
+ name: google-spreadsheet-ruby
140
+ requirement: &2152185860 !ruby/object:Gem::Requirement
143
141
  none: false
144
- requirements:
142
+ requirements:
145
143
  - - ~>
146
- - !ruby/object:Gem::Version
144
+ - !ruby/object:Gem::Version
147
145
  version: 0.1.2
148
146
  type: :development
149
- version_requirements: *id012
150
- description: A library for extracting quality measure information from HITSP C32's and ASTM CCR's
147
+ prerelease: false
148
+ version_requirements: *2152185860
149
+ description: A library for extracting quality measure information from HITSP C32's
150
+ and ASTM CCR's
151
151
  email: talk@projectpophealth.org
152
152
  executables: []
153
-
154
153
  extensions: []
155
-
156
154
  extra_rdoc_files: []
157
-
158
- files:
155
+ files:
159
156
  - lib/qme/database_access.rb
160
- - lib/qme/ext/string.rb
161
- - lib/qme/importer/code_system_helper.rb
157
+ - lib/qme/ext/record.rb
162
158
  - lib/qme/importer/entry.rb
163
159
  - lib/qme/importer/generic_importer.rb
164
- - lib/qme/importer/hl7_helper.rb
165
- - lib/qme/importer/patient_importer.rb
160
+ - lib/qme/importer/measure_properties_generator.rb
166
161
  - lib/qme/importer/property_matcher.rb
167
162
  - lib/qme/importer/provider_importer.rb
168
- - lib/qme/importer/section_importer.rb
169
163
  - lib/qme/map/map_reduce_builder.rb
170
164
  - lib/qme/map/map_reduce_executor.rb
171
165
  - lib/qme/map/measure_calculation_job.rb
@@ -177,6 +171,7 @@ files:
177
171
  - lib/qme/quality_report.rb
178
172
  - lib/qme/randomizer/patient_randomization_job.rb
179
173
  - lib/qme/randomizer/patient_randomizer.rb
174
+ - lib/qme/randomizer/random_patient_creator.rb
180
175
  - lib/qme_test.rb
181
176
  - lib/quality-measure-engine.rb
182
177
  - lib/tasks/fixtures.rake
@@ -192,30 +187,27 @@ files:
192
187
  has_rdoc: true
193
188
  homepage: http://github.com/pophealth/quality-measure-engine
194
189
  licenses: []
195
-
196
190
  post_install_message:
197
191
  rdoc_options: []
198
-
199
- require_paths:
192
+ require_paths:
200
193
  - lib
201
- required_ruby_version: !ruby/object:Gem::Requirement
194
+ required_ruby_version: !ruby/object:Gem::Requirement
202
195
  none: false
203
- requirements:
204
- - - ">="
205
- - !ruby/object:Gem::Version
206
- version: "0"
207
- required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ! '>='
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ required_rubygems_version: !ruby/object:Gem::Requirement
208
201
  none: false
209
- requirements:
210
- - - ">="
211
- - !ruby/object:Gem::Version
212
- version: "0"
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
213
206
  requirements: []
214
-
215
207
  rubyforge_project:
216
208
  rubygems_version: 1.6.2
217
209
  signing_key:
218
210
  specification_version: 3
219
- summary: A library for extracting quality measure information from HITSP C32's and ASTM CCR's
211
+ summary: A library for extracting quality measure information from HITSP C32's and
212
+ ASTM CCR's
220
213
  test_files: []
221
-
@@ -1,5 +0,0 @@
1
- class String
2
- def to_boolean
3
- ['1', 'true', 't'].include?(self.downcase)
4
- end
5
- end
@@ -1,41 +0,0 @@
1
- module QME
2
- module Importer
3
- # General helpers for working with codes and code systems
4
- class CodeSystemHelper
5
- CODE_SYSTEMS = {
6
- '2.16.840.1.113883.6.1' => 'LOINC',
7
- '2.16.840.1.113883.6.96' => 'SNOMED-CT',
8
- '2.16.840.1.113883.6.12' => 'CPT',
9
- #'2.16.840.1.113883.3.88.12.80.32' => 'CPT',
10
- '2.16.840.1.113883.6.88' => 'RxNorm',
11
- '2.16.840.1.113883.6.103' => 'ICD-9-CM',
12
- '2.16.840.1.113883.6.104' => 'ICD-9-CM',
13
- '2.16.840.1.113883.6.90' => 'ICD-10-CM',
14
- '2.16.840.1.113883.6.14' => 'HCPCS',
15
- '2.16.840.1.113883.6.59' => 'CVX',
16
- '2.16.840.1.113883.6.238' => 'CDC-RE'
17
- }
18
-
19
- # Returns the name of a code system given an oid
20
- # @param [String] oid of a code system
21
- # @return [String] the name of the code system as described in the measure definition JSON
22
- def self.code_system_for(oid)
23
- CODE_SYSTEMS[oid] || "Unknown"
24
- end
25
-
26
- # Returns the oid for a code system given a codesystem name
27
- # @param [String] the name of the code system
28
- # @return [String] the oid of the code system
29
- def self.oid_for_code_system(code_system)
30
- CODE_SYSTEMS.invert[code_system]
31
- end
32
-
33
- # Returns the whole map of OIDs to code systems
34
- # @terurn [Hash] oids as keys, code system names as values
35
- def self.code_systems
36
- CODE_SYSTEMS
37
- end
38
- end
39
- end
40
- end
41
-
@@ -1,27 +0,0 @@
1
- module QME
2
- module Importer
3
-
4
- # General helpers for working with HL7 data types
5
- class HL7Helper
6
-
7
- # Converts an HL7 timestamp into an Integer
8
- # @param [String] timestamp the HL7 timestamp. Expects YYYYMMDD format
9
- # @return [Integer] Date in seconds since the epoch
10
- def self.timestamp_to_integer(timestamp)
11
- if timestamp && timestamp.length >= 4
12
- year = timestamp[0..3].to_i
13
- month = timestamp.length >= 6 ? timestamp[4..5].to_i : 1
14
- day = timestamp.length >= 8 ? timestamp[6..7].to_i : 1
15
- hour = timestamp.length >= 10 ? timestamp[8..9].to_i : 0
16
- min = timestamp.length >= 12 ? timestamp[10..11].to_i : 0
17
- sec = timestamp.length >= 14 ? timestamp[12..13].to_i : 0
18
-
19
- Time.gm(year, month, day, hour, min, sec).to_i
20
- else
21
- nil
22
- end
23
-
24
- end
25
- end
26
- end
27
- end
@@ -1,228 +0,0 @@
1
- module QME
2
- module Importer
3
-
4
- # This class is the central location for taking a HITSP C32 XML document and converting it
5
- # into the processed form we store in MongoDB. The class does this by running each measure
6
- # independently on the XML document
7
- #
8
- # This class is a Singleton. It should be accessed by calling PatientImporter.instance
9
- class PatientImporter
10
-
11
- include Singleton
12
-
13
- # Creates a new PatientImporter with the following XPath expressions used to find content in
14
- # a HITSP C32:
15
- #
16
- # Encounter entries
17
- # //cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.127']/cda:entry/cda:encounter
18
- #
19
- # Procedure entries
20
- # //cda:procedure[cda:templateId/@root='2.16.840.1.113883.10.20.1.29']
21
- #
22
- # Result entries - There seems to be some confusion around the correct templateId, so the code checks for both
23
- # //cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.15.1'] | //cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.15']
24
- #
25
- # Vital sign entries
26
- # //cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.14']
27
- #
28
- # Medication entries
29
- # //cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.112']/cda:entry/cda:substanceAdministration
30
- #
31
- # Codes for medications are found in the substanceAdministration with the following relative XPath
32
- # ./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code
33
- #
34
- # Condition entries
35
- # //cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.103']/cda:entry/cda:act/cda:entryRelationship/cda:observation
36
- #
37
- # Codes for conditions are determined by examining the value child element as opposed to the code child element
38
- #
39
- # Social History entries (non-C32 section, specified in the HL7 CCD)
40
- # //cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.19']
41
- #
42
- # Care Goal entries(non-C32 section, specified in the HL7 CCD)
43
- # //cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.25']
44
- #
45
- # Allergy entries
46
- # //cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.18']
47
- #
48
- # Immunization entries
49
- # //cda:substanceAdministration[cda:templateId/@root='2.16.840.1.113883.10.20.1.24']
50
- #
51
- # Codes for immunizations are found in the substanceAdministration with the following relative XPath
52
- # ./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code
53
- def initialize (check_usable = true)
54
- @measure_importers = {}
55
- @section_importers = {}
56
- @id_map = {}
57
- @section_importers[:encounters] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.127']/cda:entry/cda:encounter")
58
- @section_importers[:procedures] = SectionImporter.new("//cda:procedure[cda:templateId/@root='2.16.840.1.113883.10.20.1.29']")
59
- @section_importers[:results] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.15.1'] | //cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.15']")
60
- @section_importers[:vital_signs] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.14']")
61
- @section_importers[:medications] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.112']/cda:entry/cda:substanceAdministration",
62
- "./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code",
63
- nil,
64
- "./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code/cda:originalText/cda:reference[@value]")
65
- @section_importers[:conditions] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.103']/cda:entry/cda:act/cda:entryRelationship/cda:observation",
66
- "./cda:value",
67
- "./cda:entryRelationship/cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.50']/cda:value",
68
- "./cda:text/cda:reference[@value]")
69
- @section_importers[:social_history] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.19']")
70
- @section_importers[:care_goals] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.25']")
71
- @section_importers[:medical_equipment] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.128']/cda:entry/cda:supply",
72
- "./cda:participant/cda:participantRole/cda:playingDevice/cda:code")
73
- @section_importers[:allergies] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.18']",
74
- "./cda:participant/cda:participantRole/cda:playingEntity/cda:code")
75
- @section_importers[:immunizations] = SectionImporter.new("//cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.117']/cda:entry/cda:substanceAdministration",
76
- "./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code",
77
- nil,
78
- "./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code/cda:originalText/cda:reference[@value]" )
79
- end
80
-
81
- def build_id_map(doc)
82
- id_map = {}
83
- path = "//*[@ID]"
84
- ids = doc.xpath(path)
85
- ids.each do |id|
86
- tag = id['ID']
87
- value = id.content
88
- id_map[tag] = value
89
- end
90
- return id_map
91
- end
92
-
93
- # @param [boolean] value for check_usable_entries...importer uses true, stats uses false
94
- def check_usable(check_usable_entries)
95
- @section_importers.each_pair do |section, importer|
96
- importer.check_for_usable = check_usable_entries
97
- end
98
- end
99
-
100
- # Parses a HITSP C32 document and returns a Hash of of the patient.
101
- #
102
- # @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
103
- # will have the "cda" namespace registered to "urn:hl7-org:v3"
104
- # @return [Hash] a representation of the patient that can be inserted into MongoDB
105
- def parse_c32(doc)
106
- c32_patient = {}
107
- entries = create_c32_hash(doc)
108
- get_demographics(c32_patient, doc)
109
- process_events(c32_patient, entries)
110
- end
111
-
112
- # Parses a patient hash containing demographic and event information
113
- #
114
- # @param [Hash] patient_hash patient data
115
- # @return [Hash] a representation of the patient that can be inserted into MongoDB
116
- def parse_hash(patient_hash)
117
- patient_record = {}
118
- patient_record['first'] = patient_hash['first']
119
- patient_record['patient_id'] = patient_hash['patient_id']
120
- patient_record['last'] = patient_hash['last']
121
- patient_record['gender'] = patient_hash['gender']
122
- patient_record['patient_id'] = patient_hash['patient_id']
123
- patient_record['birthdate'] = patient_hash['birthdate']
124
- patient_record['race'] = patient_hash['race']
125
- patient_record['ethnicity'] = patient_hash['ethnicity']
126
- patient_record['languages'] = patient_hash['languages']
127
- patient_record['addresses'] = patient_hash['addresses']
128
- event_hash = {}
129
- patient_hash['events'].each do |key, value|
130
- event_hash[key.intern] = parse_events(value)
131
- end
132
- process_events(patient_record, event_hash)
133
- end
134
-
135
- # Adds the entries and denormalized measure information to the patient_record.
136
- # Each Entry will be converted to a Hash and stored in an Array under the appropriate
137
- # section key, such as medications. Measure information is listed under the measures
138
- # key which has a Hash value. The Hash has the measure id as a key, and the denormalized
139
- # measure information as a value
140
- #
141
- # @param patient_record - Hash with basic patient demographic information
142
- # @entries - Hash of entries with section names a keys and an Array of Entry values
143
- def process_events(patient_record, entries)
144
- patient_record['measures'] = {}
145
- @measure_importers.each_pair do |measure_id, importer|
146
- patient_record['measures'][measure_id] = importer.parse(entries)
147
- end
148
-
149
- entries.each_pair do |key, value|
150
- patient_record[key] = value.map do |e|
151
- if e.usable?
152
- e.to_hash
153
- else
154
- nil
155
- end
156
- end.compact
157
- end
158
-
159
- patient_record
160
- end
161
-
162
- # Parses a list of event hashes into an array of Entry objects
163
- #
164
- # @param [Array] event_list list of event hashes
165
- # @return [Array] array of Entry objects
166
- def parse_events(event_list)
167
- event_list.collect do |event|
168
- if event.class==String.class
169
- # skip String elements in the event list, patient randomization templates
170
- # introduce String elements to simplify tailing-comma handling when generating
171
- # JSON using ERb
172
- nil
173
- else
174
- QME::Importer::Entry.from_event_hash(event)
175
- end
176
- end.compact
177
- end
178
-
179
- # Adds a measure to run on a C32 that is passed in
180
- #
181
- # @param [MeasureBase] measure an Class that can extract information from a C32 that is necessary
182
- # to calculate the measure
183
- def add_measure(measure_id, importer)
184
- @measure_importers[measure_id] = importer
185
- end
186
-
187
- # Create a simple representation of the patient from a HITSP C32
188
- #
189
- # @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
190
- # will have the "cda" namespace registered to "urn:hl7-org:v3"
191
- # @return [Hash] a represnetation of the patient with symbols as keys for each section
192
- def create_c32_hash(doc, check_usable_entries = true)
193
- c32_patient = {}
194
- id_map = build_id_map(doc)
195
- @section_importers.each_pair do |section, importer|
196
- importer.check_for_usable = check_usable_entries
197
- c32_patient[section] = importer.create_entries(doc,id_map)
198
- end
199
- c32_patient
200
- end
201
-
202
- # Inspects a C32 document and populates the patient Hash with first name, last name
203
- # birth date and gender.
204
- #
205
- # @param [Hash] patient A hash that is used to represent the patient
206
- # @param [Nokogiri::XML::Node] doc The C32 document parsed by Nokogiri
207
- def get_demographics(patient, doc)
208
- patient['first'] = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/cda:name/cda:given').text
209
- patient['last'] = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/cda:name/cda:family').text
210
- birthdate_in_hl7ts_node = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/cda:birthTime')
211
- birthdate_in_hl7ts = birthdate_in_hl7ts_node['value']
212
- patient['birthdate'] = HL7Helper.timestamp_to_integer(birthdate_in_hl7ts)
213
- gender_node = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/cda:administrativeGenderCode')
214
- patient['gender'] = gender_node['code']
215
- race_node = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/cda:raceCode')
216
- patient['race'] = race_node['code'] if race_node
217
- ethnicity_node = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient/cda:ethnicGroupCode')
218
- patient['ethnicity'] = ethnicity_node['code'] if ethnicity_node
219
-
220
- languages = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient').search('languageCommunication').map {|lc| lc.at_xpath('cda:languageCode')['code'] }
221
- patient['languages'] = languages unless languages.empty?
222
-
223
- id_node = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:id')
224
- patient['patient_id'] = id_node['extension']
225
- end
226
- end
227
- end
228
- end