health-data-standards 3.5.3 → 3.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -3
- data/README.md +10 -2
- data/lib/health-data-standards.rb +5 -28
- data/lib/health-data-standards/export/cat_1.rb +3 -2
- data/lib/health-data-standards/export/cat_1_r2.rb +11 -0
- data/lib/health-data-standards/ext/node.rb +1 -2
- data/lib/health-data-standards/import/bulk_record_importer.rb +4 -4
- data/lib/health-data-standards/import/bundle/importer.rb +62 -0
- data/lib/health-data-standards/import/c32/insurance_provider_importer.rb +10 -8
- data/lib/health-data-standards/import/cat1/patient_importer.rb +3 -3
- data/lib/health-data-standards/import/cat1/procedure_importer.rb +42 -0
- data/lib/health-data-standards/import/cda/medication_importer.rb +11 -11
- data/lib/health-data-standards/import/cda/section_importer.rb +13 -7
- data/lib/health-data-standards/models/cqm/aggregate_objects.rb +5 -1
- data/lib/health-data-standards/models/cqm/bundle.rb +5 -3
- data/lib/health-data-standards/models/cqm/measure.rb +1 -1
- data/lib/health-data-standards/models/entry.rb +5 -1
- data/lib/health-data-standards/models/record.rb +10 -0
- data/lib/health-data-standards/models/reference.rb +23 -0
- data/lib/health-data-standards/models/svs/value_set.rb +4 -3
- data/lib/health-data-standards/railtie.rb +1 -1
- data/lib/health-data-standards/tasks.rb +1 -0
- data/lib/health-data-standards/tasks/bundle.rake +84 -2
- data/lib/health-data-standards/util/vs_api.rb +40 -7
- data/lib/health-data-standards/validate/base_validator.rb +23 -0
- data/lib/health-data-standards/validate/data_validator.rb +85 -0
- data/lib/health-data-standards/validate/measure_validator.rb +127 -0
- data/lib/health-data-standards/validate/performance_rate_validator.rb +94 -0
- data/lib/health-data-standards/validate/reported_result_extractor.rb +170 -0
- data/lib/health-data-standards/validate/schema_validator.rb +24 -0
- data/lib/health-data-standards/validate/schematron/c_processor.rb +28 -0
- data/lib/health-data-standards/validate/schematron/java_processor.rb +93 -0
- data/lib/health-data-standards/validate/schematron_validator.rb +34 -0
- data/lib/health-data-standards/validate/validation_error.rb +10 -0
- data/lib/health-data-standards/validate/validators.rb +80 -0
- data/lib/hqmf-generator/fulfills.xml.erb +7 -0
- data/lib/hqmf-generator/hqmf-generator.rb +8 -3
- data/lib/hqmf-model/data_criteria.rb +3 -0
- data/lib/hqmf-model/types.rb +29 -0
- data/lib/hqmf-parser/2.0/data_criteria.rb +7 -0
- data/lib/hqmf-parser/2.0/types.rb +24 -0
- data/resources/schema/infrastructure/cda/CDA_SDTC.xsd +44 -0
- data/resources/schema/infrastructure/cda/POCD_MT000040_SDTC.xsd +1500 -0
- data/resources/schema/infrastructure/cda/SDTC.xsd +210 -0
- data/resources/schema/processable/coreschemas/NarrativeBlock.xsd +557 -0
- data/resources/schema/processable/coreschemas/datatypes-base_SDTC.xsd +1850 -0
- data/resources/schema/processable/coreschemas/datatypes.xsd +1375 -0
- data/resources/schema/processable/coreschemas/infrastructureRoot.xsd +27 -0
- data/resources/schema/processable/coreschemas/voc.xsd +2124 -0
- data/resources/schematron/iso-schematron-xslt1/ExtractSchFromRNG.xsl +75 -0
- data/resources/schematron/iso-schematron-xslt1/ExtractSchFromXSD.xsl +77 -0
- data/resources/schematron/iso-schematron-xslt1/iso_abstract_expand.xsl +297 -0
- data/resources/schematron/iso-schematron-xslt1/iso_dsdl_include.xsl +1509 -0
- data/resources/schematron/iso-schematron-xslt1/iso_schematron_message.xsl +55 -0
- data/resources/schematron/iso-schematron-xslt1/iso_schematron_skeleton_for_xslt1.xsl +1844 -0
- data/resources/schematron/iso-schematron-xslt1/iso_svrl_for_xslt1.xsl +605 -0
- data/resources/schematron/iso-schematron-xslt1/readme.txt +101 -0
- data/resources/schematron/iso-schematron-xslt1/schematron-skeleton-api.htm +723 -0
- data/resources/schematron/iso-schematron-xslt2/ExtractSchFromRNG-2.xsl +75 -0
- data/resources/schematron/iso-schematron-xslt2/ExtractSchFromXSD-2.xsl +77 -0
- data/resources/schematron/iso-schematron-xslt2/iso_abstract_expand.xsl +297 -0
- data/resources/schematron/iso-schematron-xslt2/iso_dsdl_include.xsl +1508 -0
- data/resources/schematron/iso-schematron-xslt2/iso_schematron_message_xslt2.xsl +55 -0
- data/resources/schematron/iso-schematron-xslt2/iso_schematron_skeleton_for_saxon.xsl +2299 -0
- data/resources/schematron/iso-schematron-xslt2/iso_svrl_for_xslt2.xsl +684 -0
- data/resources/schematron/iso-schematron-xslt2/readme.txt +100 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-cs.xhtml +56 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-de.xhtml +57 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-en.xhtml +57 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-fr.xhtml +54 -0
- data/resources/schematron/iso-schematron-xslt2/sch-messages-nl.xhtml +58 -0
- data/resources/schematron/iso-schematron-xslt2/schematron-skeleton-api.htm +723 -0
- data/resources/schematron/qrda/cat_1/CDAR2_QRDA_I_R1_D3_2015MAY_Schematron.sch +4676 -0
- data/resources/schematron/qrda/cat_1/voc.xml +1177 -0
- data/resources/schematron/qrda/cat_1_r2/QRDA Category I Release 2.sch +4069 -0
- data/resources/schematron/qrda/cat_1_r2/voc.xml +1065 -0
- data/resources/schematron/qrda/cat_3/QRDA Category III.sch +675 -0
- data/resources/schematron/qrda/cat_3/voc.xml +21 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.26.cat1.erb +18 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.32.cat1.erb +4 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.38.cat1.erb +5 -1
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.4.cat1.erb +1 -0
- data/templates/cat1/_2.16.840.1.113883.10.20.24.3.64.cat1.erb +20 -0
- data/templates/cat1/_fulfills.cat1.erb +14 -0
- data/templates/cat1/_organization.cat1.erb +2 -1
- data/templates/cat1/_patient_data.cat1.erb +1 -1
- data/templates/cat1/show.cat1.erb +5 -4
- data/templates/cat3/_performance_rate.cat3.erb +5 -1
- data/templates/cat3/show.cat3.erb +1 -1
- metadata +128 -109
- data/lib/health-data-standards/export/ccr.rb +0 -417
- data/lib/health-data-standards/export/green_c32/entry.rb +0 -18
- data/lib/health-data-standards/export/green_c32/export_generator.rb +0 -23
- data/lib/health-data-standards/export/green_c32/record.rb +0 -18
- data/lib/health-data-standards/export/helper/gc32_view_helper.rb +0 -39
- data/lib/health-data-standards/import/ccr/patient_importer.rb +0 -238
- data/lib/health-data-standards/import/ccr/product_importer.rb +0 -60
- data/lib/health-data-standards/import/ccr/provider_importer.rb +0 -49
- data/lib/health-data-standards/import/ccr/result_importer.rb +0 -49
- data/lib/health-data-standards/import/ccr/section_importer.rb +0 -135
- data/lib/health-data-standards/import/ccr/simple_importer.rb +0 -30
- data/lib/health-data-standards/import/green_c32/advance_directive_importer.rb +0 -14
- data/lib/health-data-standards/import/green_c32/allergy_importer.rb +0 -20
- data/lib/health-data-standards/import/green_c32/care_goal_importer.rb +0 -26
- data/lib/health-data-standards/import/green_c32/condition_importer.rb +0 -38
- data/lib/health-data-standards/import/green_c32/encounter_importer.rb +0 -33
- data/lib/health-data-standards/import/green_c32/immunization_importer.rb +0 -23
- data/lib/health-data-standards/import/green_c32/medical_equipment_importer.rb +0 -24
- data/lib/health-data-standards/import/green_c32/medication_importer.rb +0 -68
- data/lib/health-data-standards/import/green_c32/patient_importer.rb +0 -14
- data/lib/health-data-standards/import/green_c32/procedure_importer.rb +0 -27
- data/lib/health-data-standards/import/green_c32/result_importer.rb +0 -43
- data/lib/health-data-standards/import/green_c32/section_importer.rb +0 -186
- data/lib/health-data-standards/import/green_c32/social_history_importer.rb +0 -13
- data/lib/health-data-standards/import/green_c32/support_importer.rb +0 -22
- data/lib/health-data-standards/import/green_c32/vital_sign_importer.rb +0 -21
- data/templates/gc32/_address.gc32.erb +0 -9
- data/templates/gc32/_advance_directive.gc32.erb +0 -5
- data/templates/gc32/_allergy.gc32.erb +0 -12
- data/templates/gc32/_care_goal.gc32.erb +0 -8
- data/templates/gc32/_condition.gc32.erb +0 -10
- data/templates/gc32/_encounter.gc32.erb +0 -28
- data/templates/gc32/_entry.gc32.erb +0 -3
- data/templates/gc32/_entry_attributes.gc32.erb +0 -10
- data/templates/gc32/_immunization.gc32.erb +0 -9
- data/templates/gc32/_insurance_provider.gc32.erb +0 -28
- data/templates/gc32/_medical_equipment.gc32.erb +0 -6
- data/templates/gc32/_medication.gc32.erb +0 -91
- data/templates/gc32/_name.gc32.erb +0 -11
- data/templates/gc32/_organization.gc32.erb +0 -10
- data/templates/gc32/_person_attributes.gc32.erb +0 -7
- data/templates/gc32/_procedure.gc32.erb +0 -9
- data/templates/gc32/_provider.gc32.erb +0 -9
- data/templates/gc32/_result.gc32.erb +0 -12
- data/templates/gc32/_social_history.gc32.erb +0 -6
- data/templates/gc32/_support.gc32.erb +0 -15
- data/templates/gc32/_telecom.gc32.erb +0 -1
- data/templates/gc32/_vital_sign.gc32.erb +0 -4
- data/templates/gc32/record.gc32.erb +0 -97
@@ -1,18 +0,0 @@
|
|
1
|
-
module HealthDataStandards
|
2
|
-
module Export
|
3
|
-
module GreenC32
|
4
|
-
class Record
|
5
|
-
def initialize
|
6
|
-
template_helper = TemplateHelper.new('gc32', 'gc32')
|
7
|
-
@rendering_context = RenderingContext.new
|
8
|
-
@rendering_context.template_helper = template_helper
|
9
|
-
@rendering_context.extensions = [HealthDataStandards::Export::Helper::GC32ViewHelper]
|
10
|
-
end
|
11
|
-
|
12
|
-
def export(patient)
|
13
|
-
@rendering_context.render(:template => 'record', :locals => {:record => patient})
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module HealthDataStandards
|
2
|
-
module Export
|
3
|
-
module Helper
|
4
|
-
module GC32ViewHelper
|
5
|
-
def gc32_code_display(entry, options={})
|
6
|
-
code_display(entry, options.merge("exclude_null_flavor" => true))
|
7
|
-
end
|
8
|
-
|
9
|
-
def gc32_effective_time(entry)
|
10
|
-
if entry.time
|
11
|
-
"<effectiveTime value=\"#{Time.at(entry.time).xmlschema}\" />"
|
12
|
-
elsif entry.start_time || entry.end_time
|
13
|
-
gc32_interval(entry, "effectiveTime", :start_time, :end_time)
|
14
|
-
else
|
15
|
-
"<effectiveTime />"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def gc32_interval(entry, tag_name, start_attribute, end_attribute)
|
20
|
-
time = "<#{tag_name}>"
|
21
|
-
time += "<start value=\"#{Time.at(entry.send(start_attribute)).xmlschema}\" />" if entry.send(start_attribute)
|
22
|
-
time += "<end value=\"#{Time.at(entry.send(end_attribute)).xmlschema}\" />" if entry.send(end_attribute)
|
23
|
-
time += "</#{tag_name}>"
|
24
|
-
end
|
25
|
-
|
26
|
-
def quantity_display(value, tag_name="value")
|
27
|
-
return unless value
|
28
|
-
if value.respond_to?(:scalar)
|
29
|
-
"<#{tag_name} amount=\"#{value.scalar}\" unit=\"#{value.units}\" />"
|
30
|
-
elsif value['scalar'].present?
|
31
|
-
"<#{tag_name} amount=\"#{value['scalar']}\" unit=\"#{value['units']}\" />"
|
32
|
-
else
|
33
|
-
"<#{tag_name} amount=\"#{value['value']}\" unit=\"#{value['unit']}\" />"
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,238 +0,0 @@
|
|
1
|
-
require "time"
|
2
|
-
|
3
|
-
module HealthDataStandards
|
4
|
-
module Import
|
5
|
-
module CCR
|
6
|
-
# This class is the central location for taking an ASTM CCR XML document and converting it
|
7
|
-
# into the processed form we store in MongoDB. The class does this by running each measure
|
8
|
-
# independently on the XML document
|
9
|
-
#
|
10
|
-
# This class is a Singleton. It should be accessed by calling PatientImporter.instance
|
11
|
-
class PatientImporter
|
12
|
-
|
13
|
-
include Singleton
|
14
|
-
|
15
|
-
Gender = {"male" => "M", "female" => "F"}
|
16
|
-
|
17
|
-
# Creates a new PatientImporter with the following XPath expressions used to find content in
|
18
|
-
# an ASTM CCR
|
19
|
-
#
|
20
|
-
# Encounter entries
|
21
|
-
# //ccr:Encounters/ccr:Encounter
|
22
|
-
# Procedure entries
|
23
|
-
# //ccr:Procedures/ccr:Procedure
|
24
|
-
#
|
25
|
-
# Result entries -
|
26
|
-
# //ccr:Results/ccr:Result
|
27
|
-
#
|
28
|
-
# Vital sign entries
|
29
|
-
# //ccr:VitalSigns/ccr:Result
|
30
|
-
#
|
31
|
-
# Medication entries
|
32
|
-
# //ccr:Medications/ccr:Medication
|
33
|
-
#
|
34
|
-
# Codes for medications are found in the Product sections
|
35
|
-
# ./ccr:Product
|
36
|
-
#
|
37
|
-
# Condition entries
|
38
|
-
# //ccr:Problems/ccr:Problem
|
39
|
-
#
|
40
|
-
# Social History entries
|
41
|
-
# //ccr:SocialHistory/ccr:SocialHistoryElement
|
42
|
-
#
|
43
|
-
# Care Goal entries
|
44
|
-
# //ccr:Goals/ccr:Goal
|
45
|
-
#
|
46
|
-
# Allergy entries
|
47
|
-
# //ccr:Alerts/ccr:Alert
|
48
|
-
#
|
49
|
-
# Immunization entries
|
50
|
-
# //ccr:Immunizations/ccr:Immunization
|
51
|
-
#
|
52
|
-
# Codes for immunizations are found in the substanceAdministration with the following relative XPath
|
53
|
-
# ./ccr:Product
|
54
|
-
|
55
|
-
def initialize (check_usable = true)
|
56
|
-
@measure_importers = {}
|
57
|
-
@section_importers = {}
|
58
|
-
@section_importers[:encounters] = SimpleImporter.new("//ccr:Encounters/ccr:Encounter",:encounters)
|
59
|
-
@section_importers[:procedures] = SimpleImporter.new("//ccr:Procedures/ccr:Procedure",:procedures)
|
60
|
-
@section_importers[:results] = ResultImporter.new("//ccr:Results/ccr:Result",:results)
|
61
|
-
@section_importers[:vital_signs] = ResultImporter.new("//ccr:VitalSigns/ccr:Result",:vital_signs)
|
62
|
-
@section_importers[:medications] = ProductImporter.new("//ccr:Medications/ccr:Medication", :medications)
|
63
|
-
@section_importers[:conditions] = SimpleImporter.new("//ccr:Problems/ccr:Problem",:conditions)
|
64
|
-
@section_importers[:social_history] = SimpleImporter.new("//ccr:SocialHistory/ccr:SocialHistoryElement", :social_history)
|
65
|
-
@section_importers[:care_goals] = SimpleImporter.new("//ccr:Goals/ccr:Goal",:care_goals)
|
66
|
-
@section_importers[:medical_equipment] = ProductImporter.new("//ccr:Equipment/ccr:EquipmentElement",:medical_equipment)
|
67
|
-
@section_importers[:allergies] = SimpleImporter.new("//ccr:Alerts/ccr:Alert",:allergies)
|
68
|
-
@section_importers[:immunizations] = ProductImporter.new("//ccr:Immunizations/ccr:Immunization",:immunizations)
|
69
|
-
end
|
70
|
-
|
71
|
-
|
72
|
-
# @param [boolean] value for check_usable_entries...importer uses true, stats uses false
|
73
|
-
def check_usable(check_usable_entries)
|
74
|
-
@section_importers.each_pair do |section, importer|
|
75
|
-
importer.check_for_usable = check_usable_entries
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Parses a ASTM CCR document and returns a Hash of of the patient.
|
80
|
-
#
|
81
|
-
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
82
|
-
# will have the "ccr" namespace registered to ""urn:astm-org:CCR""
|
83
|
-
# @return [Hash] a representation of the patient that can be inserted into MongoDB
|
84
|
-
def parse_ccr(doc, patient_id_xpath="//ccr:ContinuityOfCareRecord/ccr:Patient/ccr:ActorID")
|
85
|
-
ccr_patient = {}
|
86
|
-
entries = create_hash(doc)
|
87
|
-
get_demographics(ccr_patient, doc, patient_id_xpath)
|
88
|
-
process_events(ccr_patient, entries)
|
89
|
-
Record.new(ccr_patient)
|
90
|
-
end
|
91
|
-
#
|
92
|
-
# # Parses a patient hash containing demographic and event information
|
93
|
-
# #
|
94
|
-
# # @param [Hash] patient_hash patient data
|
95
|
-
# # @return [Hash] a representation of the patient that can be inserted into MongoDB
|
96
|
-
# def parse_hash(patient_hash)
|
97
|
-
# patient_record = {}
|
98
|
-
# patient_record['first'] = patient_hash['first']
|
99
|
-
# patient_record['patient_id'] = patient_hash['patient_id']
|
100
|
-
# patient_record['last'] = patient_hash['last']
|
101
|
-
# patient_record['gender'] = patient_hash['gender']
|
102
|
-
# patient_record['patient_id'] = patient_hash['patient_id']
|
103
|
-
# patient_record['birthdate'] = patient_hash['birthdate']
|
104
|
-
# patient_record['race'] = patient_hash['race']
|
105
|
-
# patient_record['ethnicity'] = patient_hash['ethnicity']
|
106
|
-
# patient_record['languages'] = patient_hash['languages']
|
107
|
-
# patient_record['addresses'] = patient_hash['addresses']
|
108
|
-
# event_hash = {}
|
109
|
-
# patient_hash['events'].each do |key, value|
|
110
|
-
# event_hash[key.intern] = parse_events(value)
|
111
|
-
# end
|
112
|
-
# process_events(patient_record, event_hash)
|
113
|
-
# end
|
114
|
-
|
115
|
-
# Adds the entries and denormalized measure information to the patient_record.
|
116
|
-
# Each Entry will be converted to a Hash and stored in an Array under the appropriate
|
117
|
-
# section key, such as medications. Measure information is listed under the measures
|
118
|
-
# key which has a Hash value. The Hash has the measure id as a key, and the denormalized
|
119
|
-
# measure information as a value
|
120
|
-
#
|
121
|
-
# @param patient_record - Hash with basic patient demographic information
|
122
|
-
# @entries - Hash of entries with section names a keys and an Array of Entry values
|
123
|
-
def process_events(patient_record, entries)
|
124
|
-
patient_record['measures'] = {}
|
125
|
-
@measure_importers.each_pair do |measure_id, importer|
|
126
|
-
patient_record['measures'][measure_id] = importer.parse(entries)
|
127
|
-
end
|
128
|
-
|
129
|
-
|
130
|
-
entries.each_pair do |key, value|
|
131
|
-
patient_record[key] = value.map do |e|
|
132
|
-
if e.usable?
|
133
|
-
e.to_hash
|
134
|
-
else
|
135
|
-
nil
|
136
|
-
end
|
137
|
-
end.compact
|
138
|
-
end
|
139
|
-
patient_record
|
140
|
-
end
|
141
|
-
|
142
|
-
# # Parses a list of event hashes into an array of Entry objects
|
143
|
-
# #
|
144
|
-
# # @param [Array] event_list list of event hashes
|
145
|
-
# # @return [Array] array of Entry objects
|
146
|
-
# def parse_events(event_list)
|
147
|
-
# event_list.collect do |event|
|
148
|
-
# if event.class==String.class
|
149
|
-
# # skip String elements in the event list, patient randomization templates
|
150
|
-
# # introduce String elements to simplify tailing-comma handling when generating
|
151
|
-
# # JSON using ERb
|
152
|
-
# nil
|
153
|
-
# else
|
154
|
-
# QME::Importer::Entry.from_event_hash(event)
|
155
|
-
# end
|
156
|
-
# end.compact
|
157
|
-
# end
|
158
|
-
|
159
|
-
# Adds a measure to run on a CCR that is passed in
|
160
|
-
#
|
161
|
-
# @param [MeasureBase] measure an Class that can extract information from a CCR that is necessary
|
162
|
-
# to calculate the measure
|
163
|
-
def add_measure(measure_id, importer)
|
164
|
-
@measure_importers[measure_id] = importer
|
165
|
-
end
|
166
|
-
|
167
|
-
# Create a simple representation of the patient from an ASTM CCR
|
168
|
-
#
|
169
|
-
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
170
|
-
# will have the "ccr" namespace registered to ""urn:astm-org:CCR""
|
171
|
-
# @return [Hash] a represnetation of the patient with symbols as keys for each section
|
172
|
-
def create_hash(doc, check_usable_entries = false)
|
173
|
-
ccr_patient = {}
|
174
|
-
@section_importers.each_pair do |section, importer|
|
175
|
-
importer.check_for_usable = check_usable_entries
|
176
|
-
ccr_patient[section] = importer.create_entries(doc)
|
177
|
-
end
|
178
|
-
ccr_patient
|
179
|
-
end
|
180
|
-
|
181
|
-
# Inspects a CCR document and populates the patient Hash with first name, last name
|
182
|
-
# birth date and gender.
|
183
|
-
#
|
184
|
-
# @param [Hash] patient A hash that is used to represent the patient
|
185
|
-
# @param [Nokogiri::XML::Node] doc The CCR document parsed by Nokogiri
|
186
|
-
def get_demographics(patient, doc, patient_id_xpath)
|
187
|
-
patientActorID = doc.at_xpath("//ccr:ContinuityOfCareRecord/ccr:Patient/ccr:ActorID").content
|
188
|
-
patientActor = doc.at_xpath("//ccr:ContinuityOfCareRecord/ccr:Actors/ccr:Actor[ccr:ActorObjectID = \"#{patientActorID}\"]")
|
189
|
-
patientID = patientActor.at_xpath(patient_id_xpath).try(:content)
|
190
|
-
patientID ||= patientActorID
|
191
|
-
|
192
|
-
name_element = patientActor.at_xpath('./ccr:Person/ccr:Name')
|
193
|
-
|
194
|
-
if name_element
|
195
|
-
if name_element.at_xpath("./ccr:CurrentName")
|
196
|
-
patient['first'] = name_element.at_xpath('./ccr:CurrentName/ccr:Given').try(:content)
|
197
|
-
patient['last'] = name_element.at_xpath('./ccr:CurrentName/ccr:Family').try(:content)
|
198
|
-
elsif name_element.at_xpath("./ccr:DisplayName")
|
199
|
-
# this will not work in all cases, but we're using it as a last resort if no CurrentName is found
|
200
|
-
first, last = name_element.at_xpath("./ccr:DisplayName").content.split(" ")
|
201
|
-
patient['first'] = first.strip
|
202
|
-
patient['last'] = last.strip
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
|
-
birthdate = patientActor.at_xpath('./ccr:Person//ccr:DateOfBirth/ccr:ExactDateTime | ./ccr:Person//ccr:DateOfBirth/ccr:ApproximateDateTime')
|
208
|
-
patient['birthdate'] = Time.parse(birthdate.content).to_i if birthdate
|
209
|
-
|
210
|
-
gender_string = patientActor.at_xpath('./ccr:Person/ccr:Gender/ccr:Text').content.downcase
|
211
|
-
patient['gender'] = Gender[gender_string.downcase]
|
212
|
-
#race_node = doc.at_xpath('/ccr:placeholder') #how do you find this?
|
213
|
-
race = doc.at_xpath('//ccr:SocialHistory/ccr:SocialHistoryElement[./ccr:Type/ccr:Text = "Race"]/ccr:Description/ccr:Code[./ccr:CodingSystem = "CDC-RE"]/ccr:Value')
|
214
|
-
ethnicity = doc.at_xpath('//ccr:SocialHistory/ccr:SocialHistoryElement[./ccr:Type/ccr:Text = "Ethnicity"]/ccr:Description/ccr:Code[./ccr:CodingSystem = "CDC-RE"]/ccr:Value')
|
215
|
-
|
216
|
-
if ethnicity
|
217
|
-
patient[:ethnicity] = {"code" => ethnicity.text, "codeSystem" => 'CDC-RE'}
|
218
|
-
end
|
219
|
-
|
220
|
-
|
221
|
-
if race
|
222
|
-
patient[:race] = {"code" => race.text, "codeSystem" => 'CDC-RE'}
|
223
|
-
end
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
#ethnicity_node = doc.at_xpath()
|
228
|
-
|
229
|
-
|
230
|
-
# languages = doc.at_xpath()
|
231
|
-
patient['languages'] = nil
|
232
|
-
|
233
|
-
patient['medical_record_number'] = patientID
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
module HealthDataStandards
|
2
|
-
module Import
|
3
|
-
module CCR
|
4
|
-
class ProductImporter < SectionImporter
|
5
|
-
|
6
|
-
# Traverses that ASTM CCR document passed in using XPath and creates an Array of Entry
|
7
|
-
# objects based on what it finds
|
8
|
-
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
9
|
-
# will have the "ccr" namespace registered to "urn:astm-org:CCR"
|
10
|
-
# measure definition
|
11
|
-
# @return [Array] will be a list of Entry objects
|
12
|
-
def create_entries(doc)
|
13
|
-
entry_list = []
|
14
|
-
entry_elements = doc.xpath(@entry_xpath)
|
15
|
-
entry_elements.each do |entry_element|
|
16
|
-
entry = Entry.new
|
17
|
-
product = entry_element.at_xpath("./ccr:Product")
|
18
|
-
process_product(product,entry)
|
19
|
-
extract_dates(entry_element, entry)
|
20
|
-
extract_status(entry_element, entry)
|
21
|
-
if @check_for_usable
|
22
|
-
entry_list << entry if entry.usable?
|
23
|
-
else
|
24
|
-
entry_list << entry
|
25
|
-
end
|
26
|
-
end
|
27
|
-
entry_list
|
28
|
-
end
|
29
|
-
|
30
|
-
# Add the codes from a <Product> block subsection to an Entry
|
31
|
-
def process_product_codes(node, entry)
|
32
|
-
codes = node.xpath("./ccr:Code")
|
33
|
-
if codes.size > 0
|
34
|
-
found_code = true
|
35
|
-
codes.each do |code|
|
36
|
-
normalize_coding_system(code)
|
37
|
-
codetext = code.at_xpath("./ccr:CodingSystem").content
|
38
|
-
entry.add_code(code.at_xpath("./ccr:Value").content, codetext)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Special handling for the medications section
|
44
|
-
def process_product (product, entry)
|
45
|
-
productName = product.at_xpath("./ccr:ProductName")
|
46
|
-
brandName = product.at_xpath("./ccr:BrandName")
|
47
|
-
productNameText = productName.at_xpath("./ccr:Text")
|
48
|
-
brandNameText = brandName.at_xpath("./ccr:Text") if brandName
|
49
|
-
entry.description = productNameText.content if productNameText
|
50
|
-
process_product_codes(productName, entry) # we throw any codes found within the productName and brandName into the same entry
|
51
|
-
process_product_codes(brandName, entry) if brandName
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
def create_product_entries(doc)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require "date"
|
2
|
-
# require "date/delta"
|
3
|
-
|
4
|
-
module HealthDataStandards
|
5
|
-
module Import
|
6
|
-
module CCR
|
7
|
-
class ProviderImporter
|
8
|
-
include Singleton
|
9
|
-
include ProviderImportUtils
|
10
|
-
|
11
|
-
# Extract Healthcare Providers from CCR
|
12
|
-
#
|
13
|
-
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
14
|
-
# will have the "ccr" namespace registered to "urn:astm-org:CCR"
|
15
|
-
# @return [Array] an array of providers found in the document
|
16
|
-
|
17
|
-
def create_provider(actor)
|
18
|
-
# Differentiate care providers by content of this field
|
19
|
-
provider = {}
|
20
|
-
if actor.at_xpath('./ccr:Person/ccr:Name/ccr:CurrentName/ccr:Given')
|
21
|
-
provider[:given_name] = extract_data(actor, './ccr:Person/ccr:Name/ccr:CurrentName/ccr:Given')
|
22
|
-
provider[:family_name] = extract_data(actor, './ccr:Person/ccr:Name/ccr:CurrentName/ccr:Family')
|
23
|
-
provider[:specialty] = extract_data(actor, './ccr:Specialty/ccr:Text')
|
24
|
-
end
|
25
|
-
|
26
|
-
provider[:specialty] = extract_data(actor, './ccr:Specialty/ccr:Text')
|
27
|
-
|
28
|
-
|
29
|
-
npi_ids = actor.at_xpath("./ccr:IDs[ccr:Type/ccr:Text = \"NPI\"]")
|
30
|
-
if npi_ids
|
31
|
-
npi_id = npi_ids.at_xpath("./ccr:ID")
|
32
|
-
npi = npi_id.content
|
33
|
-
provider[:npi] = npi if Provider.valid_npi?(npi)
|
34
|
-
end
|
35
|
-
|
36
|
-
find_or_create_provider(provider)
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
def extract_providers(doc)
|
41
|
-
|
42
|
-
# Providers are identified as the 'Source' for entries in the CCR. Sources can also include the patient, relatives, insurance companies, etc
|
43
|
-
provider_elements = doc.xpath("//ccr:ContinuityOfCareRecord/ccr:Actors/ccr:Actor[ccr:IDs/ccr:Type/ccr:Text=\"NPI\"]")
|
44
|
-
provider_elements.map { |pv| ProviderPerformance.new(provider: create_provider(pv)) }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
module HealthDataStandards
|
2
|
-
module Import
|
3
|
-
module CCR
|
4
|
-
class ResultImporter < SectionImporter
|
5
|
-
|
6
|
-
# Traverses that ASTM CCR document passed in using XPath and creates an Array of Entry
|
7
|
-
# objects based on what it finds
|
8
|
-
# @param [Nokogiri::XML::Document] doc It is expected that the root node of this document
|
9
|
-
# will have the "ccr" namespace registered to "urn:astm-org:CCR"
|
10
|
-
# measure definition
|
11
|
-
# @return [Array] will be a list of Entry objects
|
12
|
-
def create_entries(doc)
|
13
|
-
entry_list = []
|
14
|
-
entry_elements = doc.xpath(@entry_xpath)
|
15
|
-
entry_elements.each do |entry_element|
|
16
|
-
# Grab the time and the description from the Result node
|
17
|
-
dummy_entry = Entry.new
|
18
|
-
extract_dates(entry_element, dummy_entry)
|
19
|
-
dummy_entry.description = ""
|
20
|
-
if entry_element.at_xpath("./ccr:Description/ccr:Text")
|
21
|
-
dummy_entry.description = entry_element.at_xpath("./ccr:Description/ccr:Text").content
|
22
|
-
end
|
23
|
-
# Iterate over embedded tests
|
24
|
-
# Grab the values and the description from the Test nodes
|
25
|
-
# For each test, create an entry with the time from the Result, the description a concatenation of the Result and Test descriptions,
|
26
|
-
# and the value from the Test
|
27
|
-
|
28
|
-
tests = entry_element.xpath("./ccr:Test")
|
29
|
-
tests.each do |test|
|
30
|
-
entry = Entry.new
|
31
|
-
entry = dummy_entry.clone # copies time and description
|
32
|
-
extract_codes(test, entry)
|
33
|
-
extract_value(test, entry)
|
34
|
-
extract_status(test, entry)
|
35
|
-
extract_dates(test, entry)
|
36
|
-
entry.description = dummy_entry.description + ": " + entry.description
|
37
|
-
if @check_for_usable
|
38
|
-
entry_list << entry if entry.usable?
|
39
|
-
else
|
40
|
-
entry_list << entry
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
entry_list
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|