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
@@ -8,9 +8,9 @@ class Entry
|
|
8
8
|
field :time, type: Integer
|
9
9
|
field :start_time, type: Integer
|
10
10
|
field :end_time, type: Integer
|
11
|
-
field :status, type:
|
12
|
-
field :codes, type: Hash
|
13
|
-
field :value, type: Hash
|
11
|
+
field :status, type: String
|
12
|
+
field :codes, type: Hash, default: {}
|
13
|
+
field :value, type: Hash, default: {}
|
14
14
|
|
15
15
|
def single_code_value?
|
16
16
|
codes.size == 1 && codes.first[1].size == 1
|
@@ -20,6 +20,32 @@ class Entry
|
|
20
20
|
codes.map {|code_set, codes| "#{code_set}: #{codes.join(', ')}"}.join(' ')
|
21
21
|
end
|
22
22
|
|
23
|
+
# Will return a single code and code set if one exists in the code sets that are
|
24
|
+
# passed in. Returns a hash with a key of code and code_set if found, nil otherwise
|
25
|
+
def preferred_code(preferred_code_sets)
|
26
|
+
matching_code_sets = preferred_code_sets & codes.keys
|
27
|
+
if matching_code_sets.present?
|
28
|
+
code_set = matching_code_sets.first
|
29
|
+
{'code' => codes[code_set].first, 'code_set' => code_set}
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Will return an Array of code and code_set hashes for all codes for this entry
|
36
|
+
# except for the preferred_code. It is intended that these codes would be used in
|
37
|
+
# the translation elements as childern of a CDA code element
|
38
|
+
def translation_codes(preferred_code_sets)
|
39
|
+
tx_codes = []
|
40
|
+
codes.each_pair do |code_set, code_list|
|
41
|
+
code_list.each do |code|
|
42
|
+
tx_codes << {'code' => code, 'code_set' => code_set}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
tx_codes - [preferred_code(preferred_code_sets)]
|
47
|
+
end
|
48
|
+
|
23
49
|
def times_to_s
|
24
50
|
if start_time.present? || end_time.present?
|
25
51
|
start_string = start_time ? Time.at(start_time).to_formatted_s(:long_ordinal) : 'UNK'
|
@@ -48,4 +74,106 @@ class Entry
|
|
48
74
|
end
|
49
75
|
end
|
50
76
|
end
|
77
|
+
|
78
|
+
def self.from_event_hash(event)
|
79
|
+
entry = Entry.new
|
80
|
+
if event['code']
|
81
|
+
entry.add_code(event['code'], event['code_set'])
|
82
|
+
end
|
83
|
+
entry.time = event['time']
|
84
|
+
if event['value']
|
85
|
+
entry.set_value(event['value'], event['unit'])
|
86
|
+
end
|
87
|
+
if event['description']
|
88
|
+
entry.description = event['description']
|
89
|
+
end
|
90
|
+
if event['status']
|
91
|
+
entry.status = event['status']
|
92
|
+
end
|
93
|
+
entry
|
94
|
+
end
|
95
|
+
|
96
|
+
# Add a code into the Entry
|
97
|
+
# @param [String] code the code to add
|
98
|
+
# @param [String] code_system the code system that the code belongs to
|
99
|
+
def add_code(code, code_system)
|
100
|
+
self.codes[code_system] ||= []
|
101
|
+
self.codes[code_system] << code
|
102
|
+
end
|
103
|
+
|
104
|
+
# Sets the value for the entry
|
105
|
+
# @param [String] scalar the value
|
106
|
+
# @param [String] units the units of the scalar value
|
107
|
+
def set_value(scalar, units=nil)
|
108
|
+
self.value[:scalar] = scalar
|
109
|
+
self.value[:units] = units
|
110
|
+
end
|
111
|
+
|
112
|
+
# Checks if a code is in the list of possible codes
|
113
|
+
# @param [Array] code_set an Array of Hashes that describe the values for code sets
|
114
|
+
# @return [true, false] whether the code is in the list of desired codes
|
115
|
+
def is_in_code_set?(code_set)
|
116
|
+
codes.keys.each do |code_system|
|
117
|
+
all_codes_in_system = code_set.find_all {|set| set['set'] == code_system}
|
118
|
+
all_codes_in_system.each do |codes_in_system|
|
119
|
+
matching_codes = codes_in_system['values'] & codes[code_system]
|
120
|
+
if matching_codes.length > 0
|
121
|
+
return true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
false
|
126
|
+
end
|
127
|
+
|
128
|
+
# Tries to find a single point in time for this entry. Will first return time if it is present,
|
129
|
+
# then fall back to start_time and finally end_time
|
130
|
+
def as_point_in_time
|
131
|
+
if time
|
132
|
+
time
|
133
|
+
elsif start_time
|
134
|
+
start_time
|
135
|
+
else
|
136
|
+
end_time
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Checks to see if this Entry can be used as a date range
|
141
|
+
# @return [true, false] If the Entry has a start and end time returns true, false otherwise.
|
142
|
+
def is_date_range?
|
143
|
+
start_time.present? && end_time.present?
|
144
|
+
end
|
145
|
+
|
146
|
+
# Checks to see if this Entry is usable for measure calculation. This means that it contains
|
147
|
+
# at least one code and has one of its time properties set (start, end or time)
|
148
|
+
# @return [true, false]
|
149
|
+
def usable?
|
150
|
+
codes.present? && (start_time.present? || end_time.present? || time.present?)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Creates a Hash for this Entry
|
154
|
+
# @return [Hash] a Hash representing the Entry
|
155
|
+
def to_hash
|
156
|
+
entry_hash = {}
|
157
|
+
entry_hash['codes'] = codes
|
158
|
+
unless value.empty?
|
159
|
+
entry_hash['value'] = value
|
160
|
+
end
|
161
|
+
|
162
|
+
if is_date_range?
|
163
|
+
entry_hash['start_time'] = start_time
|
164
|
+
entry_hash['end_time'] = end_time
|
165
|
+
else
|
166
|
+
entry_hash['time'] = as_point_in_time
|
167
|
+
end
|
168
|
+
|
169
|
+
if status
|
170
|
+
entry_hash['status'] = status
|
171
|
+
end
|
172
|
+
|
173
|
+
if description
|
174
|
+
entry_hash['description'] = description
|
175
|
+
end
|
176
|
+
|
177
|
+
entry_hash
|
178
|
+
end
|
51
179
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class FulfillmentHistory
|
2
|
+
include Mongoid::Document
|
3
|
+
|
4
|
+
field :prescriptionNumber, type: String, as: 'prescription_number'
|
5
|
+
field :provider, type: Hash
|
6
|
+
field :dispensingPharmacyLocation, type: Hash, as: 'dispensing_pharmacy_location'
|
7
|
+
field :dispenseDate, type: Integer, as: 'dispense_date'
|
8
|
+
field :quantityDispensed, type: Hash, as: 'quantity_dispensed'
|
9
|
+
field :fillNumber, type: Integer, as: 'fill_number'
|
10
|
+
field :fillStatus, type: Hash, as: 'fill_status'
|
11
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Medication < Entry
|
2
|
+
field :administrationTiming, type: Hash, as: 'administration_timing'
|
3
|
+
field :freeTextSig, type: String, as: 'free_text_sig'
|
4
|
+
field :dose, type: Hash
|
5
|
+
field :brandName, type: String, as: 'brand_name'
|
6
|
+
field :typeOfMedication, type: Hash, as: 'type_of_medication'
|
7
|
+
field :statusOfMedication, type: Hash, as: 'status_of_medication'
|
8
|
+
embeds_many :fulfillmentHistory, class_name: 'FulfillmentHistory'
|
9
|
+
embeds_many :orderInformation, class_name: 'OrderInformation'
|
10
|
+
field :route, type: Hash
|
11
|
+
field :site, type: Hash
|
12
|
+
field :doseRestriction, type: Hash, as: 'dose_restriction'
|
13
|
+
field :fulfillmentInstructions, type: String, as: 'fulfillment_instructions'
|
14
|
+
field :indication, type: Hash
|
15
|
+
field :productForm, type: Hash, as: 'product_form'
|
16
|
+
field :vehicle, type: Hash
|
17
|
+
field :reaction, type: Hash
|
18
|
+
field :deliveryMethod, type: Hash, as: 'delivery_method'
|
19
|
+
field :patientInstructions, type: String, as: 'patient_instructions'
|
20
|
+
|
21
|
+
alias :fulfillment_history :fulfillmentHistory
|
22
|
+
alias :fulfillment_history= :fulfillmentHistory=
|
23
|
+
alias :order_information :orderInformation
|
24
|
+
alias :order_information= :orderInformation=
|
25
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class OrderInformation
|
2
|
+
include Mongoid::Document
|
3
|
+
|
4
|
+
field :orderNumber, type: String, as: 'order_number'
|
5
|
+
field :fills, type: Integer
|
6
|
+
field :quantityOrdered, type: Hash, as: 'quantity_ordered'
|
7
|
+
field :orderExpirationDateTime, type: Integer, as: 'order_expiration_date_time'
|
8
|
+
field :orderDateTime, type: Integer, as: 'order_date_time'
|
9
|
+
end
|
@@ -9,6 +9,7 @@ class Record
|
|
9
9
|
field :race, type: String
|
10
10
|
field :ethnicity, type: String
|
11
11
|
field :test_id, type: BSON::ObjectId
|
12
|
+
field :medical_record_number, type: String
|
12
13
|
|
13
14
|
[:allergies, :care_goals, :conditions, :encounters, :immunizations, :medical_equipment,
|
14
15
|
:medications, :procedures, :results, :social_history, :vital_signs].each do |section|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Util
|
3
|
+
# General helpers for working with codes and code systems
|
4
|
+
class CodeSystemHelper
|
5
|
+
CODE_SYSTEMS = {
|
6
|
+
'2.16.840.1.113883.6.1' => 'LOINC',
|
7
|
+
'2.16.840.1.113883.6.96' => 'SNOMED-CT',
|
8
|
+
'2.16.840.1.113883.6.12' => 'CPT',
|
9
|
+
#'2.16.840.1.113883.3.88.12.80.32' => 'CPT', # Encounter Type from C32, a subset of CPT
|
10
|
+
'2.16.840.1.113883.6.88' => 'RxNorm',
|
11
|
+
'2.16.840.1.113883.6.103' => 'ICD-9-CM',
|
12
|
+
'2.16.840.1.113883.6.104' => 'ICD-9-CM',
|
13
|
+
'2.16.840.1.113883.6.90' => 'ICD-10-CM',
|
14
|
+
'2.16.840.1.113883.6.14' => 'HCPCS',
|
15
|
+
'2.16.840.1.113883.6.59' => 'CVX',
|
16
|
+
'2.16.840.1.113883.5.83' => 'HITSP C80 Observation Status'
|
17
|
+
}
|
18
|
+
|
19
|
+
# Returns the name of a code system given an oid
|
20
|
+
# @param [String] oid of a code system
|
21
|
+
# @return [String] the name of the code system as described in the measure definition JSON
|
22
|
+
def self.code_system_for(oid)
|
23
|
+
CODE_SYSTEMS[oid] || "Unknown"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the oid for a code system given a codesystem name
|
27
|
+
# @param [String] the name of the code system
|
28
|
+
# @return [String] the oid of the code system
|
29
|
+
def self.oid_for_code_system(code_system)
|
30
|
+
CODE_SYSTEMS.invert[code_system]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the whole map of OIDs to code systems
|
34
|
+
# @terurn [Hash] oids as keys, code system names as values
|
35
|
+
def self.code_systems
|
36
|
+
CODE_SYSTEMS
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module HealthDataStandards
|
2
|
+
module Util
|
3
|
+
# General helpers for working with HL7 data types
|
4
|
+
class HL7Helper
|
5
|
+
|
6
|
+
# Converts an HL7 timestamp into an Integer
|
7
|
+
# @param [String] timestamp the HL7 timestamp. Expects YYYYMMDD format
|
8
|
+
# @return [Integer] Date in seconds since the epoch
|
9
|
+
def self.timestamp_to_integer(timestamp)
|
10
|
+
if timestamp && timestamp.length >= 4
|
11
|
+
year = timestamp[0..3].to_i
|
12
|
+
month = timestamp.length >= 6 ? timestamp[4..5].to_i : 1
|
13
|
+
day = timestamp.length >= 8 ? timestamp[6..7].to_i : 1
|
14
|
+
hour = timestamp.length >= 10 ? timestamp[8..9].to_i : 0
|
15
|
+
min = timestamp.length >= 12 ? timestamp[10..11].to_i : 0
|
16
|
+
sec = timestamp.length >= 14 ? timestamp[12..13].to_i : 0
|
17
|
+
|
18
|
+
Time.gm(year, month, day, hour, min, sec).to_i
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -43,7 +43,9 @@
|
|
43
43
|
<participant typeCode="CSM">
|
44
44
|
<participantRole classCode="MANU">
|
45
45
|
<playingEntity classCode="MMAT">
|
46
|
-
<%== render :partial => 'code_with_reference',
|
46
|
+
<%== render :partial => 'code_with_reference',
|
47
|
+
:locals => {:entry => entry, :i => i, :section => 'allergies',
|
48
|
+
:preferred_code_sets => ['RxNorm']} %>
|
47
49
|
<name><%= entry.description %></name>
|
48
50
|
</playingEntity>
|
49
51
|
</participantRole>
|
@@ -14,7 +14,7 @@
|
|
14
14
|
<templateId root="2.16.840.1.113883.10.20.1.25"/>
|
15
15
|
<!-- Plan of Activity activity template -->
|
16
16
|
<id root="<%= UUID.generate %>"/>
|
17
|
-
<%== code_display(entry) %>
|
17
|
+
<%== code_display(entry, :preferred_code_sets => ['SNOMED-CT']) %>
|
18
18
|
<statusCode code="new"/>
|
19
19
|
<effectiveTime value="<%= Time.at(entry.time).utc.to_formatted_s(:number) %>"/>
|
20
20
|
</observation>
|
@@ -1,8 +1,13 @@
|
|
1
|
-
<%
|
2
|
-
|
3
|
-
code_system_oid =
|
1
|
+
<% preferred_code = entry.preferred_code(preferred_code_sets)
|
2
|
+
if preferred_code
|
3
|
+
code_system_oid = HealthDataStandards::Util::CodeSystemHelper.oid_for_code_system(preferred_code['code_set'])
|
4
4
|
-%>
|
5
|
-
<code code="<%= code %>" codeSystem="<%= code_system_oid %>">
|
5
|
+
<code code="<%= preferred_code['code'] %>" codeSystem="<%= code_system_oid %>" displayName="<%= entry.description %>">
|
6
|
+
<% else -%>
|
7
|
+
<code nullFlavor="UNK">
|
8
|
+
<% end -%>
|
6
9
|
<originalText><reference value="#<%= section %>-desc-<%= i %>"/></originalText>
|
10
|
+
<% entry.translation_codes(preferred_code_sets).each do |translation| -%>
|
11
|
+
<translation code="<%= translation['code'] %>" codeSystem="<%= HealthDataStandards::Util::CodeSystemHelper.oid_for_code_system(translation['code_set']) %>" />
|
12
|
+
<% end -%>
|
7
13
|
</code>
|
8
|
-
<% end -%>
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<!--Problems section template-->
|
8
8
|
<code code="11450-4" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC" displayName="Problem list"/>
|
9
9
|
<title>Problems</title>
|
10
|
-
<%== render :partial => 'narrative_block', :locals => {:entries => entries, :section => 'conditions'} %>
|
10
|
+
<%== render :partial => 'narrative_block', :locals => {:entries => entries, :section => 'conditions', :status => true } %>
|
11
11
|
<% entries.each_with_index do |entry, i| -%>
|
12
12
|
<entry typeCode="DRIV">
|
13
13
|
<act classCode="ACT" moodCode="EVN">
|
@@ -40,7 +40,7 @@
|
|
40
40
|
<effectiveTime>
|
41
41
|
<low value="<%= Time.at(entry.time).utc.to_formatted_s(:number) %>"/>
|
42
42
|
</effectiveTime>
|
43
|
-
<%== code_display(entry, 'value', 'xsi:type="CD"') %>
|
43
|
+
<%== code_display(entry, {'tag_name' => 'value', 'extra_content' => 'xsi:type="CD"', 'preferred_code_sets' => ['SNOMED-CT']}) %>
|
44
44
|
<% if entry.status -%>
|
45
45
|
<entryRelationship typeCode="REFR">
|
46
46
|
<observation classCode="OBS" moodCode="EVN">
|
@@ -16,7 +16,8 @@
|
|
16
16
|
<templateId root="1.3.6.1.4.1.19376.1.5.3.1.4.14" assigningAuthorityName="IHE PCC"/>
|
17
17
|
<!-- Encounter activity template -->
|
18
18
|
<id root="<%= UUID.generate %>"/>
|
19
|
-
<%== render :partial => 'code_with_reference', :locals => {:entry => entry, :i => i, :section => 'encounters'
|
19
|
+
<%== render :partial => 'code_with_reference', :locals => {:entry => entry, :i => i, :section => 'encounters',
|
20
|
+
:preferred_code_sets => ['CPT']} %>
|
20
21
|
<text>
|
21
22
|
<reference value="#encounters-desc-<%= i %>"/>
|
22
23
|
</text>
|
@@ -29,7 +29,8 @@
|
|
29
29
|
<templateId root="1.3.6.1.4.1.19376.1.5.3.1.4.7.2"/>
|
30
30
|
<!-- Product template -->
|
31
31
|
<manufacturedMaterial>
|
32
|
-
<%== render :partial => 'code_with_reference', :locals => {:entry => entry, :i => i, :section => 'immunizations'
|
32
|
+
<%== render :partial => 'code_with_reference', :locals => {:entry => entry, :i => i, :section => 'immunizations',
|
33
|
+
:preferred_code_sets => ['CVX']} %>
|
33
34
|
<name><%= entry.description %></name>
|
34
35
|
</manufacturedMaterial>
|
35
36
|
</manufacturedProduct>
|
@@ -28,7 +28,8 @@
|
|
28
28
|
<templateId root="2.16.840.1.113883.3.88.11.83.8.2"/>
|
29
29
|
<templateId root="1.3.6.1.4.1.19376.1.5.3.1.4.7.2"/>
|
30
30
|
<manufacturedMaterial>
|
31
|
-
<%== render :partial => 'code_with_reference', :locals => {:entry => entry, :i => i, :section => 'medications'
|
31
|
+
<%== render :partial => 'code_with_reference', :locals => {:entry => entry, :i => i, :section => 'medications',
|
32
|
+
:preferred_code_sets => ['RxNorm']} %>
|
32
33
|
<name><%= entry.description %></name>
|
33
34
|
</manufacturedMaterial>
|
34
35
|
</manufacturedProduct>
|
@@ -5,6 +5,13 @@
|
|
5
5
|
<th>Description</th>
|
6
6
|
<th>Codes</th>
|
7
7
|
<th>Time</th>
|
8
|
+
<% if status.present? %>
|
9
|
+
<th>Status</th>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<% if value.present? %>
|
13
|
+
<th>Value</th>
|
14
|
+
<% end %>
|
8
15
|
</tr>
|
9
16
|
</thead>
|
10
17
|
<tbody>
|
@@ -13,6 +20,13 @@
|
|
13
20
|
<td ID="<%= section %>-desc-<%= i %>"><%= entry.description %></td>
|
14
21
|
<td ID="<%= section %>-code-<%= i %>"><%= entry.codes_to_s %></td>
|
15
22
|
<td><%= entry.times_to_s %></td>
|
23
|
+
<% if status.present? %>
|
24
|
+
<td><%= entry.status %></td>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% if value.present? %>
|
28
|
+
<td><%= entry.value["scalar"] %></td>
|
29
|
+
<% end %>
|
16
30
|
</tr>
|
17
31
|
<%- end -%>
|
18
32
|
</tbody>
|