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.
Files changed (79) hide show
  1. data/Gemfile +9 -1
  2. data/Rakefile +14 -4
  3. data/lib/health-data-standards.rb +23 -0
  4. data/lib/health-data-standards/export/ccr.rb +55 -56
  5. data/lib/health-data-standards/export/hdata/metadata.rb +16 -0
  6. data/lib/health-data-standards/export/html.rb +21 -0
  7. data/lib/health-data-standards/export/template_helper.rb +3 -0
  8. data/lib/health-data-standards/export/view_helper.rb +29 -5
  9. data/lib/health-data-standards/import/c32/condition_importer.rb +31 -33
  10. data/lib/health-data-standards/import/c32/encounter_importer.rb +4 -6
  11. data/lib/health-data-standards/import/c32/medication_importer.rb +1 -5
  12. data/lib/health-data-standards/import/c32/organization_importer.rb +23 -0
  13. data/lib/health-data-standards/import/c32/patient_importer.rb +1 -4
  14. data/lib/health-data-standards/import/c32/provider_importer.rb +43 -30
  15. data/lib/health-data-standards/import/c32/section_importer.rb +20 -41
  16. data/lib/health-data-standards/import/ccr/patient_importer.rb +27 -10
  17. data/lib/health-data-standards/import/ccr/provider_importer.rb +29 -41
  18. data/lib/health-data-standards/import/ccr/section_importer.rb +38 -27
  19. data/lib/health-data-standards/import/green_c32/allergy_importer.rb +20 -0
  20. data/lib/health-data-standards/import/green_c32/condition_importer.rb +2 -3
  21. data/lib/health-data-standards/import/green_c32/encounter_importer.rb +42 -0
  22. data/lib/health-data-standards/import/green_c32/immunization_importer.rb +23 -0
  23. data/lib/health-data-standards/import/green_c32/medication_importer.rb +69 -0
  24. data/lib/health-data-standards/import/green_c32/procedure_importer.rb +35 -0
  25. data/lib/health-data-standards/import/green_c32/result_importer.rb +21 -8
  26. data/lib/health-data-standards/import/green_c32/section_importer.rb +55 -9
  27. data/lib/health-data-standards/import/green_c32/social_history_importer.rb +18 -0
  28. data/lib/health-data-standards/import/green_c32/vital_sign_importer.rb +21 -0
  29. data/lib/health-data-standards/import/hdata/metadata_importer.rb +82 -0
  30. data/lib/health-data-standards/import/provider_import_utils.rb +23 -0
  31. data/lib/health-data-standards/models/address.rb +11 -0
  32. data/lib/health-data-standards/models/allergy.rb +1 -0
  33. data/lib/health-data-standards/models/condition.rb +1 -1
  34. data/lib/health-data-standards/models/encounter.rb +11 -6
  35. data/lib/health-data-standards/models/entry.rb +16 -5
  36. data/lib/health-data-standards/models/fulfillment_history.rb +3 -5
  37. data/lib/health-data-standards/models/immunization.rb +7 -1
  38. data/lib/health-data-standards/models/medication.rb +4 -3
  39. data/lib/health-data-standards/models/metadata/author.rb +16 -0
  40. data/lib/health-data-standards/models/metadata/base.rb +20 -0
  41. data/lib/health-data-standards/models/metadata/change_info.rb +9 -0
  42. data/lib/health-data-standards/models/metadata/link_info.rb +9 -0
  43. data/lib/health-data-standards/models/metadata/pedigree.rb +15 -0
  44. data/lib/health-data-standards/models/organization.rb +8 -0
  45. data/lib/health-data-standards/models/procedure.rb +5 -2
  46. data/lib/health-data-standards/models/provider.rb +6 -1
  47. data/lib/health-data-standards/models/record.rb +13 -3
  48. data/lib/health-data-standards/models/social_history.rb +3 -0
  49. data/lib/health-data-standards/models/telecom.rb +9 -0
  50. data/lib/health-data-standards/models/vital_sign.rb +2 -0
  51. data/lib/health-data-standards/util/code_system_helper.rb +3 -1
  52. data/templates/_address.gc32.erb +9 -0
  53. data/templates/_allergies.c32.erb +2 -2
  54. data/templates/_allergy.gc32.erb +13 -0
  55. data/templates/_care_goals.c32.erb +1 -1
  56. data/templates/_condition.gc32.erb +6 -6
  57. data/templates/_conditions.c32.erb +2 -2
  58. data/templates/_encounter.gc32.erb +32 -0
  59. data/templates/_encounters.c32.erb +1 -1
  60. data/templates/_immunization.gc32.erb +9 -0
  61. data/templates/_immunizations.c32.erb +1 -1
  62. data/templates/_medical_equipment.c32.erb +1 -1
  63. data/templates/_medication.gc32.erb +60 -0
  64. data/templates/_medications.c32.erb +1 -1
  65. data/templates/_narrative_block.c32.erb +1 -1
  66. data/templates/_organization.gc32.erb +10 -0
  67. data/templates/_pedigree.hdata.erb +24 -0
  68. data/templates/_procedure.gc32.erb +8 -0
  69. data/templates/_procedures.c32.erb +1 -1
  70. data/templates/_provider.gc32.erb +19 -0
  71. data/templates/_results.c32.erb +1 -1
  72. data/templates/_social_history.c32.erb +1 -1
  73. data/templates/_social_history.gc32.erb +6 -0
  74. data/templates/_telecom.gc32.erb +1 -0
  75. data/templates/_vital_sign.gc32.erb +12 -0
  76. data/templates/_vital_signs.c32.erb +1 -1
  77. data/templates/metadata.hdata.erb +35 -0
  78. data/templates/show.html.erb +287 -0
  79. 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
