health-data-standards 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|