cqm-converter 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +24 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rubocop.yml +41 -0
- data/.travis.yml +9 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +201 -0
- data/README.md +78 -0
- data/Rakefile +15 -0
- data/bin/console +7 -0
- data/bin/setup +6 -0
- data/cqm-converter.gemspec +33 -0
- data/lib/cqm/converter.rb +14 -0
- data/lib/cqm/converter/hds_record.rb +125 -0
- data/lib/cqm/converter/qdm_patient.rb +325 -0
- data/lib/cqm/converter/utils.rb +202 -0
- data/lib/tasks/hds.rake +16 -0
- data/lib/tasks/qdm.rake +16 -0
- metadata +230 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require 'health-data-standards'
|
3
|
+
require 'cqm/models'
|
4
|
+
|
5
|
+
# Base CQM module.
|
6
|
+
module CQM
|
7
|
+
# Base CQM Converter module.
|
8
|
+
module Converter
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require_relative 'converter/utils'
|
13
|
+
require_relative 'converter/hds_record'
|
14
|
+
require_relative 'converter/qdm_patient'
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'execjs'
|
2
|
+
require 'sprockets'
|
3
|
+
|
4
|
+
# CQM Converter module for HDS models.
|
5
|
+
module CQM::Converter
|
6
|
+
# CQM Converter class for HDS based records.
|
7
|
+
class HDSRecord
|
8
|
+
# Initialize a new HDSRecord converter. NOTE: This should be done once, and then
|
9
|
+
# used for every HDS Record you want to convert, since it takes a few seconds
|
10
|
+
# to initialize the conversion environment using Sprockets.
|
11
|
+
def initialize
|
12
|
+
# Create a new sprockets environment.
|
13
|
+
environment = Sprockets::Environment.new
|
14
|
+
|
15
|
+
# Populate the JavaScript environment with the cql_qdm_patientapi mappings and
|
16
|
+
# its dependencies.
|
17
|
+
cql_qdm_patientapi_spec = Gem::Specification.find_by_name('cql_qdm_patientapi')
|
18
|
+
momentjs_rails_spec = Gem::Specification.find_by_name('momentjs-rails')
|
19
|
+
environment.append_path(cql_qdm_patientapi_spec.gem_dir + '/app/assets/javascripts')
|
20
|
+
environment.append_path(cql_qdm_patientapi_spec.gem_dir + '/vendor/assets/javascripts')
|
21
|
+
environment.append_path(momentjs_rails_spec.gem_dir + '/vendor/assets/javascripts')
|
22
|
+
@js_dependencies = environment['moment'].to_s
|
23
|
+
@js_dependencies += environment['cql4browsers'].to_s
|
24
|
+
@js_dependencies += environment['cql_qdm_patientapi'].to_s
|
25
|
+
@qdm_model_attrs = Utils.gather_qdm_model_attrs
|
26
|
+
end
|
27
|
+
|
28
|
+
# Given an HDS record, return a corresponding QDM patient.
|
29
|
+
def to_qdm(record)
|
30
|
+
# Start with a new QDM patient.
|
31
|
+
patient = QDM::Patient.new
|
32
|
+
|
33
|
+
# Build and execute JavaScript that will create a 'CQL_QDM.Patient'
|
34
|
+
# JavaScript version of the HDS record. Specifically, we will use
|
35
|
+
# this to build our patient's 'dataElements'.
|
36
|
+
cql_qdm_patient = ExecJS.exec Utils.hds_to_qdm_js(@js_dependencies, record, @qdm_model_attrs)
|
37
|
+
|
38
|
+
# Make sure all date times are in the correct form.
|
39
|
+
Utils.date_time_adjuster(cql_qdm_patient) if cql_qdm_patient
|
40
|
+
|
41
|
+
# Grab the results from the CQL_QDM.Patient and add a new 'data_element'
|
42
|
+
# for each datatype found on the CQL_QDM.Patient to the new QDM Patient.
|
43
|
+
cql_qdm_patient.keys.each do |dc_type|
|
44
|
+
cql_qdm_patient[dc_type].each do |dc|
|
45
|
+
# Convert snake_case to camelCase
|
46
|
+
dc_fixed_keys = dc.deep_transform_keys { |key| key.to_s.camelize(:lower) }
|
47
|
+
|
48
|
+
# Our Code model uses 'codeSystem' to describe the code system (since system is
|
49
|
+
# a reserved keyword). The cql.Code calls this 'system', so make sure the proper
|
50
|
+
# conversion is made. Also do this for 'display', where we call this descriptor.
|
51
|
+
dc_fixed_keys = dc_fixed_keys.deep_transform_keys { |key| key.to_s == 'system' ? :codeSystem : key }
|
52
|
+
dc_fixed_keys = dc_fixed_keys.deep_transform_keys { |key| key.to_s == 'display' ? :descriptor : key }
|
53
|
+
|
54
|
+
patient.dataElements << QDM.const_get(dc_type).new.from_json(dc_fixed_keys.to_json)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Convert patient characteristic birthdate.
|
59
|
+
birthdate = record.birthdate
|
60
|
+
if birthdate
|
61
|
+
birth_datetime = DateTime.strptime(birthdate.to_s, '%s')
|
62
|
+
code = QDM::Code.new('21112-8', 'LOINC')
|
63
|
+
patient.dataElements << QDM::PatientCharacteristicBirthdate.new(birthDatetime: birth_datetime, dataElementCodes: [code])
|
64
|
+
end
|
65
|
+
|
66
|
+
# Convert patient characteristic clinical trial participant.
|
67
|
+
# TODO, Adam 4/1: The Bonnie team is working on implementing this in HDS. When that work
|
68
|
+
# is complete, this should be updated to reflect how that looks in HDS.
|
69
|
+
# patient.dataElements << QDM::PatientCharacteristicClinicalTrialParticipant.new
|
70
|
+
|
71
|
+
# Convert patient characteristic ethnicity.
|
72
|
+
ethnicity = record.ethnicity
|
73
|
+
if ethnicity
|
74
|
+
code = QDM::Code.new(ethnicity['code'], ethnicity['codeSystem'], ethnicity['name'], Utils.code_system_helper(ethnicity['codeSystem']))
|
75
|
+
patient.dataElements << QDM::PatientCharacteristicEthnicity.new(dataElementCodes: [code])
|
76
|
+
end
|
77
|
+
|
78
|
+
# Convert patient characteristic expired.
|
79
|
+
expired = record.deathdate
|
80
|
+
if expired
|
81
|
+
expired_datetime = DateTime.strptime(expired.to_s, '%s')
|
82
|
+
code = QDM::Code.new('419099009', 'SNOMED-CT')
|
83
|
+
patient.dataElements << QDM::PatientCharacteristicExpired.new(expiredDatetime: expired_datetime, dataElementCodes: [code])
|
84
|
+
end
|
85
|
+
|
86
|
+
# Convert patient characteristic race.
|
87
|
+
race = record.race
|
88
|
+
if race
|
89
|
+
code = QDM::Code.new(race['code'], race['codeSystem'], race['name'], Utils.code_system_helper(race['codeSystem']))
|
90
|
+
patient.dataElements << QDM::PatientCharacteristicRace.new(dataElementCodes: [code])
|
91
|
+
end
|
92
|
+
|
93
|
+
# Convert patient characteristic sex.
|
94
|
+
sex = record.gender
|
95
|
+
if sex
|
96
|
+
code = QDM::Code.new(sex, 'AdministrativeSex', Utils.code_system_helper('AdministrativeSex'))
|
97
|
+
patient.dataElements << QDM::PatientCharacteristicSex.new(dataElementCodes: [code])
|
98
|
+
end
|
99
|
+
|
100
|
+
# Convert remaining metadata.
|
101
|
+
patient.birthDatetime = DateTime.strptime(record.birthdate.to_s, '%s') if record.birthdate
|
102
|
+
patient.givenNames = record.first ? [record.first] : []
|
103
|
+
patient.familyName = record.last if record.last
|
104
|
+
patient.bundleId = record.bundle_id if record.bundle_id
|
105
|
+
|
106
|
+
# Convert extended_data.
|
107
|
+
patient.extendedData = {}
|
108
|
+
patient.extendedData['type'] = record.type if record.respond_to?('type')
|
109
|
+
patient.extendedData['measure_ids'] = record.measure_ids if record.respond_to?('measure_ids')
|
110
|
+
patient.extendedData['source_data_criteria'] = record.source_data_criteria if record.respond_to?('source_data_criteria')
|
111
|
+
patient.extendedData['expected_values'] = record.expected_values if record.respond_to?('expected_values')
|
112
|
+
patient.extendedData['notes'] = record.notes if record.respond_to?('notes')
|
113
|
+
patient.extendedData['is_shared'] = record.is_shared if record.respond_to?('is_shared')
|
114
|
+
patient.extendedData['origin_data'] = record.origin_data if record.respond_to?('origin_data')
|
115
|
+
patient.extendedData['test_id'] = record.test_id if record.respond_to?('test_id')
|
116
|
+
patient.extendedData['medical_record_number'] = record.medical_record_number if record.respond_to?('medical_record_number')
|
117
|
+
patient.extendedData['medical_record_assigner'] = record.medical_record_assigner if record.respond_to?('medical_record_assigner')
|
118
|
+
patient.extendedData['description'] = record.description if record.respond_to?('description')
|
119
|
+
patient.extendedData['description_category'] = record.description_category if record.respond_to?('description_category')
|
120
|
+
patient.extendedData['insurance_providers'] = record.insurance_providers.to_json(except: '_id') if record.respond_to?('insurance_providers')
|
121
|
+
|
122
|
+
patient
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,325 @@
|
|
1
|
+
# CQM Converter module for QDM models.
|
2
|
+
module CQM::Converter
|
3
|
+
# CQM Converter class for QDM based patients.
|
4
|
+
class QDMPatient
|
5
|
+
# Initialize a new QDMPatient converter. NOTE: This should be done once, and then
|
6
|
+
# used for every QDM Patient you want to convert, since it takes a few seconds
|
7
|
+
# to initialize the conversion environment.
|
8
|
+
def initialize
|
9
|
+
@qdm_model_attrs = Utils.gather_qdm_model_attrs
|
10
|
+
@qdm_to_hds_mappings = Utils.gather_qdm_to_hds_mappings(@qdm_model_attrs)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Given a QDM patient, return a corresponding HDS record.
|
14
|
+
def to_hds(patient)
|
15
|
+
# Start with a new HDS record.
|
16
|
+
record = Record.new
|
17
|
+
|
18
|
+
# Loop over the QDM Patient's data elements, and create the corresponding
|
19
|
+
# HDS Record Entry models on the newly created record.
|
20
|
+
patient.dataElements.each do |data_element|
|
21
|
+
category = data_element.category if data_element.fields.include? 'category'
|
22
|
+
next unless category
|
23
|
+
# Handle patient characteristics seperately.
|
24
|
+
next if data_element.category == 'patient_characteristic'
|
25
|
+
|
26
|
+
# Grab the QDM datatype name of this data element.
|
27
|
+
qdm_model_name = data_element.class.name.demodulize
|
28
|
+
|
29
|
+
# Handle mismatched HDS names for QDM things.
|
30
|
+
category = Utils.qdm_to_hds_class_type(category)
|
31
|
+
|
32
|
+
# Grab the corresponding HDS model we will cast this QDM model into.
|
33
|
+
hds_model = category.camelize.constantize
|
34
|
+
|
35
|
+
# Start with a new HDS entry.
|
36
|
+
hds_entry = hds_model.new
|
37
|
+
|
38
|
+
# Populate codes.
|
39
|
+
hds_entry.codes = Utils.qdm_codes_to_hds_codes(data_element.dataElementCodes)
|
40
|
+
|
41
|
+
# Populate OID.
|
42
|
+
hds_entry.oid = data_element.hqmfOid
|
43
|
+
|
44
|
+
# Populate description.
|
45
|
+
hds_entry.description = data_element.description
|
46
|
+
|
47
|
+
# Set type.
|
48
|
+
hds_entry.set(_type: hds_model.to_s)
|
49
|
+
|
50
|
+
# Set status_code.
|
51
|
+
status = data_element.fields.include?('qdmStatus') ? data_element[:qdmStatus] : nil
|
52
|
+
status = 'ordered' if status == 'order'
|
53
|
+
hds_entry.status_code = { 'HL7 ActStatus': [status] }
|
54
|
+
|
55
|
+
# Grab the QDM attribute mappings for this data element, and construct the
|
56
|
+
# corresponding HDS attributes.
|
57
|
+
hds_attrs = {}
|
58
|
+
@qdm_to_hds_mappings[qdm_model_name].each do |qdm_attr, hds_attr|
|
59
|
+
next if data_element[qdm_attr].nil?
|
60
|
+
extracted_value = extractor(data_element[qdm_attr].as_json)
|
61
|
+
if hds_attr.is_a?(Hash) && hds_attr[:low]
|
62
|
+
# Handle something that has multiple parts.
|
63
|
+
hds_attrs[hds_attr[:low]] = extracted_value.first
|
64
|
+
hds_attrs[hds_attr[:high]] = extracted_value.last if hds_attr[:high]
|
65
|
+
elsif extracted_value.is_a?(Array) && extracted_value.any? && extracted_value.first.is_a?(Hash) && extracted_value.first[:code]
|
66
|
+
# Handle a result that is returning multiple codes.
|
67
|
+
hds_attrs[hds_attr] = [CodedResultValue.new(codes: Utils.qdm_codes_to_hds_codes(extracted_value), description: extracted_value.first[:title])]
|
68
|
+
elsif extracted_value.is_a?(Array)
|
69
|
+
# Handle simple Arrays.
|
70
|
+
hds_attrs[hds_attr] = { values: extracted_value } if extracted_value.any?
|
71
|
+
elsif hds_attr == 'values' && extracted_value.key?(:scalar)
|
72
|
+
# Handle a Quantity.
|
73
|
+
hds_attrs[hds_attr] = [PhysicalQuantityResultValue.new(extracted_value)]
|
74
|
+
elsif hds_attr == 'values' && extracted_value.key?(:code)
|
75
|
+
# Handle a Code result.
|
76
|
+
hds_attrs[hds_attr] = [CodedResultValue.new(codes: Utils.qdm_codes_to_hds_codes([extracted_value]), description: extracted_value[:title])]
|
77
|
+
elsif hds_attr == 'dose'
|
78
|
+
# Handle a dosage.
|
79
|
+
hds_attrs[hds_attr] = dose_extractor(extracted_value)
|
80
|
+
else
|
81
|
+
# Nothing special.
|
82
|
+
hds_attrs[hds_attr] = extracted_value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# If there is an actual negationReason, set negationInd to true.
|
87
|
+
if hds_attrs.key?('negationReason') && !hds_attrs['negationReason'].nil?
|
88
|
+
hds_attrs['negationInd'] = true
|
89
|
+
end
|
90
|
+
|
91
|
+
# Unpack references.
|
92
|
+
unpack_references(hds_attrs)
|
93
|
+
|
94
|
+
# Unpack facility.
|
95
|
+
unpack_facility(hds_attrs)
|
96
|
+
|
97
|
+
# Unpack diagnosis.
|
98
|
+
unpack_diagnosis(hds_attrs)
|
99
|
+
|
100
|
+
# Unpack components.
|
101
|
+
unpack_components(hds_attrs)
|
102
|
+
|
103
|
+
# Communication entries need direction, which we can get from the QDM model name.
|
104
|
+
if hds_entry._type == 'Communication'
|
105
|
+
hds_attrs['direction'] = qdm_model_name.underscore
|
106
|
+
end
|
107
|
+
|
108
|
+
# Ignore infinity dates.
|
109
|
+
Utils.fix_infinity_dates(hds_attrs)
|
110
|
+
|
111
|
+
# Apply the attributes to the entry.
|
112
|
+
hds_entry.set(hds_attrs)
|
113
|
+
|
114
|
+
# Add entry to HDS record, make sure to handle tricky plural types.
|
115
|
+
plural_category = ['medical_equipment'].include?(category) ? category : category.pluralize
|
116
|
+
record.send(plural_category) << hds_entry unless hds_entry.codes.empty?
|
117
|
+
end
|
118
|
+
|
119
|
+
# Unpack patient characteristics.
|
120
|
+
unpack_patient_characteristics(patient, record)
|
121
|
+
|
122
|
+
# Unpack extended_data.
|
123
|
+
unpack_extended_data(patient, record)
|
124
|
+
|
125
|
+
record
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
# Given something QDM model based, return a corresponding HDS
|
131
|
+
# representation. This will operate recursively.
|
132
|
+
def extractor(qdm_thing)
|
133
|
+
keys = qdm_thing.symbolize_keys.keys if qdm_thing.class.to_s == 'Hash'
|
134
|
+
if qdm_thing.nil? # Is nothing.
|
135
|
+
nil
|
136
|
+
elsif ['Time', 'DateTime', 'Date'].include? qdm_thing.class.to_s # Is a DateTime.
|
137
|
+
date_time_converter(qdm_thing)
|
138
|
+
elsif qdm_thing.is_a?(String) && date_time?(qdm_thing) # Is a DateTime.
|
139
|
+
date_time_converter(qdm_thing)
|
140
|
+
elsif qdm_thing.is_a?(Hash) && keys.include?(:low) # Is a QDM::Interval.
|
141
|
+
interval_extractor(qdm_thing.symbolize_keys)
|
142
|
+
elsif qdm_thing.is_a?(Hash) && keys.include?(:code) # Is a QDM::Code.
|
143
|
+
code_extractor(qdm_thing.symbolize_keys)
|
144
|
+
elsif qdm_thing.is_a?(Hash) && keys.include?(:unit) # Is a QDM::Quantity.
|
145
|
+
quantity_extractor(qdm_thing.symbolize_keys)
|
146
|
+
elsif qdm_thing.is_a?(Array) # Is an Array.
|
147
|
+
qdm_thing.collect { |item| extractor(item) }
|
148
|
+
elsif qdm_thing.is_a?(Numeric) # Is an Number.
|
149
|
+
{ units: '', scalar: qdm_thing.to_s }
|
150
|
+
elsif qdm_thing.is_a?(String) # Is an String.
|
151
|
+
qdm_thing
|
152
|
+
elsif qdm_thing.is_a?(Hash)
|
153
|
+
qdm_thing.each { |k, v| qdm_thing[k] = extractor(v) }
|
154
|
+
else
|
155
|
+
raise 'Unsupported type! Found: ' + qdm_thing.class.to_s
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Extract a QDM::Code to something usable in HDS.
|
160
|
+
def code_extractor(code)
|
161
|
+
{ code: code[:code], code_system: code[:codeSystem], title: code[:descriptor] }
|
162
|
+
end
|
163
|
+
|
164
|
+
# Extract a QDM::Interval to something usable in HDS.
|
165
|
+
def interval_extractor(interval)
|
166
|
+
# If this interval has a high of inifinity, nil it out.
|
167
|
+
interval[:high] = nil if interval[:high] == '9999-12-31T23:59:59.99+0000'
|
168
|
+
[extractor(interval[:low]), extractor(interval[:high])]
|
169
|
+
end
|
170
|
+
|
171
|
+
# Extract a QDM::Quantity to something usable in HDS.
|
172
|
+
def quantity_extractor(quantity)
|
173
|
+
{ units: quantity[:unit], scalar: quantity[:value].to_s }
|
174
|
+
end
|
175
|
+
|
176
|
+
# Extract a Dose to something usable in HDS.
|
177
|
+
def dose_extractor(dose)
|
178
|
+
{ unit: dose[:units], value: dose[:scalar] }
|
179
|
+
end
|
180
|
+
|
181
|
+
# Convert a DateTime to something usable in HDS.
|
182
|
+
def date_time_converter(date_time)
|
183
|
+
date_time = DateTime.parse(date_time) if date_time.class.to_s == 'String'
|
184
|
+
date_time.to_i
|
185
|
+
end
|
186
|
+
|
187
|
+
# Grab the data elements on the patient. This should only be used when there
|
188
|
+
# is no active Mongo connection.
|
189
|
+
def get_data_elements(patient, category, status = nil)
|
190
|
+
matches = []
|
191
|
+
patient.dataElements.each do |data_element|
|
192
|
+
matches << data_element if data_element[:category] == category && (data_element[:qdmStatus] == status || status.nil?)
|
193
|
+
end
|
194
|
+
matches
|
195
|
+
end
|
196
|
+
|
197
|
+
# Grab the data elements on the patient by _type. This should only be used when there
|
198
|
+
# is no active Mongo connection.
|
199
|
+
def get_data_elements_by_type(patient, type)
|
200
|
+
matches = []
|
201
|
+
patient.dataElements.each do |data_element|
|
202
|
+
matches << data_element if data_element[:_type] == type
|
203
|
+
end
|
204
|
+
matches
|
205
|
+
end
|
206
|
+
|
207
|
+
# Check if the given string is a DateTime.
|
208
|
+
def date_time?(date_time)
|
209
|
+
true if DateTime.parse(date_time)
|
210
|
+
rescue ArgumentError
|
211
|
+
false
|
212
|
+
end
|
213
|
+
|
214
|
+
# Unpack components.
|
215
|
+
def unpack_components(hds_attrs)
|
216
|
+
return unless hds_attrs.key?('components') && !hds_attrs['components'].nil?
|
217
|
+
hds_attrs['components']['type'] = 'COL'
|
218
|
+
hds_attrs['components'][:values]&.collect do |code_value|
|
219
|
+
code_value['code'] = code_value.delete('Code')
|
220
|
+
code_value['result'] = { code: code_value.delete('Result'), title: code_value['code'][:title] }
|
221
|
+
code_value['code'].delete(:title)
|
222
|
+
code_value['result'][:code].delete(:title)
|
223
|
+
{ code: code_value }
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Unpack diagnosis.
|
228
|
+
def unpack_diagnosis(hds_attrs)
|
229
|
+
if hds_attrs.key?('diagnosis') && !hds_attrs['diagnosis'].empty?
|
230
|
+
unpacked = {}
|
231
|
+
unpacked['type'] = 'COL'
|
232
|
+
unpacked['values'] = hds_attrs['diagnosis'].collect do |diag|
|
233
|
+
code = Utils.hds_codes_to_qdm_codes(diag.codes).first
|
234
|
+
{
|
235
|
+
code_system: code[:codeSystem],
|
236
|
+
code: code[:code],
|
237
|
+
title: diag.description
|
238
|
+
}
|
239
|
+
end
|
240
|
+
hds_attrs['diagnosis'] = unpacked
|
241
|
+
end
|
242
|
+
# Remove diagnosis if principalDiagnosis is equivalent.
|
243
|
+
return unless hds_attrs.key?('diagnosis') && hds_attrs.key?('principalDiagnosis')
|
244
|
+
return unless hds_attrs['diagnosis']['values'] && Hash[hds_attrs['diagnosis']['values'].first.sort] == Hash[hds_attrs['principalDiagnosis'].sort]
|
245
|
+
hds_attrs.delete('diagnosis')
|
246
|
+
end
|
247
|
+
|
248
|
+
# Unpack facility.
|
249
|
+
def unpack_facility(hds_attrs)
|
250
|
+
return unless hds_attrs.key?('facility') && !hds_attrs['facility'].empty?
|
251
|
+
hds_attrs['facility']['type'] = 'COL'
|
252
|
+
hds_attrs['facility'][:values]&.each do |value|
|
253
|
+
value['code'] = value.delete('Code')
|
254
|
+
value[:display] = value['code'].delete(:title) if value['code']
|
255
|
+
value[:locationPeriodHigh] = Time.at(value['Locationperiod'].last).utc.strftime('%m/%d/%Y %l:%M %p').split.join(' ')
|
256
|
+
value[:locationPeriodLow] = Time.at(value['Locationperiod'].first).utc.strftime('%m/%d/%Y %l:%M %p').split.join(' ')
|
257
|
+
value.delete('Locationperiod')
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
# Unpack references.
|
262
|
+
def unpack_references(hds_attrs)
|
263
|
+
return unless hds_attrs.key?('references') && !hds_attrs['references'].empty?
|
264
|
+
hds_attrs['references'] = hds_attrs['references'][:values].collect { |value| { referenced_id: value['value'], referenced_type: value['referencedType'], type: value['type'] } }
|
265
|
+
end
|
266
|
+
|
267
|
+
# Unpack patient characteristics.
|
268
|
+
def unpack_patient_characteristics(patient, record)
|
269
|
+
# Convert patient characteristic birthdate.
|
270
|
+
birthdate = get_data_elements(patient, 'patient_characteristic', 'birthdate').first
|
271
|
+
record.birthdate = birthdate.birthDatetime if birthdate
|
272
|
+
|
273
|
+
# Convert patient characteristic clinical trial participant.
|
274
|
+
# TODO, Adam 4/9: The Bonnie team is working on implementing this in HDS. When that work
|
275
|
+
# is complete, this should be updated to reflect how that looks in HDS.
|
276
|
+
# clinical_trial_participant = get_data_elements(patient, 'patient_characteristic', 'clinical_trial_participant').first
|
277
|
+
|
278
|
+
# Convert patient characteristic ethnicity.
|
279
|
+
ethnicity = get_data_elements(patient, 'patient_characteristic', 'ethnicity').first
|
280
|
+
ethnicity_code = ethnicity.dataElementCodes.first.symbolize_keys if ethnicity.dataElementCodes.any?
|
281
|
+
record.ethnicity = { code: ethnicity_code[:code], name: ethnicity_code[:descriptor], codeSystem: ethnicity_code[:codeSystem] } if ethnicity_code
|
282
|
+
|
283
|
+
# Convert patient characteristic expired.
|
284
|
+
expired = get_data_elements_by_type(patient, 'QDM::PatientCharacteristicExpired').first
|
285
|
+
record.deathdate = date_time_converter(expired.expiredDatetime) if expired
|
286
|
+
record.expired = record.deathdate if record.deathdate
|
287
|
+
|
288
|
+
# Convert patient characteristic race.
|
289
|
+
race = get_data_elements(patient, 'patient_characteristic', 'race').first
|
290
|
+
race_code = race.dataElementCodes.first.symbolize_keys if race.dataElementCodes.any?
|
291
|
+
record.race = { code: race_code[:code], name: race_code[:descriptor], codeSystem: race_code[:codeSystem] } if race_code
|
292
|
+
|
293
|
+
# Convert patient characteristic sex.
|
294
|
+
sex = get_data_elements_by_type(patient, 'QDM::PatientCharacteristicSex').first
|
295
|
+
sex_code = sex.dataElementCodes.first.symbolize_keys if sex.dataElementCodes.any?
|
296
|
+
record.gender = sex_code[:code] if sex
|
297
|
+
|
298
|
+
# Convert remaining metadata.
|
299
|
+
record.birthdate = date_time_converter(patient.birthDatetime) unless record.birthdate
|
300
|
+
record.first = patient.givenNames.first if patient.givenNames.any?
|
301
|
+
record.last = patient.familyName if patient.familyName
|
302
|
+
record.bundle_id = patient.bundleId if patient.bundleId
|
303
|
+
end
|
304
|
+
|
305
|
+
# Unpack extended data.
|
306
|
+
def unpack_extended_data(patient, record)
|
307
|
+
record['type'] = patient.extendedData['type'] if patient.extendedData['type']
|
308
|
+
record['measure_ids'] = patient.extendedData['measure_ids'] if patient.extendedData['measure_ids']
|
309
|
+
record['source_data_criteria'] = patient.extendedData['source_data_criteria'] if patient.extendedData['source_data_criteria']
|
310
|
+
record['expected_values'] = patient.extendedData['expected_values'] if patient.extendedData['expected_values'].is_a?(Array)
|
311
|
+
record['notes'] = patient.extendedData['notes'] if patient.extendedData['notes']
|
312
|
+
record['is_shared'] = patient.extendedData['is_shared'] if patient.extendedData['is_shared']
|
313
|
+
record['origin_data'] = patient.extendedData['origin_data'] if patient.extendedData['origin_data']
|
314
|
+
record['test_id'] = patient.extendedData['test_id'] if patient.extendedData['test_id']
|
315
|
+
record['medical_record_number'] = patient.extendedData['medical_record_number'] if patient.extendedData['medical_record_number']
|
316
|
+
record['medical_record_assigner'] = patient.extendedData['medical_record_assigner'] if patient.extendedData['medical_record_assigner']
|
317
|
+
record['description'] = patient.extendedData['description'] if patient.extendedData['description']
|
318
|
+
record['description_category'] = patient.extendedData['description_category'] if patient.extendedData['description_category']
|
319
|
+
insurance_providers = JSON.parse(patient.extendedData['insurance_providers']).collect do |insurance_provider|
|
320
|
+
InsuranceProvider.new.from_json(insurance_provider.to_json)
|
321
|
+
end
|
322
|
+
record['insurance_providers'] = insurance_providers
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|