@@ -0,0 +1,11 @@
1
+ class Address
2
+ include Mongoid::Document
3
+
4
+ field :street, type: Array
5
+ field :city, type: String
6
+ field :state, type: String
7
+ field :zip, type: String
8
+ field :country, type: String
9
+
10
+ embedded_in :locatable, polymorphic: true
11
+ end
@@ -1,4 +1,5 @@
1
1
  class Allergy < Entry
2
+ field :type, type: String
2
3
  field :reaction, type: Hash
3
4
  field :severity, type: Hash
4
5
  end
@@ -3,7 +3,7 @@ class Condition < Entry
3
3
  field :causeOfDeath, type: Boolean
4
4
  field :name, type: String
5
5
 
6
- # embeds_many :treating_provider, class_name: "Provider"
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 :dischargeDisp, type: Hash
6
- embeds_one :reason, class_name: "Entry"
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 :discharge_disp :dischargeDisp
11
- alias :discharge_disp= :dischargeDisp=
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
- matching_code_sets = preferred_code_sets & codes.keys
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' => codes[code_set].first, 'code_set' => code_set}
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: Hash
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,9 @@
1
+ module Metadata
2
+ class ChangeInfo
3
+ include Mongoid::Document
4
+
5
+ embeds_one :pedigree, class_name: "Metadata::Pedigree"
6
+
7
+ field :timestamp, type: Time
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Metadata
2
+ class LinkInfo
3
+ include Mongoid::Document
4
+
5
+ field :href, type: String
6
+ field :extension, type: String
7
+
8
+ end
9
+ 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
@@ -0,0 +1,8 @@
1
+ class Organization
2
+ include Mongoid::Document
3
+
4
+ field :name, type: String
5
+
6
+ embeds_many :addresses, as: :locatable
7
+ embeds_many :telecoms, as: :contactable
8
+ end
@@ -1,4 +1,7 @@
1
1
  class Procedure < Entry
2
- field :performer, type: Hash
3
- field :site, type: Hash
2
+ field :type, type: String
3
+ field :site, type: Hash
4
+ field :description, type: String
5
+
6
+ belongs_to :performer, class_name: "Provider"
4
7
  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, class_name: "Entry"
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, class_name: "Entry"
26
- embeds_many :vital_signs, class_name: "Entry"
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
@@ -0,0 +1,3 @@
1
+ class SocialHistory < Entry
2
+ field :type, type: Hash
3
+ end
@@ -0,0 +1,9 @@
1
+ class Telecom
2
+ include Mongoid::Document
3
+
4
+ field :use, type: String
5
+ field :value, type: String
6
+ field :preferred, type: Boolean
7
+
8
+ embedded_in :contactable, polymorphic: true
9
+ end
@@ -0,0 +1,2 @@
1
+ class VitalSign < LabResult
2
+ 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 value="<%= Time.at(entry.time).utc.to_formatted_s(:number) %>"/>
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 value="<%= Time.at(entry.time).utc.to_formatted_s(:number) %>"/>
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 value="<%= Time.at(entry.time).utc.to_formatted_s(:number) %>"/>
20
+ <effectiveTime <%= value_or_null_flavor(entry.as_point_in_time) %>/>
21
21
  </observation>
22
22
  </entry>
23
23
  <% end -%>