health-data-standards 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -2
- data/Rakefile +6 -1
- data/VERSION +1 -1
- data/lib/health-data-standards.rb +27 -2
- data/lib/health-data-standards/export/view_helper.rb +15 -16
- data/lib/health-data-standards/ext/string.rb +5 -0
- data/lib/health-data-standards/ext/symbol.rb +8 -0
- data/lib/health-data-standards/import/c32/allergy_importer.rb +42 -0
- data/lib/health-data-standards/import/c32/encounter_importer.rb +80 -0
- data/lib/health-data-standards/import/c32/immunization_importer.rb +61 -0
- data/lib/health-data-standards/import/c32/medication_importer.rb +138 -0
- data/lib/health-data-standards/import/c32/patient_importer.rb +139 -0
- data/lib/health-data-standards/import/c32/procedure_importer.rb +55 -0
- data/lib/health-data-standards/import/c32/result_importer.rb +58 -0
- data/lib/health-data-standards/import/c32/section_importer.rb +214 -0
- data/lib/health-data-standards/import/c32/vital_sign_importer.rb +12 -0
- data/lib/health-data-standards/models/allergy.rb +4 -0
- data/lib/health-data-standards/models/encounter.rb +7 -0
- data/lib/health-data-standards/models/entry.rb +131 -3
- data/lib/health-data-standards/models/fulfillment_history.rb +11 -0
- data/lib/health-data-standards/models/immunization.rb +5 -0
- data/lib/health-data-standards/models/lab_result.rb +4 -0
- data/lib/health-data-standards/models/medication.rb +25 -0
- data/lib/health-data-standards/models/order_information.rb +9 -0
- data/lib/health-data-standards/models/procedure.rb +4 -0
- data/lib/health-data-standards/models/record.rb +1 -0
- data/lib/health-data-standards/util/code_system_helper.rb +41 -0
- data/lib/health-data-standards/util/hl7_helper.rb +25 -0
- data/templates/_allergies.c32.erb +3 -1
- data/templates/_care_goals.c32.erb +1 -1
- data/templates/_code_with_reference.c32.erb +10 -5
- data/templates/_conditions.c32.erb +2 -2
- data/templates/_encounters.c32.erb +2 -1
- data/templates/_immunizations.c32.erb +2 -1
- data/templates/_medical_equipment.c32.erb +1 -1
- data/templates/_medications.c32.erb +2 -1
- data/templates/_narrative_block.c32.erb +14 -0
- data/templates/_procedures.c32.erb +2 -1
- data/templates/_results.c32.erb +2 -2
- data/templates/_social_history.c32.erb +1 -1
- data/templates/_vital_signs.c32.erb +2 -2
- metadata +37 -16
@@ -0,0 +1,139 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Import
|
3
|
+
module C32
|
4
|
+
|
5
|
+
# This class is the central location for taking a HITSP C32 XML document and converting it
|
6
|
+
# into the processed form we store in MongoDB. The class does this by running each measure
|
7
|
+
# independently on the XML document
|
8
|
+
#
|
9
|
+
# This class is a Singleton. It should be accessed by calling PatientImporter.instance
|
10
|
+
class PatientImporter
|
11
|
+
|
12
|
+
include Singleton
|
13
|
+
include HealthDataStandards::Util
|
14
|
+
|
15
|
+
# Creates a new PatientImporter with the following XPath expressions used to find content in
|
16
|
+
# a HITSP C32:
|
17
|
+
#
|
18
|
+
# Encounter entries
|
19
|
+
# //cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.127']/cda:entry/cda:encounter
|
20
|
+
#
|
21
|
+
# Procedure entries
|
22
|
+
# //cda:procedure[cda:templateId/@root='2.16.840.1.113883.10.20.1.29']
|
23
|
+
#
|
24
|
+
# Result entries - There seems to be some confusion around the correct templateId, so the code checks for both
|
25
|
+
# //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']
|
26
|
+
#
|
27
|
+
# Vital sign entries
|
28
|
+
# //cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.14']
|
29
|
+
#
|
30
|
+
# Medication entries
|
31
|
+
# //cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.112']/cda:entry/cda:substanceAdministration
|
32
|
+
#
|
33
|
+
# Codes for medications are found in the substanceAdministration with the following relative XPath
|
34
|
+
# ./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code
|
35
|
+
#
|
36
|
+
# Condition entries
|
37
|
+
# //cda:section[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.103']/cda:entry/cda:act/cda:entryRelationship/cda:observation
|
38
|
+
#
|
39
|
+
# Codes for conditions are determined by examining the value child element as opposed to the code child element
|
40
|
+
#
|
41
|
+
# Social History entries (non-C32 section, specified in the HL7 CCD)
|
42
|
+
# //cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.19']
|
43
|
+
#
|
44
|
+
# Care Goal entries(non-C32 section, specified in the HL7 CCD)
|
45
|
+
# //cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.25']
|
46
|
+
#
|
47
|
+
# Allergy entries
|
48
|
+
# //cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.18']
|
49
|
+
#
|
50
|
+
# Immunization entries
|
51
|
+
# //cda:substanceAdministration[cda:templateId/@root='2.16.840.1.113883.10.20.1.24']
|
52
|
+
#
|
53
|
+
# Codes for immunizations are found in the substanceAdministration with the following relative XPath
|
54
|
+
# ./cda:consumable/cda:manufacturedProduct/cda:manufacturedMaterial/cda:code
|
55
|
+
def initialize(check_usable = true)
|
56
|
+
@section_importers = {}
|
57
|
+
@section_importers[:encounters] = EncounterImporter.new
|
58
|
+
@section_importers[:procedures] = ProcedureImporter.new
|
59
|
+
@section_importers[:results] = ResultImporter.new
|
60
|
+
@section_importers[:vital_signs] = VitalSignImporter.new
|
61
|
+
@section_importers[:medications] = MedicationImporter.new
|
62
|
+
@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",
|
63
|
+
"./cda:value",
|
64
|
+
"./cda:entryRelationship/cda:observation[cda:templateId/@root='2.16.840.1.1 13883.10.20.1.50']/cda:value",
|
65
|
+
"./cda:text/cda:reference[@value]")
|
66
|
+
@section_importers[:social_history] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.3.88.11.83.19']")
|
67
|
+
@section_importers[:care_goals] = SectionImporter.new("//cda:observation[cda:templateId/@root='2.16.840.1.113883.10.20.1.25']")
|
68
|
+
@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",
|
69
|
+
"./cda:participant/cda:participantRole/cda:playingDevice/cda:code")
|
70
|
+
@section_importers[:allergies] = AllergyImporter.new
|
71
|
+
@section_importers[:immunizations] = ImmunizationImporter.new
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_id_map(doc)
|
75
|
+
id_map = {}
|
76
|
+
path = "//*[@ID]"
|
77
|
+
ids = doc.xpath(path)
|
78
|
+
ids.each do |id|
|
79
|
+
tag = id['ID']
|
80
|
+
value = id.content
|
81
|
+
id_map[tag] = value
|
82
|
+
end
|
83
|
+
|
84
|
+
id_map
|
85
|
+
end
|
86
|
+
|
87
|
+
# @param [boolean] value for check_usable_entries...importer uses true, stats uses false
|
88
|
+
def check_usable(check_usable_entries)
|
89
|
+
@section_importers.each_pair do |section, importer|
|
90
|
+
importer.check_for_usable = check_usable_entries
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Parses a HITSP C32 document and returns a Hash of of the patient.
|
95
|
+
#
|
96
|
+
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
97
|
+
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
98
|
+
# @return [Record] a Mongoid model representing the patient
|
99
|
+
def parse_c32(doc)
|
100
|
+
c32_patient = Record.new
|
101
|
+
get_demographics(c32_patient, doc)
|
102
|
+
create_c32_hash(c32_patient, doc)
|
103
|
+
|
104
|
+
c32_patient
|
105
|
+
end
|
106
|
+
|
107
|
+
# Create a simple representation of the patient from a HITSP C32
|
108
|
+
# @param [Record] record Mongoid model to append the Entry objects to
|
109
|
+
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
110
|
+
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
111
|
+
# @return [Hash] a represnetation of the patient with symbols as keys for each section
|
112
|
+
def create_c32_hash(record, doc)
|
113
|
+
id_map = build_id_map(doc)
|
114
|
+
@section_importers.each_pair do |section, importer|
|
115
|
+
record.send(section.to_setter, importer.create_entries(doc, id_map))
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Inspects a C32 document and populates the patient Hash with first name, last name
|
120
|
+
# birth date and gender.
|
121
|
+
#
|
122
|
+
# @param [Hash] patient A hash that is used to represent the patient
|
123
|
+
# @param [Nokogiri::XML::Node] doc The C32 document parsed by Nokogiri
|
124
|
+
def get_demographics(patient, doc)
|
125
|
+
patient_element = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:patient')
|
126
|
+
patient.first = patient_element.at_xpath('cda:name/cda:given').text
|
127
|
+
patient.last = patient_element.at_xpath('cda:name/cda:family').text
|
128
|
+
birthdate_in_hl7ts_node = patient_element.at_xpath('cda:birthTime')
|
129
|
+
birthdate_in_hl7ts = birthdate_in_hl7ts_node['value']
|
130
|
+
patient.birthdate = HL7Helper.timestamp_to_integer(birthdate_in_hl7ts)
|
131
|
+
gender_node = patient_element.at_xpath('cda:administrativeGenderCode')
|
132
|
+
patient.gender = gender_node['code']
|
133
|
+
id_node = doc.at_xpath('/cda:ClinicalDocument/cda:recordTarget/cda:patientRole/cda:id')
|
134
|
+
patient.medical_record_number = id_node['extension']
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Import
|
3
|
+
module C32
|
4
|
+
|
5
|
+
# TODO Extract Discharge Disposition
|
6
|
+
class ProcedureImporter < SectionImporter
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@entry_xpath = "//cda:procedure[cda:templateId/@root='2.16.840.1.113883.10.20.1.29']"
|
10
|
+
@code_xpath = "./cda:code"
|
11
|
+
@status_xpath = "./cda:statusCode"
|
12
|
+
@description_xpath = "./cda:code/cda:originalText/cda:reference[@value] | ./cda:text/cda:reference[@value] "
|
13
|
+
@check_for_usable = true # Pilot tools will set this to false
|
14
|
+
end
|
15
|
+
|
16
|
+
# Traverses that HITSP C32 document passed in using XPath and creates an Array of Entry
|
17
|
+
# objects based on what it finds
|
18
|
+
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
19
|
+
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
20
|
+
# measure definition
|
21
|
+
# @return [Array] will be a list of Entry objects
|
22
|
+
def create_entries(doc,id_map = {})
|
23
|
+
procedure_list = []
|
24
|
+
entry_elements = doc.xpath(@entry_xpath)
|
25
|
+
entry_elements.each do |entry_element|
|
26
|
+
procedure = Procedure.new
|
27
|
+
extract_codes(entry_element, procedure)
|
28
|
+
extract_dates(entry_element, procedure)
|
29
|
+
extract_description(entry_element, procedure, id_map)
|
30
|
+
if @check_for_usable
|
31
|
+
procedure_list << procedure if procedure.usable?
|
32
|
+
else
|
33
|
+
procedure_list << procedure
|
34
|
+
end
|
35
|
+
extract_performer(entry_element, procedure)
|
36
|
+
extract_site(entry_element, procedure)
|
37
|
+
end
|
38
|
+
procedure_list
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def extract_performer(parent_element, procedure)
|
44
|
+
performer_element = parent_element.at_xpath("./cda:performer")
|
45
|
+
procedure.performer = import_actor(performer_element) if performer_element
|
46
|
+
end
|
47
|
+
|
48
|
+
def extract_site(parent_element, procedure)
|
49
|
+
procedure.site = extract_code(parent_element, "./cda:targetSiteCode")
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Import
|
3
|
+
module C32
|
4
|
+
class ResultImporter < SectionImporter
|
5
|
+
def initialize
|
6
|
+
@entry_xpath = "//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']"
|
7
|
+
@code_xpath = "./cda:code"
|
8
|
+
@status_xpath = "./cda:statusCode"
|
9
|
+
@description_xpath = "./cda:code/cda:originalText/cda:reference[@value] | ./cda:text/cda:reference[@value] "
|
10
|
+
@check_for_usable = true # Pilot tools will set this to false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Traverses that HITSP C32 document passed in using XPath and creates an Array of Entry
|
14
|
+
# objects based on what it finds
|
15
|
+
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
16
|
+
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
17
|
+
# measure definition
|
18
|
+
# @return [Array] will be a list of Entry objects
|
19
|
+
def create_entries(doc,id_map = {})
|
20
|
+
result_list = []
|
21
|
+
entry_elements = doc.xpath(@entry_xpath)
|
22
|
+
entry_elements.each do |entry_element|
|
23
|
+
result = LabResult.new
|
24
|
+
extract_codes(entry_element, result)
|
25
|
+
extract_dates(entry_element, result)
|
26
|
+
extract_value(entry_element, result)
|
27
|
+
extract_status(entry_element, result)
|
28
|
+
extract_description(entry_element, result, id_map)
|
29
|
+
extract_interpretation(entry_element, result)
|
30
|
+
if @check_for_usable
|
31
|
+
result_list << result if result.usable?
|
32
|
+
else
|
33
|
+
result_list << result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
result_list
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def extract_interpretation(parent_element, result)
|
41
|
+
interpretation_element = parent_element.at_xpath("./cda:interpretationCode")
|
42
|
+
if interpretation_element
|
43
|
+
code = interpretation_element['code']
|
44
|
+
code_system = CodeSystemHelper.code_system_for(interpretation_element['codeSystem'])
|
45
|
+
result.interpretation = {'code' => code, 'codeSystem' => code_system}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def extract_status(parent_element, result)
|
50
|
+
status_code_element = parent_element.at_xpath(@status_xpath)
|
51
|
+
if status_code_element
|
52
|
+
result.status = status_code_element['code']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Import
|
3
|
+
module C32
|
4
|
+
# Class that can be used to create an importer for a section of a HITSP C32 document. It usually
|
5
|
+
# operates by selecting all CDA entries in a section and then creates entries for them.
|
6
|
+
class SectionImporter
|
7
|
+
include HealthDataStandards::Util
|
8
|
+
|
9
|
+
attr_accessor :check_for_usable
|
10
|
+
# Creates a new SectionImporter
|
11
|
+
# @param [Hash] id_map A hash of all ID tags to values for the enclosing document. Used to look up descriptions.
|
12
|
+
# @param [String] entry_xpath An XPath expression that can be used to find the desired entries
|
13
|
+
# @param [String] code_xpath XPath expression to find the code element as a child of the desired CDA entry.
|
14
|
+
# Defaults to "./cda:code"
|
15
|
+
# @param [String] status_xpath XPath expression to find the status element as a child of the desired CDA
|
16
|
+
# entry. Defaults to nil. If not provided, a status will not be checked for since it is not applicable
|
17
|
+
# to all enrty types
|
18
|
+
def initialize(entry_xpath, code_xpath="./cda:code", status_xpath=nil, description_xpath="./cda:code/cda:originalText/cda:reference[@value] | ./cda:text/cda:reference[@value] ")
|
19
|
+
@entry_xpath = entry_xpath
|
20
|
+
@code_xpath = code_xpath
|
21
|
+
@status_xpath = status_xpath
|
22
|
+
@description_xpath = description_xpath
|
23
|
+
@check_for_usable = true # Pilot tools will set this to false
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [String] tag
|
27
|
+
# @param [Hash] id_map A map of ids to all tagged text in the narrative portion of a document
|
28
|
+
# @return [String] text description of tag
|
29
|
+
def lookup_tag(tag, id_map)
|
30
|
+
value = id_map[tag]
|
31
|
+
# Not sure why, but sometimes the reference is #<Reference> and the ID value is <Reference>, and
|
32
|
+
# sometimes it is #<Reference>. We look for both.
|
33
|
+
if !value and tag[0] == '#'
|
34
|
+
tag = tag[1,tag.length]
|
35
|
+
value = id_map[tag]
|
36
|
+
end
|
37
|
+
|
38
|
+
value
|
39
|
+
end
|
40
|
+
|
41
|
+
# Traverses that HITSP C32 document passed in using XPath and creates an Array of Entry
|
42
|
+
# objects based on what it finds
|
43
|
+
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
44
|
+
# will have the "cda" namespace registered to "urn:hl7-org:v3"
|
45
|
+
# measure definition
|
46
|
+
# @return [Array] will be a list of Entry objects
|
47
|
+
def create_entries(doc,id_map = {})
|
48
|
+
entry_list = []
|
49
|
+
entry_elements = doc.xpath(@entry_xpath)
|
50
|
+
entry_elements.each do |entry_element|
|
51
|
+
entry = Entry.new
|
52
|
+
extract_codes(entry_element, entry)
|
53
|
+
extract_dates(entry_element, entry)
|
54
|
+
extract_value(entry_element, entry)
|
55
|
+
if @status_xpath
|
56
|
+
extract_status(entry_element, entry)
|
57
|
+
end
|
58
|
+
if @description_xpath
|
59
|
+
extract_description(entry_element, entry, id_map)
|
60
|
+
end
|
61
|
+
if @check_for_usable
|
62
|
+
entry_list << entry if entry.usable?
|
63
|
+
else
|
64
|
+
entry_list << entry
|
65
|
+
end
|
66
|
+
end
|
67
|
+
entry_list
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def extract_status(parent_element, entry)
|
73
|
+
status_element = parent_element.at_xpath(@status_xpath)
|
74
|
+
if status_element
|
75
|
+
case status_element['code']
|
76
|
+
when '55561003'
|
77
|
+
entry.status = :active
|
78
|
+
when '73425007'
|
79
|
+
entry.status = :inactive
|
80
|
+
when '413322009'
|
81
|
+
entry.status = :resolved
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def extract_description(parent_element, entry, id_map)
|
87
|
+
code_elements = parent_element.xpath(@description_xpath)
|
88
|
+
code_elements.each do |code_element|
|
89
|
+
tag = code_element['value']
|
90
|
+
entry.description = lookup_tag(tag, id_map)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def extract_codes(parent_element, entry)
|
95
|
+
code_elements = parent_element.xpath(@code_xpath)
|
96
|
+
code_elements.each do |code_element|
|
97
|
+
add_code_if_present(code_element, entry)
|
98
|
+
translations = code_element.xpath('cda:translation')
|
99
|
+
translations.each do |translation|
|
100
|
+
add_code_if_present(translation, entry)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_code_if_present(code_element, entry)
|
106
|
+
if code_element['codeSystem'] && code_element['code']
|
107
|
+
entry.add_code(code_element['code'], CodeSystemHelper.code_system_for(code_element['codeSystem']))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def extract_dates(parent_element, entry)
|
112
|
+
if parent_element.at_xpath('cda:effectiveTime')
|
113
|
+
entry.time = HL7Helper.timestamp_to_integer(parent_element.at_xpath('cda:effectiveTime')['value'])
|
114
|
+
end
|
115
|
+
if parent_element.at_xpath('cda:effectiveTime/cda:low')
|
116
|
+
entry.start_time = HL7Helper.timestamp_to_integer(parent_element.at_xpath('cda:effectiveTime/cda:low')['value'])
|
117
|
+
end
|
118
|
+
if parent_element.at_xpath('cda:effectiveTime/cda:high')
|
119
|
+
entry.end_time = HL7Helper.timestamp_to_integer(parent_element.at_xpath('cda:effectiveTime/cda:high')['value'])
|
120
|
+
end
|
121
|
+
if parent_element.at_xpath('cda:effectiveTime/cda:center')
|
122
|
+
entry.time = HL7Helper.timestamp_to_integer(parent_element.at_xpath('cda:effectiveTime/cda:center')['value'])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def extract_value(parent_element, entry)
|
127
|
+
value_element = parent_element.at_xpath('cda:value')
|
128
|
+
if value_element
|
129
|
+
value = value_element['value']
|
130
|
+
unit = value_element['unit']
|
131
|
+
if value
|
132
|
+
entry.set_value(value, unit)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def import_actor(actor_element)
|
138
|
+
actor_hash = {}
|
139
|
+
addresses = actor_element.xpath("./cda:assignedEntity/cda:addr").try(:map) {|ae| import_address(ae)}
|
140
|
+
telecoms = actor_element.xpath("./cda:assignedEntity/cda:telecom").try(:map) {|te| import_telecom(te)}
|
141
|
+
person_element = actor_element.at_xpath("./cda:assignedEntity/cda:assignedPerson")
|
142
|
+
if person_element
|
143
|
+
actor_hash['person'] = import_person(person_element)
|
144
|
+
actor_hash['person']['addresses'] = addresses
|
145
|
+
actor_hash['person']['telecoms'] = telecoms
|
146
|
+
end
|
147
|
+
organization_element = actor_element.at_xpath("./cda:assignedEntity/cda:assignedOrganization")
|
148
|
+
if organization_element
|
149
|
+
actor_hash['organization'] = import_organization(organization_element)
|
150
|
+
end
|
151
|
+
|
152
|
+
actor_hash
|
153
|
+
end
|
154
|
+
|
155
|
+
def import_person(person_element)
|
156
|
+
person_hash = {}
|
157
|
+
name_element = person_element.at_xpath("./cda:name")
|
158
|
+
person_hash['name'] = name_element.try(:text)
|
159
|
+
person_hash['first'] = name_element.at_xpath("./cda:given").try(:text)
|
160
|
+
person_hash['last'] = name_element.at_xpath("./cda:family").try(:text)
|
161
|
+
person_hash
|
162
|
+
end
|
163
|
+
|
164
|
+
def import_address(address_element)
|
165
|
+
address_hash = {}
|
166
|
+
address_hash['streetAddress'] = [address_element.at_xpath("./cda:streetAddressLine").try(:text)]
|
167
|
+
address_hash['city'] = address_element.at_xpath("./cda:city").try(:text)
|
168
|
+
address_hash['stateOrProvince'] = address_element.at_xpath("./cda:state").try(:text)
|
169
|
+
address_hash['zip'] = address_element.at_xpath("./cda:postalCode").try(:text)
|
170
|
+
address_hash['country'] = address_element.at_xpath("./cda:country").try(:text)
|
171
|
+
address_hash
|
172
|
+
end
|
173
|
+
|
174
|
+
def import_telecom(telecom_element)
|
175
|
+
telecom_hash = {}
|
176
|
+
telecom_hash['value'] = telecom_element['value']
|
177
|
+
telecom_hash['use'] = telecom_element['use']
|
178
|
+
telecom_hash
|
179
|
+
end
|
180
|
+
|
181
|
+
def import_organization
|
182
|
+
# TODO: Implement when the Patient API has an implementation of
|
183
|
+
# organization
|
184
|
+
end
|
185
|
+
|
186
|
+
def extract_code(parent_element, code_xpath, code_system=nil)
|
187
|
+
code_element = parent_element.at_xpath(code_xpath)
|
188
|
+
code_hash = nil
|
189
|
+
if code_element
|
190
|
+
code_hash = {'code' => code_element['code']}
|
191
|
+
if code_system
|
192
|
+
code_hash['codeSystem'] = code_system
|
193
|
+
else
|
194
|
+
code_hash['codeSystemOid'] = code_element['codeSystem']
|
195
|
+
code_hash['codeSystem'] = CodeSystemHelper.code_system_for(code_hash['codeSystemOid'])
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
code_hash
|
200
|
+
end
|
201
|
+
|
202
|
+
def extract_scalar(parent_element, scalar_xpath)
|
203
|
+
scalar_element = parent_element.at_xpath(scalar_xpath)
|
204
|
+
if scalar_element
|
205
|
+
{'unit' => scalar_element['unit'], 'value' => scalar_element['value'].to_i}
|
206
|
+
else
|
207
|
+
nil
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|