health-data-standards 0.7.0 → 0.7.1
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.
- data/lib/health-data-standards.rb +20 -1
- data/lib/health-data-standards/export/ccr.rb +30 -5
- data/lib/health-data-standards/export/green_c32/entry.rb +15 -0
- data/lib/health-data-standards/export/green_c32/export_generator.rb +23 -0
- data/lib/health-data-standards/export/template_helper.rb +0 -1
- data/lib/health-data-standards/import/c32/condition_importer.rb +66 -0
- data/lib/health-data-standards/import/c32/provider_importer.rb +66 -0
- data/lib/health-data-standards/import/c32/section_importer.rb +2 -0
- data/lib/health-data-standards/import/ccr/patient_importer.rb +208 -0
- data/lib/health-data-standards/import/ccr/product_importer.rb +60 -0
- data/lib/health-data-standards/import/ccr/provider_importer.rb +62 -0
- data/lib/health-data-standards/import/ccr/result_importer.rb +49 -0
- data/lib/health-data-standards/import/ccr/section_importer.rb +124 -0
- data/lib/health-data-standards/import/ccr/simple_importer.rb +30 -0
- data/lib/health-data-standards/import/green_c32/condition_importer.rb +45 -0
- data/lib/health-data-standards/import/green_c32/patient_importer.rb +14 -0
- data/lib/health-data-standards/import/green_c32/result_importer.rb +30 -0
- data/lib/health-data-standards/import/green_c32/section_importer.rb +93 -0
- data/lib/health-data-standards/models/comment.rb +2 -0
- data/lib/health-data-standards/models/condition.rb +10 -0
- data/lib/health-data-standards/models/entry.rb +5 -0
- data/lib/health-data-standards/models/fulfillment_history.rb +2 -0
- data/lib/health-data-standards/models/lab_result.rb +1 -0
- data/lib/health-data-standards/models/provider.rb +51 -0
- data/lib/health-data-standards/models/provider_performance.rb +10 -0
- data/lib/health-data-standards/models/record.rb +21 -4
- data/lib/health-data-standards/models/treating_provider.rb +3 -0
- data/lib/health-data-standards/util/hl7_helper.rb +1 -0
- data/templates/_condition.gc32.erb +11 -0
- data/templates/_result.gc32.erb +20 -0
- data/templates/show.c32.erb +16 -5
- metadata +33 -12
@@ -20,9 +20,14 @@ require_relative 'health-data-standards/export/c32'
|
|
20
20
|
require_relative 'health-data-standards/export/ccr'
|
21
21
|
require_relative 'health-data-standards/export/csv'
|
22
22
|
|
23
|
+
require_relative 'health-data-standards/export/green_c32/entry'
|
24
|
+
require_relative 'health-data-standards/export/green_c32/export_generator'
|
25
|
+
|
26
|
+
|
23
27
|
require_relative 'health-data-standards/models/entry'
|
24
28
|
require_relative 'health-data-standards/models/allergy'
|
25
29
|
require_relative 'health-data-standards/models/encounter'
|
30
|
+
require_relative 'health-data-standards/models/condition'
|
26
31
|
require_relative 'health-data-standards/models/immunization'
|
27
32
|
require_relative 'health-data-standards/models/fulfillment_history'
|
28
33
|
require_relative 'health-data-standards/models/order_information'
|
@@ -30,6 +35,8 @@ require_relative 'health-data-standards/models/medication'
|
|
30
35
|
require_relative 'health-data-standards/models/procedure'
|
31
36
|
require_relative 'health-data-standards/models/lab_result'
|
32
37
|
require_relative 'health-data-standards/models/record'
|
38
|
+
require_relative 'health-data-standards/models/provider'
|
39
|
+
require_relative 'health-data-standards/models/provider_performance'
|
33
40
|
|
34
41
|
require_relative 'health-data-standards/import/c32/section_importer'
|
35
42
|
require_relative 'health-data-standards/import/c32/allergy_importer'
|
@@ -39,4 +46,16 @@ require_relative 'health-data-standards/import/c32/medication_importer'
|
|
39
46
|
require_relative 'health-data-standards/import/c32/procedure_importer'
|
40
47
|
require_relative 'health-data-standards/import/c32/result_importer'
|
41
48
|
require_relative 'health-data-standards/import/c32/vital_sign_importer'
|
42
|
-
require_relative 'health-data-standards/import/c32/patient_importer'
|
49
|
+
require_relative 'health-data-standards/import/c32/patient_importer'
|
50
|
+
require_relative 'health-data-standards/import/c32/provider_importer'
|
51
|
+
|
52
|
+
require_relative 'health-data-standards/import/ccr/patient_importer'
|
53
|
+
require_relative 'health-data-standards/import/ccr/provider_importer'
|
54
|
+
require_relative 'health-data-standards/import/ccr/section_importer'
|
55
|
+
require_relative 'health-data-standards/import/ccr/result_importer'
|
56
|
+
require_relative 'health-data-standards/import/ccr/simple_importer'
|
57
|
+
require_relative 'health-data-standards/import/ccr/product_importer'
|
58
|
+
|
59
|
+
require_relative 'health-data-standards/import/green_c32/section_importer'
|
60
|
+
require_relative 'health-data-standards/import/green_c32/result_importer'
|
61
|
+
require_relative 'health-data-standards/import/green_c32/condition_importer'
|
@@ -36,6 +36,7 @@ module HealthDataStandards
|
|
36
36
|
to_ccr_results(xml, patient)
|
37
37
|
to_ccr_procedures(xml, patient)
|
38
38
|
to_ccr_encounters(xml, patient)
|
39
|
+
to_ccr_socialhistory(xml, patient)
|
39
40
|
|
40
41
|
end
|
41
42
|
to_ccr_actors(xml, patient)
|
@@ -169,14 +170,15 @@ module HealthDataStandards
|
|
169
170
|
#time
|
170
171
|
xml.ExactDateTime(convert_to_ccr_time_string(res.time))
|
171
172
|
end
|
172
|
-
|
173
|
-
xml.Text(res.description)
|
174
|
-
code_section(xml, res.codes)
|
175
|
-
end
|
176
|
-
|
173
|
+
|
177
174
|
xml.Source
|
178
175
|
xml.Test do
|
179
176
|
xml.CCRDataObjectID("#{ccr_id}TestResult")
|
177
|
+
xml.Description do
|
178
|
+
xml.Text(res.description)
|
179
|
+
code_section(xml, res.codes)
|
180
|
+
end
|
181
|
+
|
180
182
|
xml.Source
|
181
183
|
xml.TestResult do
|
182
184
|
xml.Value(res.value["scalar"])
|
@@ -320,6 +322,29 @@ module HealthDataStandards
|
|
320
322
|
end
|
321
323
|
end
|
322
324
|
end
|
325
|
+
|
326
|
+
# Builds the XML snippet for the social history section inside the CCR standard
|
327
|
+
#
|
328
|
+
# @return [Builder::XmlMarkup] CCR XML representation of patient data
|
329
|
+
def to_ccr_socialhistory(xml, patient)
|
330
|
+
if patient.social_history.present?
|
331
|
+
xml.SocialHistory do
|
332
|
+
patient.social_history.each_with_index do |history, index|
|
333
|
+
xml.SocialHistoryElement do
|
334
|
+
xml.CCRDataObjectID("SH000#{index + 1}")
|
335
|
+
|
336
|
+
|
337
|
+
xml.Description do
|
338
|
+
xml.Text(history.description)
|
339
|
+
code_section(xml, history.codes)
|
340
|
+
end
|
341
|
+
|
342
|
+
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
323
348
|
|
324
349
|
# Builds the XML snippet for a actors section inside the CCR standard
|
325
350
|
#
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Export
|
3
|
+
module GreenC32
|
4
|
+
module Entry
|
5
|
+
include TemplateHelper
|
6
|
+
|
7
|
+
def export(object, object_type)
|
8
|
+
self.template_format = "gc32"
|
9
|
+
render(partial: object_type, locals: {object_type => object})
|
10
|
+
end
|
11
|
+
extend self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Export
|
3
|
+
module GreenC32
|
4
|
+
# Module that will create objects that can be used to export GreenCDA sections
|
5
|
+
module ExportGenerator
|
6
|
+
# Creates an object that can be used to export objects into GreenCDA.
|
7
|
+
# @example Creating an results exporter
|
8
|
+
# exporter = ExportGenerator.create_exporter_for(:result)
|
9
|
+
# exporter.export(result) # => Returns GreenCDA XML in a String
|
10
|
+
# @param [Symbol] section the section to create the exporter for
|
11
|
+
# @return [Object] that has an export method
|
12
|
+
def create_exporter_for(section)
|
13
|
+
object = Object.new
|
14
|
+
object.define_singleton_method(:export) do |section_instance|
|
15
|
+
HealthDataStandards::Export::GreenC32::Entry.export(section_instance, section)
|
16
|
+
end
|
17
|
+
object
|
18
|
+
end
|
19
|
+
extend self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Import
|
3
|
+
module C32
|
4
|
+
class ConditionImporter < SectionImporter
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@entry_xpath = "//cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.11']"
|
8
|
+
@code_xpath = "./cda:code"
|
9
|
+
@description_xpath = "./cda:code/cda:originalText/cda:reference[@value] | ./cda:text/cda:reference[@value] "
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_entries(doc, id_map = {})
|
13
|
+
@id_map = id_map
|
14
|
+
condition_list = []
|
15
|
+
entry_elements = doc.xpath(@entry_xpath)
|
16
|
+
|
17
|
+
entry_elements.each do |entry_element|
|
18
|
+
condition = Condition.new
|
19
|
+
|
20
|
+
extract_codes(entry_element, condition)
|
21
|
+
extract_dates(entry_element, condition)
|
22
|
+
extract_description(entry_element, condition, id_map)
|
23
|
+
|
24
|
+
condition.diagnosis_priority = extract_code(entry_element, "???")
|
25
|
+
|
26
|
+
condition.problem_date = extract_code(entry_element,
|
27
|
+
"./cda:entryRelationship[@typeCode='SUBJ']/cda:observation[@classCode='OBS']/cda.effectiveTime")
|
28
|
+
|
29
|
+
condition.problem_type = extract_code(entry_element,
|
30
|
+
"./cda:entryRelationship[@typeCode='SUBJ']/cda:observation[@classCode='OBS']")
|
31
|
+
|
32
|
+
condition.problem_name = extract_code(entry_element, "./cda:text")
|
33
|
+
|
34
|
+
condition.problem_code = extract_code(entry_element,
|
35
|
+
"./cda:entryRelationship[@typeCode='SUBJ']/cda:observation[@classCode='OBS']/cda:code[@code='11450-4']/cda:value[@codeSystem='2.16.840.1.113883.96']")
|
36
|
+
|
37
|
+
condition.age_at_onset = extract_code(entry_element, "???")
|
38
|
+
|
39
|
+
extract_cause_of_death(entry_element, condition)
|
40
|
+
|
41
|
+
condition.problem_status = extract_code(entry_element,
|
42
|
+
"./cda:entryRelationship[@typeCode='SUBJ']/cda:observation[@classCode='OBS']/cda:statusCode[@code='completed']")
|
43
|
+
|
44
|
+
condition_list << condition
|
45
|
+
end
|
46
|
+
|
47
|
+
condition_list
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def extract_cause_of_death(parent_element, condition)
|
53
|
+
cause_of_death_element = parent_element.at_xpath("???")
|
54
|
+
|
55
|
+
if cause_of_death_element
|
56
|
+
condition.cause_of_death = {}
|
57
|
+
|
58
|
+
condition.cause_of_death[:time_of_death] = cause_of_death_element.xpath("???")
|
59
|
+
condition.cause_of_death[:age_at_death] = cause_of_death_element.xpath("???")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "date"
|
2
|
+
require "date/delta"
|
3
|
+
|
4
|
+
module HealthDataStandards
|
5
|
+
module Import
|
6
|
+
module C32
|
7
|
+
class ProviderImporter
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
# Extract Healthcare Providers from C32
|
11
|
+
#
|
12
|
+
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
13
|
+
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
14
|
+
# @return [Array] an array of providers found in the document
|
15
|
+
def extract_providers(doc, use_encounters=false)
|
16
|
+
xpath_base = use_encounters ? "//cda:encounter/cda:performer" : "//cda:documentationOf/cda:serviceEvent/cda:performer"
|
17
|
+
|
18
|
+
performers = doc.xpath(xpath_base)
|
19
|
+
|
20
|
+
providers = performers.map do |performer|
|
21
|
+
provider = {}
|
22
|
+
entity = performer.xpath(performer, "./cda:assignedEntity")
|
23
|
+
name = entity.xpath("./cda:assignedPerson/cda:name")
|
24
|
+
provider[:title] = extract_data(name, "./cda:prefix")
|
25
|
+
provider[:given_name] = extract_data(name, "./cda:given[1]")
|
26
|
+
provider[:family_name] = extract_data(name, "./cda:family")
|
27
|
+
provider[:phone] = extract_data(entity, "./cda:telecom/@value") { |text| text.gsub("tel:", "") }
|
28
|
+
provider[:organization] = extract_data(entity, "./cda:representedOrganization/cda:name")
|
29
|
+
provider[:specialty] = extract_data(entity, "./cda:code/@code")
|
30
|
+
time = performer.xpath(performer, "./cda:time")
|
31
|
+
provider[:start] = extract_date(time, "./cda:low/@value")
|
32
|
+
provider[:end] = extract_date(time, "./cda:high/@value")
|
33
|
+
# NIST sample C32s use different OID for NPI vs C83, support both
|
34
|
+
npi = extract_data(entity, "./cda:id[@root='2.16.840.1.113883.4.6' or @root='2.16.840.1.113883.3.72.5.2']/@extension")
|
35
|
+
if Provider.valid_npi?(npi)
|
36
|
+
provider[:npi] = npi
|
37
|
+
else
|
38
|
+
puts "Warning: Invalid NPI (#{npi})"
|
39
|
+
end
|
40
|
+
provider
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def extract_date(subject,query)
|
47
|
+
date = extract_data(subject,query)
|
48
|
+
date ? Date.parse(date).to_time.to_i : nil
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns nil if result is an empty string, block allows text munging of result if there is one
|
52
|
+
def extract_data(subject, query)
|
53
|
+
result = subject.xpath(query).text
|
54
|
+
if result == ""
|
55
|
+
nil
|
56
|
+
elsif block_given?
|
57
|
+
yield(result)
|
58
|
+
else
|
59
|
+
result
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -136,6 +136,8 @@ module HealthDataStandards
|
|
136
136
|
|
137
137
|
def import_actor(actor_element)
|
138
138
|
actor_hash = {}
|
139
|
+
npi = actor_element.xpath("./cda:assignedEntity/cda:id[@root='2.16.840.1.113883.4.6' or @root='2.16.840.1.113883.3.72.5.2']/@extension")
|
140
|
+
actor_hash[:npi] = npi if npi
|
139
141
|
addresses = actor_element.xpath("./cda:assignedEntity/cda:addr").try(:map) {|ae| import_address(ae)}
|
140
142
|
telecoms = actor_element.xpath("./cda:assignedEntity/cda:telecom").try(:map) {|te| import_telecom(te)}
|
141
143
|
person_element = actor_element.at_xpath("./cda:assignedEntity/cda:assignedPerson")
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Import
|
3
|
+
module CCR
|
4
|
+
# This class is the central location for taking an ASTM CCR 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
|
+
Gender = {"male" => "M", "female" => "F"}
|
14
|
+
|
15
|
+
# Creates a new PatientImporter with the following XPath expressions used to find content in
|
16
|
+
# an ASTM CCR
|
17
|
+
#
|
18
|
+
# Encounter entries
|
19
|
+
# //ccr:Encounters/ccr:Encounter
|
20
|
+
# Procedure entries
|
21
|
+
# //ccr:Procedures/ccr:Procedure
|
22
|
+
#
|
23
|
+
# Result entries -
|
24
|
+
# //ccr:Results/ccr:Result
|
25
|
+
#
|
26
|
+
# Vital sign entries
|
27
|
+
# //ccr:VitalSigns/ccr:Result
|
28
|
+
#
|
29
|
+
# Medication entries
|
30
|
+
# //ccr:Medications/ccr:Medication
|
31
|
+
#
|
32
|
+
# Codes for medications are found in the Product sections
|
33
|
+
# ./ccr:Product
|
34
|
+
#
|
35
|
+
# Condition entries
|
36
|
+
# //ccr:Problems/ccr:Problem
|
37
|
+
#
|
38
|
+
# Social History entries
|
39
|
+
# //ccr:SocialHistory/ccr:SocialHistoryElement
|
40
|
+
#
|
41
|
+
# Care Goal entries
|
42
|
+
# //ccr:Goals/ccr:Goal
|
43
|
+
#
|
44
|
+
# Allergy entries
|
45
|
+
# //ccr:Alerts/ccr:Alert
|
46
|
+
#
|
47
|
+
# Immunization entries
|
48
|
+
# //ccr:Immunizations/ccr:Immunization
|
49
|
+
#
|
50
|
+
# Codes for immunizations are found in the substanceAdministration with the following relative XPath
|
51
|
+
# ./ccr:Product
|
52
|
+
|
53
|
+
def initialize (check_usable = true)
|
54
|
+
@measure_importers = {}
|
55
|
+
@section_importers = {}
|
56
|
+
@section_importers[:encounters] = SimpleImporter.new("//ccr:Encounters/ccr:Encounter",:encounters)
|
57
|
+
@section_importers[:procedures] = SimpleImporter.new("//ccr:Procedures/ccr:Procedure",:procedures)
|
58
|
+
@section_importers[:results] = ResultImporter.new("//ccr:Results/ccr:Result",:results)
|
59
|
+
@section_importers[:vital_signs] = ResultImporter.new("//ccr:VitalSigns/ccr:Result",:vital_signs)
|
60
|
+
@section_importers[:medications] = ProductImporter.new("//ccr:Medications/ccr:Medication", :medications)
|
61
|
+
@section_importers[:conditions] = SimpleImporter.new("//ccr:Problems/ccr:Problem",:conditions)
|
62
|
+
@section_importers[:social_history] = SimpleImporter.new("//ccr:SocialHistory/ccr:SocialHistoryElement", :social_history)
|
63
|
+
@section_importers[:care_goals] = SimpleImporter.new("//ccr:Goals/ccr:Goal",:care_goals)
|
64
|
+
@section_importers[:medical_equipment] = ProductImporter.new("//ccr:Equpment/ccr:EquipmentElement",:medical_equipment)
|
65
|
+
@section_importers[:allergies] = SimpleImporter.new("//ccr:Alerts/ccr:Alert",:allergies)
|
66
|
+
@section_importers[:immunizations] = ProductImporter.new("//ccr:Immunizations/ccr:Immunization",:immunizations)
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# @param [boolean] value for check_usable_entries...importer uses true, stats uses false
|
71
|
+
def check_usable(check_usable_entries)
|
72
|
+
@section_importers.each_pair do |section, importer|
|
73
|
+
importer.check_for_usable = check_usable_entries
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Parses a ASTM CCR document and returns a Hash of of the patient.
|
78
|
+
#
|
79
|
+
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
80
|
+
# will have the "ccr" namespace registered to ""urn:astm-org:CCR""
|
81
|
+
# @return [Hash] a representation of the patient that can be inserted into MongoDB
|
82
|
+
def parse_ccr(doc)
|
83
|
+
ccr_patient = {}
|
84
|
+
entries = create_hash(doc)
|
85
|
+
get_demographics(ccr_patient, doc)
|
86
|
+
process_events(ccr_patient, entries)
|
87
|
+
Record.new(ccr_patient)
|
88
|
+
end
|
89
|
+
#
|
90
|
+
# # Parses a patient hash containing demographic and event information
|
91
|
+
# #
|
92
|
+
# # @param [Hash] patient_hash patient data
|
93
|
+
# # @return [Hash] a representation of the patient that can be inserted into MongoDB
|
94
|
+
# def parse_hash(patient_hash)
|
95
|
+
# patient_record = {}
|
96
|
+
# patient_record['first'] = patient_hash['first']
|
97
|
+
# patient_record['patient_id'] = patient_hash['patient_id']
|
98
|
+
# patient_record['last'] = patient_hash['last']
|
99
|
+
# patient_record['gender'] = patient_hash['gender']
|
100
|
+
# patient_record['patient_id'] = patient_hash['patient_id']
|
101
|
+
# patient_record['birthdate'] = patient_hash['birthdate']
|
102
|
+
# patient_record['race'] = patient_hash['race']
|
103
|
+
# patient_record['ethnicity'] = patient_hash['ethnicity']
|
104
|
+
# patient_record['languages'] = patient_hash['languages']
|
105
|
+
# patient_record['addresses'] = patient_hash['addresses']
|
106
|
+
# event_hash = {}
|
107
|
+
# patient_hash['events'].each do |key, value|
|
108
|
+
# event_hash[key.intern] = parse_events(value)
|
109
|
+
# end
|
110
|
+
# process_events(patient_record, event_hash)
|
111
|
+
# end
|
112
|
+
|
113
|
+
# Adds the entries and denormalized measure information to the patient_record.
|
114
|
+
# Each Entry will be converted to a Hash and stored in an Array under the appropriate
|
115
|
+
# section key, such as medications. Measure information is listed under the measures
|
116
|
+
# key which has a Hash value. The Hash has the measure id as a key, and the denormalized
|
117
|
+
# measure information as a value
|
118
|
+
#
|
119
|
+
# @param patient_record - Hash with basic patient demographic information
|
120
|
+
# @entries - Hash of entries with section names a keys and an Array of Entry values
|
121
|
+
def process_events(patient_record, entries)
|
122
|
+
patient_record['measures'] = {}
|
123
|
+
@measure_importers.each_pair do |measure_id, importer|
|
124
|
+
patient_record['measures'][measure_id] = importer.parse(entries)
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
entries.each_pair do |key, value|
|
129
|
+
patient_record[key] = value.map do |e|
|
130
|
+
if e.usable?
|
131
|
+
e.to_hash
|
132
|
+
else
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
end.compact
|
136
|
+
end
|
137
|
+
patient_record
|
138
|
+
end
|
139
|
+
|
140
|
+
# # Parses a list of event hashes into an array of Entry objects
|
141
|
+
# #
|
142
|
+
# # @param [Array] event_list list of event hashes
|
143
|
+
# # @return [Array] array of Entry objects
|
144
|
+
# def parse_events(event_list)
|
145
|
+
# event_list.collect do |event|
|
146
|
+
# if event.class==String.class
|
147
|
+
# # skip String elements in the event list, patient randomization templates
|
148
|
+
# # introduce String elements to simplify tailing-comma handling when generating
|
149
|
+
# # JSON using ERb
|
150
|
+
# nil
|
151
|
+
# else
|
152
|
+
# QME::Importer::Entry.from_event_hash(event)
|
153
|
+
# end
|
154
|
+
# end.compact
|
155
|
+
# end
|
156
|
+
|
157
|
+
# Adds a measure to run on a CCR that is passed in
|
158
|
+
#
|
159
|
+
# @param [MeasureBase] measure an Class that can extract information from a CCR that is necessary
|
160
|
+
# to calculate the measure
|
161
|
+
def add_measure(measure_id, importer)
|
162
|
+
@measure_importers[measure_id] = importer
|
163
|
+
end
|
164
|
+
|
165
|
+
# Create a simple representation of the patient from an ASTM CCR
|
166
|
+
#
|
167
|
+
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
168
|
+
# will have the "ccr" namespace registered to ""urn:astm-org:CCR""
|
169
|
+
# @return [Hash] a represnetation of the patient with symbols as keys for each section
|
170
|
+
def create_hash(doc, check_usable_entries = false)
|
171
|
+
ccr_patient = {}
|
172
|
+
@section_importers.each_pair do |section, importer|
|
173
|
+
importer.check_for_usable = check_usable_entries
|
174
|
+
ccr_patient[section] = importer.create_entries(doc)
|
175
|
+
end
|
176
|
+
ccr_patient
|
177
|
+
end
|
178
|
+
|
179
|
+
# Inspects a CCR document and populates the patient Hash with first name, last name
|
180
|
+
# birth date and gender.
|
181
|
+
#
|
182
|
+
# @param [Hash] patient A hash that is used to represent the patient
|
183
|
+
# @param [Nokogiri::XML::Node] doc The CCR document parsed by Nokogiri
|
184
|
+
def get_demographics(patient, doc)
|
185
|
+
patientID = doc.at_xpath('//ccr:ContinuityOfCareRecord/ccr:Patient/ccr:ActorID').content
|
186
|
+
patientActor = doc.at_xpath("//ccr:ContinuityOfCareRecord/ccr:Actors/ccr:Actor[ccr:ActorObjectID = \"#{patientID}\"]")
|
187
|
+
patient['first'] = patientActor.at_xpath('./ccr:Person/ccr:Name/ccr:CurrentName/ccr:Given').content
|
188
|
+
patient['last'] = patientActor.at_xpath('./ccr:Person/ccr:Name/ccr:CurrentName/ccr:Family').content
|
189
|
+
birthdate = patientActor.at_xpath('./ccr:Person//ccr:DateOfBirth/ccr:ExactDateTime | ./ccr:Person//ccr:DateOfBirth/ccr:ApproximateDateTime')
|
190
|
+
patient['birthdate'] = Time.iso8601(birthdate).to_i
|
191
|
+
|
192
|
+
gender_string = patientActor.at_xpath('./ccr:Person/ccr:Gender/ccr:Text').content.downcase
|
193
|
+
patient['gender'] = Gender[gender_string.downcase]
|
194
|
+
|
195
|
+
#race_node = doc.at_xpath('/ccr:placeholder') #how do you find this?
|
196
|
+
patient['race'] = nil
|
197
|
+
#ethnicity_node = doc.at_xpath()
|
198
|
+
patient['ethnicity'] = nil
|
199
|
+
|
200
|
+
# languages = doc.at_xpath()
|
201
|
+
patient['languages'] = nil
|
202
|
+
|
203
|
+
patient['medical_record_number'] = patientID
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|