health-data-standards 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|