health-data-standards 0.7.1 → 0.8.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 +9 -1
- data/Rakefile +14 -4
- data/lib/health-data-standards.rb +23 -0
- data/lib/health-data-standards/export/ccr.rb +55 -56
- data/lib/health-data-standards/export/hdata/metadata.rb +16 -0
- data/lib/health-data-standards/export/html.rb +21 -0
- data/lib/health-data-standards/export/template_helper.rb +3 -0
- data/lib/health-data-standards/export/view_helper.rb +29 -5
- data/lib/health-data-standards/import/c32/condition_importer.rb +31 -33
- data/lib/health-data-standards/import/c32/encounter_importer.rb +4 -6
- data/lib/health-data-standards/import/c32/medication_importer.rb +1 -5
- data/lib/health-data-standards/import/c32/organization_importer.rb +23 -0
- data/lib/health-data-standards/import/c32/patient_importer.rb +1 -4
- data/lib/health-data-standards/import/c32/provider_importer.rb +43 -30
- data/lib/health-data-standards/import/c32/section_importer.rb +20 -41
- data/lib/health-data-standards/import/ccr/patient_importer.rb +27 -10
- data/lib/health-data-standards/import/ccr/provider_importer.rb +29 -41
- data/lib/health-data-standards/import/ccr/section_importer.rb +38 -27
- data/lib/health-data-standards/import/green_c32/allergy_importer.rb +20 -0
- data/lib/health-data-standards/import/green_c32/condition_importer.rb +2 -3
- data/lib/health-data-standards/import/green_c32/encounter_importer.rb +42 -0
- data/lib/health-data-standards/import/green_c32/immunization_importer.rb +23 -0
- data/lib/health-data-standards/import/green_c32/medication_importer.rb +69 -0
- data/lib/health-data-standards/import/green_c32/procedure_importer.rb +35 -0
- data/lib/health-data-standards/import/green_c32/result_importer.rb +21 -8
- data/lib/health-data-standards/import/green_c32/section_importer.rb +55 -9
- data/lib/health-data-standards/import/green_c32/social_history_importer.rb +18 -0
- data/lib/health-data-standards/import/green_c32/vital_sign_importer.rb +21 -0
- data/lib/health-data-standards/import/hdata/metadata_importer.rb +82 -0
- data/lib/health-data-standards/import/provider_import_utils.rb +23 -0
- data/lib/health-data-standards/models/address.rb +11 -0
- data/lib/health-data-standards/models/allergy.rb +1 -0
- data/lib/health-data-standards/models/condition.rb +1 -1
- data/lib/health-data-standards/models/encounter.rb +11 -6
- data/lib/health-data-standards/models/entry.rb +16 -5
- data/lib/health-data-standards/models/fulfillment_history.rb +3 -5
- data/lib/health-data-standards/models/immunization.rb +7 -1
- data/lib/health-data-standards/models/medication.rb +4 -3
- data/lib/health-data-standards/models/metadata/author.rb +16 -0
- data/lib/health-data-standards/models/metadata/base.rb +20 -0
- data/lib/health-data-standards/models/metadata/change_info.rb +9 -0
- data/lib/health-data-standards/models/metadata/link_info.rb +9 -0
- data/lib/health-data-standards/models/metadata/pedigree.rb +15 -0
- data/lib/health-data-standards/models/organization.rb +8 -0
- data/lib/health-data-standards/models/procedure.rb +5 -2
- data/lib/health-data-standards/models/provider.rb +6 -1
- data/lib/health-data-standards/models/record.rb +13 -3
- data/lib/health-data-standards/models/social_history.rb +3 -0
- data/lib/health-data-standards/models/telecom.rb +9 -0
- data/lib/health-data-standards/models/vital_sign.rb +2 -0
- data/lib/health-data-standards/util/code_system_helper.rb +3 -1
- data/templates/_address.gc32.erb +9 -0
- data/templates/_allergies.c32.erb +2 -2
- data/templates/_allergy.gc32.erb +13 -0
- data/templates/_care_goals.c32.erb +1 -1
- data/templates/_condition.gc32.erb +6 -6
- data/templates/_conditions.c32.erb +2 -2
- data/templates/_encounter.gc32.erb +32 -0
- data/templates/_encounters.c32.erb +1 -1
- data/templates/_immunization.gc32.erb +9 -0
- data/templates/_immunizations.c32.erb +1 -1
- data/templates/_medical_equipment.c32.erb +1 -1
- data/templates/_medication.gc32.erb +60 -0
- data/templates/_medications.c32.erb +1 -1
- data/templates/_narrative_block.c32.erb +1 -1
- data/templates/_organization.gc32.erb +10 -0
- data/templates/_pedigree.hdata.erb +24 -0
- data/templates/_procedure.gc32.erb +8 -0
- data/templates/_procedures.c32.erb +1 -1
- data/templates/_provider.gc32.erb +19 -0
- data/templates/_results.c32.erb +1 -1
- data/templates/_social_history.c32.erb +1 -1
- data/templates/_social_history.gc32.erb +6 -0
- data/templates/_telecom.gc32.erb +1 -0
- data/templates/_vital_sign.gc32.erb +12 -0
- data/templates/_vital_signs.c32.erb +1 -1
- data/templates/metadata.hdata.erb +35 -0
- data/templates/show.html.erb +287 -0
- metadata +50 -15
@@ -0,0 +1,23 @@
|
|
1
|
+
module ProviderImportUtils
|
2
|
+
|
3
|
+
def extract_provider(performer)
|
4
|
+
provider_data = extract_provider_data(performer, false)
|
5
|
+
find_or_create_provider(provider_data)
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_or_create_provider(provider_hash)
|
9
|
+
provider = Provider.first(conditions: {npi: provider_hash[:npi]}) if provider_hash[:npi] && !provider_hash[:npi].empty?
|
10
|
+
provider ||= Provider.create(provider_hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns nil if result is an empty string, block allows text munging of result if there is one
|
14
|
+
def extract_data(subject, query)
|
15
|
+
result = subject.at_xpath(query)
|
16
|
+
if result.nil? || result.content.empty?
|
17
|
+
nil
|
18
|
+
else
|
19
|
+
result.content
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -3,7 +3,7 @@ class Condition < Entry
|
|
3
3
|
field :causeOfDeath, type: Boolean
|
4
4
|
field :name, type: String
|
5
5
|
|
6
|
-
|
6
|
+
embeds_many :treating_provider, class_name: "Provider"
|
7
7
|
|
8
8
|
alias :cause_of_death :causeOfDeath
|
9
9
|
alias :cause_of_death= :causeOfDeath=
|
@@ -1,12 +1,17 @@
|
|
1
1
|
class Encounter < Entry
|
2
|
-
field :performer, type: Hash
|
3
|
-
field :facility, type: Hash
|
4
2
|
field :admitType, type: Hash
|
5
|
-
field :
|
6
|
-
|
3
|
+
field :dischargeDisposition, type: Hash
|
4
|
+
field :free_text, type: String
|
7
5
|
|
6
|
+
embeds_one :facility, class_name: "Organization"
|
7
|
+
embeds_one :reason, class_name: "Entry"
|
8
|
+
|
9
|
+
belongs_to :performer, class_name: "Provider"
|
10
|
+
|
8
11
|
alias :admit_type :admitType
|
9
12
|
alias :admit_type= :admitType=
|
10
|
-
alias :
|
11
|
-
alias :
|
13
|
+
alias :discharge_disposition :dischargeDisposition
|
14
|
+
alias :discharge_disposition= :dischargeDisposition=
|
15
|
+
alias :freeText :free_text
|
16
|
+
alias :freeText= :free_text=
|
12
17
|
end
|
@@ -2,9 +2,12 @@ class Entry
|
|
2
2
|
|
3
3
|
include Mongoid::Document
|
4
4
|
|
5
|
-
embedded_in :entry_list, polymorphic: true
|
6
|
-
|
5
|
+
# embedded_in :entry_list, polymorphic: true
|
6
|
+
|
7
|
+
embedded_in :record
|
8
|
+
|
7
9
|
field :description, type: String
|
10
|
+
field :specifics, type: String
|
8
11
|
field :time, type: Integer
|
9
12
|
field :start_time, type: Integer
|
10
13
|
field :end_time, type: Integer
|
@@ -27,11 +30,12 @@ class Entry
|
|
27
30
|
|
28
31
|
# Will return a single code and code set if one exists in the code sets that are
|
29
32
|
# passed in. Returns a hash with a key of code and code_set if found, nil otherwise
|
30
|
-
def preferred_code(preferred_code_sets)
|
31
|
-
|
33
|
+
def preferred_code(preferred_code_sets, codes_attribute=:codes)
|
34
|
+
codes_value = send(codes_attribute)
|
35
|
+
matching_code_sets = preferred_code_sets & codes_value.keys
|
32
36
|
if matching_code_sets.present?
|
33
37
|
code_set = matching_code_sets.first
|
34
|
-
{'code' =>
|
38
|
+
{'code' => codes_value[code_set].first, 'code_set' => code_set}
|
35
39
|
else
|
36
40
|
nil
|
37
41
|
end
|
@@ -92,6 +96,9 @@ class Entry
|
|
92
96
|
if event['description']
|
93
97
|
entry.description = event['description']
|
94
98
|
end
|
99
|
+
if event['specifics']
|
100
|
+
entry.specifics = event['specifics']
|
101
|
+
end
|
95
102
|
if event['status']
|
96
103
|
entry.status = event['status']
|
97
104
|
end
|
@@ -195,6 +202,10 @@ class Entry
|
|
195
202
|
if description
|
196
203
|
entry_hash['description'] = description
|
197
204
|
end
|
205
|
+
|
206
|
+
if specifics
|
207
|
+
entry_hash['specifics'] = specifics
|
208
|
+
end
|
198
209
|
|
199
210
|
entry_hash
|
200
211
|
end
|
@@ -2,17 +2,15 @@ class FulfillmentHistory
|
|
2
2
|
include Mongoid::Document
|
3
3
|
|
4
4
|
field :prescriptionNumber, type: String
|
5
|
-
field :provider, type: Hash
|
6
|
-
field :dispensingPharmacyLocation, type: Hash
|
7
5
|
field :dispenseDate, type: Integer
|
8
6
|
field :quantityDispensed, type: Hash
|
9
7
|
field :fillNumber, type: Integer
|
10
|
-
field :fillStatus, type:
|
8
|
+
field :fillStatus, type: String
|
11
9
|
|
10
|
+
belongs_to :provider, class_name: "Provider"
|
11
|
+
|
12
12
|
alias :prescription_number :prescriptionNumber
|
13
13
|
alias :prescription_number= :prescriptionNumber=
|
14
|
-
alias :dispensing_pharmacy_location :dispensingPharmacyLocation
|
15
|
-
alias :dispensing_pharmacy_location= :dispensingPharmacyLocation=
|
16
14
|
alias :dispense_date :dispenseDate
|
17
15
|
alias :dispense_date= :dispenseDate=
|
18
16
|
alias :quantity_dispensed :quantityDispensed
|
@@ -1,10 +1,16 @@
|
|
1
1
|
class Immunization < Entry
|
2
2
|
field :refusalInd, type: Boolean
|
3
|
-
field :performer, type: Hash
|
4
3
|
field :refusalReason, type: Hash
|
4
|
+
field :seriesNumber, type: Integer
|
5
|
+
|
6
|
+
belongs_to :performer, class_name: "Provider"
|
7
|
+
|
8
|
+
embeds_one :medication_product
|
5
9
|
|
6
10
|
alias :refusal_ind :refusalInd
|
7
11
|
alias :refusal_ind= :refusalInd=
|
8
12
|
alias :refusal_reason :refusalReason
|
9
13
|
alias :refusal_reason= :refusalReason=
|
14
|
+
alias :series_number :seriesNumber
|
15
|
+
alias :series_number= :seriesNumber=
|
10
16
|
end
|
@@ -2,11 +2,11 @@ class Medication < Entry
|
|
2
2
|
field :administrationTiming, type: Hash
|
3
3
|
field :freeTextSig, type: String
|
4
4
|
field :dose, type: Hash
|
5
|
-
field :brandName, type: String
|
6
5
|
field :typeOfMedication, type: Hash
|
7
6
|
field :statusOfMedication, type: Hash
|
8
7
|
embeds_many :fulfillmentHistory, class_name: 'FulfillmentHistory'
|
9
8
|
embeds_many :orderInformation, class_name: 'OrderInformation'
|
9
|
+
|
10
10
|
field :route, type: Hash
|
11
11
|
field :site, type: Hash
|
12
12
|
field :doseRestriction, type: Hash
|
@@ -17,13 +17,12 @@ class Medication < Entry
|
|
17
17
|
field :reaction, type: Hash
|
18
18
|
field :deliveryMethod, type: Hash
|
19
19
|
field :patientInstructions, type: String
|
20
|
+
field :doseIndicator, type: String
|
20
21
|
|
21
22
|
alias :administration_timing :administrationTiming
|
22
23
|
alias :administration_timing= :administrationTiming=
|
23
24
|
alias :free_text_sig :freeTextSig
|
24
25
|
alias :free_text_sig= :freeTextSig=
|
25
|
-
alias :brand_name :brandName
|
26
|
-
alias :brand_name= :brandName=
|
27
26
|
alias :type_of_medication :typeOfMedication
|
28
27
|
alias :type_of_medication= :typeOfMedication=
|
29
28
|
alias :status_of_medication :statusOfMedication
|
@@ -42,4 +41,6 @@ class Medication < Entry
|
|
42
41
|
alias :delivery_method= :deliveryMethod=
|
43
42
|
alias :patient_instructions :patientInstructions
|
44
43
|
alias :patient_instructions= :patientInstructions=
|
44
|
+
alias :dose_indicator :doseIndicator
|
45
|
+
alias :dose_indicator= :doseIndicator=
|
45
46
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Metadata
|
2
|
+
class Author
|
3
|
+
include Mongoid::Document
|
4
|
+
|
5
|
+
embedded_in :pedigree, class_name: "Metadata::Pedigree"
|
6
|
+
|
7
|
+
Types = %w(authenticator authorcustodiandataEnterer informant
|
8
|
+
legalAuthenticator participant performer recordTarget)
|
9
|
+
|
10
|
+
field :name, type: String
|
11
|
+
field :type, type: String
|
12
|
+
field :role, type: String
|
13
|
+
|
14
|
+
validates_inclusion_of :type, in: Types, allow_nil: true
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Represents the metadata associated with a hData section
|
2
|
+
module Metadata
|
3
|
+
NS = 'http://www.hl7.org/schemas/hdata/2009/11/metadata'
|
4
|
+
|
5
|
+
class Base
|
6
|
+
include Mongoid::Document
|
7
|
+
|
8
|
+
field :mime_types, type: Array
|
9
|
+
field :confidentiality, type: String
|
10
|
+
field :original_creation_time, type: Time
|
11
|
+
|
12
|
+
embedded_in :entry
|
13
|
+
|
14
|
+
embeds_many :pedigrees, class_name: "Metadata::Pedigree"
|
15
|
+
embeds_many :modified_dates, class_name: "Metadata::ChangeInfo"
|
16
|
+
embeds_many :copied_dates, class_name: "Metadata::ChangeInfo"
|
17
|
+
embeds_many :linked_documents, class_name: "Metadata::LinkInfo"
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Metadata
|
2
|
+
class Pedigree
|
3
|
+
include Mongoid::Document
|
4
|
+
|
5
|
+
field :organization, type: String
|
6
|
+
field :signature, type: String
|
7
|
+
field :document_method, type: String
|
8
|
+
field :derived, type: Boolean
|
9
|
+
|
10
|
+
embeds_one :author, class_name: "Metadata::Author"
|
11
|
+
embeds_many :source_pedigrees, class_name: "Metadata::Pedigree"
|
12
|
+
embeds_many :source_documents, class_name: "Metadata::LinkInfo"
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -12,7 +12,11 @@ class Provider
|
|
12
12
|
|
13
13
|
validates_uniqueness_of :npi, allow_blank: true
|
14
14
|
|
15
|
-
|
15
|
+
embeds_many :addresses, as: :locatable
|
16
|
+
embeds_many :telecoms, as: :contactable
|
17
|
+
embeds_one :organization
|
18
|
+
|
19
|
+
|
16
20
|
def records(effective_date=nil)
|
17
21
|
Record.by_provider(self, effective_date)
|
18
22
|
end
|
@@ -21,6 +25,7 @@ class Provider
|
|
21
25
|
# checksum using the Luhn algorithm with additional special handling as described in
|
22
26
|
# https://www.cms.gov/NationalProvIdentStand/Downloads/NPIcheckdigit.pdf
|
23
27
|
def self.valid_npi?(npi)
|
28
|
+
return false unless npi
|
24
29
|
return false if npi.length != 10 and npi.length != 15
|
25
30
|
return false if npi.gsub(/\d/, '').length > 0 # npi must be all digits
|
26
31
|
return false if npi.length == 15 and (npi =~ /^80840/)==nil # 15 digit npi must start with 80840
|
@@ -15,15 +15,15 @@ class Record
|
|
15
15
|
|
16
16
|
embeds_many :allergies
|
17
17
|
embeds_many :care_goals, class_name: "Entry"
|
18
|
-
embeds_many :conditions
|
18
|
+
embeds_many :conditions
|
19
19
|
embeds_many :encounters
|
20
20
|
embeds_many :immunizations
|
21
21
|
embeds_many :medical_equipment, class_name: "Entry"
|
22
22
|
embeds_many :medications
|
23
23
|
embeds_many :procedures
|
24
24
|
embeds_many :results, class_name: "LabResult"
|
25
|
-
embeds_many :social_history
|
26
|
-
embeds_many :vital_signs
|
25
|
+
embeds_many :social_history
|
26
|
+
embeds_many :vital_signs
|
27
27
|
|
28
28
|
Sections = [:allergies, :care_goals, :conditions, :encounters, :immunizations, :medical_equipment,
|
29
29
|
:medications, :procedures, :results, :social_history, :vital_signs]
|
@@ -40,4 +40,14 @@ class Record
|
|
40
40
|
def over_18?
|
41
41
|
Time.at(birthdate) < Time.now.years_ago(18)
|
42
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def self.provider_queries(provider_id, effective_date)
|
47
|
+
{'$or' => [provider_query(provider_id, effective_date,effective_date), provider_query(provider_id, nil,effective_date), provider_query(provider_id, effective_date,nil)]}
|
48
|
+
end
|
49
|
+
def self.provider_query(provider_id, start_before, end_after)
|
50
|
+
{'provider_performances' => {'$elemMatch' => {'provider_id' => provider_id, '$and'=>[{'$or'=>[{'start_date'=>nil},{'start_date'=>{'$lt'=>start_before}}]}, {'$or'=>[{'end_date'=>nil},{'end_date'=> {'$gt'=>end_after}}]}] } }}
|
51
|
+
end
|
52
|
+
|
43
53
|
end
|
@@ -13,7 +13,9 @@ module HealthDataStandards
|
|
13
13
|
'2.16.840.1.113883.6.90' => 'ICD-10-CM',
|
14
14
|
'2.16.840.1.113883.6.14' => 'HCPCS',
|
15
15
|
'2.16.840.1.113883.6.59' => 'CVX',
|
16
|
-
'2.16.840.1.113883.5.83' => 'HITSP C80 Observation Status'
|
16
|
+
'2.16.840.1.113883.5.83' => 'HITSP C80 Observation Status',
|
17
|
+
"2.16.840.1.113883.3.26.1.1" => "NCI Thesaurus",
|
18
|
+
"2.16.840.1.113883.3.88.12.80.20" => "FDA"
|
17
19
|
}
|
18
20
|
|
19
21
|
# Returns the name of a code system given an oid
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<% tag_name ||= "address"%>
|
2
|
+
<<%=tag_name%>>
|
3
|
+
<% address.street.each do |street| %>
|
4
|
+
<streetAddressLine><%= street %></streetAddressLine>
|
5
|
+
<% end %>
|
6
|
+
<city><%= address.city %></city>
|
7
|
+
<state><%= address.state %></state>
|
8
|
+
<postalCode><%= address.zip %></postalCode>
|
9
|
+
</<%=tag_name%>>
|
@@ -20,7 +20,7 @@
|
|
20
20
|
<code nullFlavor="NA"/>
|
21
21
|
<statusCode code="active"/>
|
22
22
|
<effectiveTime>
|
23
|
-
<low
|
23
|
+
<low <%= value_or_null_flavor(entry.as_point_in_time) %>/>
|
24
24
|
</effectiveTime>
|
25
25
|
<entryRelationship typeCode="SUBJ" inversionInd="false">
|
26
26
|
<observation classCode="OBS" moodCode="EVN">
|
@@ -37,7 +37,7 @@
|
|
37
37
|
</text>
|
38
38
|
<statusCode code="completed"/>
|
39
39
|
<effectiveTime>
|
40
|
-
<low
|
40
|
+
<low <%= value_or_null_flavor(entry.as_point_in_time) %>/>
|
41
41
|
</effectiveTime>
|
42
42
|
<value xsi:type="CD" nullFlavor="UNK"/>
|
43
43
|
<participant typeCode="CSM">
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<allergy xmlns="urn:hl7-org:greencda:c32">
|
2
|
+
<id><%= allergy.id%></id>
|
3
|
+
<status><%= allergy.status%></status>
|
4
|
+
<%== code_display allergy, "tag_name" => "code", 'preferred_code_sets' => ['SNOMED-CT', "RxNorm", "FDA"] %>
|
5
|
+
<type><%= allergy.type%></type>
|
6
|
+
<effectiveTime>
|
7
|
+
<start><%= time_if_not_nil(allergy.start_time, allergy.time) %></start>
|
8
|
+
<end><%= time_if_not_nil(allergy.end_time) %></end>
|
9
|
+
</effectiveTime>
|
10
|
+
<%== code_display allergy, "tag_name" => "reaction", 'preferred_code_sets' => ['SNOMED-CT'] %>
|
11
|
+
<%== code_display allergy, "tag_name" => "severity", 'preferred_code_sets' => ['SNOMED-CT'] %>
|
12
|
+
</allergy>
|
13
|
+
|
@@ -17,7 +17,7 @@
|
|
17
17
|
<id root="<%= UUID.generate %>"/>
|
18
18
|
<%== code_display(entry, :preferred_code_sets => ['SNOMED-CT']) %>
|
19
19
|
<statusCode code="new"/>
|
20
|
-
<effectiveTime
|
20
|
+
<effectiveTime <%= value_or_null_flavor(entry.as_point_in_time) %>/>
|
21
21
|
</observation>
|
22
22
|
</entry>
|
23
23
|
<% end -%>
|