Class: HealthDataStandards::Import::CCR::PatientImporter

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/health-data-standards/import/ccr/patient_importer.rb

Overview

This class is the central location for taking an ASTM CCR XML document and converting it into the processed form we store in MongoDB. The class does this by running each measure independently on the XML document

This class is a Singleton. It should be accessed by calling PatientImporter.instance

Constant Summary collapse

Gender =
{"male" => "M", "female" => "F"}

Instance Method Summary collapse

Constructor Details

#initialize(check_usable = true) ⇒ PatientImporter

Creates a new PatientImporter with the following XPath expressions used to find content in an ASTM CCR

Encounter entries

//ccr:Encounters/ccr:Encounter

Procedure entries

//ccr:Procedures/ccr:Procedure

Result entries -

//ccr:Results/ccr:Result

Vital sign entries

//ccr:VitalSigns/ccr:Result

Medication entries

//ccr:Medications/ccr:Medication

Codes for medications are found in the Product sections

./ccr:Product

Condition entries

//ccr:Problems/ccr:Problem

Social History entries

//ccr:SocialHistory/ccr:SocialHistoryElement

Care Goal entries

//ccr:Goals/ccr:Goal

Allergy entries

//ccr:Alerts/ccr:Alert

Immunization entries

//ccr:Immunizations/ccr:Immunization

Codes for immunizations are found in the substanceAdministration with the following relative XPath

./ccr:Product


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/health-data-standards/import/ccr/patient_importer.rb', line 55

def initialize (check_usable = true)
  @measure_importers = {}
  @section_importers = {}
  @section_importers[:encounters] = SimpleImporter.new("//ccr:Encounters/ccr:Encounter",:encounters)
  @section_importers[:procedures] = SimpleImporter.new("//ccr:Procedures/ccr:Procedure",:procedures)
  @section_importers[:results] = ResultImporter.new("//ccr:Results/ccr:Result",:results)
  @section_importers[:vital_signs] = ResultImporter.new("//ccr:VitalSigns/ccr:Result",:vital_signs)
  @section_importers[:medications] = ProductImporter.new("//ccr:Medications/ccr:Medication", :medications)
  @section_importers[:conditions] = SimpleImporter.new("//ccr:Problems/ccr:Problem",:conditions)
  @section_importers[:social_history] = SimpleImporter.new("//ccr:SocialHistory/ccr:SocialHistoryElement", :social_history)
  @section_importers[:care_goals] = SimpleImporter.new("//ccr:Goals/ccr:Goal",:care_goals)
  @section_importers[:medical_equipment] = ProductImporter.new("//ccr:Equipment/ccr:EquipmentElement",:medical_equipment)
  @section_importers[:allergies] = SimpleImporter.new("//ccr:Alerts/ccr:Alert",:allergies)
  @section_importers[:immunizations] = ProductImporter.new("//ccr:Immunizations/ccr:Immunization",:immunizations)
end

Instance Method Details

#add_measure(measure_id, importer) ⇒ Object

Adds a measure to run on a CCR that is passed in

Parameters:

  • measure (MeasureBase)

    an Class that can extract information from a CCR that is necessary to calculate the measure



163
164
165
# File 'lib/health-data-standards/import/ccr/patient_importer.rb', line 163

def add_measure(measure_id, importer)
  @measure_importers[measure_id] = importer
end

#check_usable(check_usable_entries) ⇒ Object

Parameters:

  • value (boolean)

    for check_usable_entries…importer uses true, stats uses false



73
74
75
76
77
# File 'lib/health-data-standards/import/ccr/patient_importer.rb', line 73

def check_usable(check_usable_entries)
  @section_importers.each_pair do |section, importer|
    importer.check_for_usable = check_usable_entries
  end
end

#create_hash(doc, check_usable_entries = false) ⇒ Hash

Create a simple representation of the patient from an ASTM CCR

Parameters:

  • doc (Nokogiri::XML::Document)

    It is expected that the root node of this document will have the “ccr” namespace registered to “”urn:astm-org:CCR“”

Returns:

  • (Hash)

    a represnetation of the patient with symbols as keys for each section



172
173
174
175
176
177
178
179
# File 'lib/health-data-standards/import/ccr/patient_importer.rb', line 172

def create_hash(doc, check_usable_entries = false)
  ccr_patient = {}
  @section_importers.each_pair do |section, importer|
    importer.check_for_usable = check_usable_entries
    ccr_patient[section] = importer.create_entries(doc)
  end
  ccr_patient
end

#get_demographics(patient, doc, patient_id_xpath) ⇒ Object

Inspects a CCR document and populates the patient Hash with first name, last name birth date and gender.

Parameters:

  • patient (Hash)

    A hash that is used to represent the patient

  • doc (Nokogiri::XML::Node)

    The CCR document parsed by Nokogiri



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/health-data-standards/import/ccr/patient_importer.rb', line 186

def get_demographics(patient, doc, patient_id_xpath)
  patientActorID = doc.at_xpath("//ccr:ContinuityOfCareRecord/ccr:Patient/ccr:ActorID").content
  patientActor = doc.at_xpath("//ccr:ContinuityOfCareRecord/ccr:Actors/ccr:Actor[ccr:ActorObjectID = \"#{patientActorID}\"]")
  patientID = patientActor.at_xpath(patient_id_xpath).try(:content)
  patientID ||= patientActorID
  
  name_element = patientActor.at_xpath('./ccr:Person/ccr:Name')
  
  if name_element
    if name_element.at_xpath("./ccr:CurrentName")
      patient['first'] = name_element.at_xpath('./ccr:CurrentName/ccr:Given').try(:content)
      patient['last'] = name_element.at_xpath('./ccr:CurrentName/ccr:Family').try(:content)
    elsif name_element.at_xpath("./ccr:DisplayName")
      # this will not work in all cases, but we're using it as a last resort if no CurrentName is found
      first, last = name_element.at_xpath("./ccr:DisplayName").content.split(" ")
      patient['first'] = first.strip
      patient['last'] = last.strip
    end
  end
      
  
  birthdate = patientActor.at_xpath('./ccr:Person//ccr:DateOfBirth/ccr:ExactDateTime | ./ccr:Person//ccr:DateOfBirth/ccr:ApproximateDateTime')
  patient['birthdate'] = Time.parse(birthdate.content).to_i if birthdate
  
  gender_string = patientActor.at_xpath('./ccr:Person/ccr:Gender/ccr:Text').content.downcase
  patient['gender'] =  Gender[gender_string.downcase]
  #race_node = doc.at_xpath('/ccr:placeholder')    #how do you find this?
  race = doc.at_xpath('//ccr:SocialHistory/ccr:SocialHistoryElement[./ccr:Type/ccr:Text = "Race"]/ccr:Description/ccr:Code[./ccr:CodingSystem = "CDC-RE"]/ccr:Value')
  ethnicity = doc.at_xpath('//ccr:SocialHistory/ccr:SocialHistoryElement[./ccr:Type/ccr:Text = "Ethnicity"]/ccr:Description/ccr:Code[./ccr:CodingSystem = "CDC-RE"]/ccr:Value')
  
  if ethnicity
    patient[:ethnicity] = {"code" => ethnicity.text, "codeSystem" => 'CDC-RE'}
  end
 
  
  if race
     patient[:race] = {"code" => race.text, "codeSystem" => 'CDC-RE'}
  end

 
 
  #ethnicity_node = doc.at_xpath()
  

  # languages = doc.at_xpath()
  patient['languages'] = nil
 
  patient['medical_record_number'] = patientID
end

#parse_ccr(doc, patient_id_xpath = "//ccr:ContinuityOfCareRecord/ccr:Patient/ccr:ActorID") ⇒ Hash

Parses a ASTM CCR document and returns a Hash of of the patient.

Parameters:

  • doc (Nokogiri::XML::Document)

    It is expected that the root node of this document will have the “ccr” namespace registered to “”urn:astm-org:CCR“”

Returns:

  • (Hash)

    a representation of the patient that can be inserted into MongoDB



84
85
86
87
88
89
90
# File 'lib/health-data-standards/import/ccr/patient_importer.rb', line 84

def parse_ccr(doc, patient_id_xpath="//ccr:ContinuityOfCareRecord/ccr:Patient/ccr:ActorID")
  ccr_patient = {}
  entries = create_hash(doc)
  get_demographics(ccr_patient, doc, patient_id_xpath)
  process_events(ccr_patient, entries)
  Record.new(ccr_patient)
end

#process_events(patient_record, entries) ⇒ Object

Adds the entries and denormalized measure information to the patient_record. Each Entry will be converted to a Hash and stored in an Array under the appropriate section key, such as medications. Measure information is listed under the measures key which has a Hash value. The Hash has the measure id as a key, and the denormalized measure information as a value

Parameters:

  • patient_record
    • Hash with basic patient demographic information



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/health-data-standards/import/ccr/patient_importer.rb', line 123

def process_events(patient_record, entries)
  patient_record['measures'] = {}
  @measure_importers.each_pair do |measure_id, importer|
    patient_record['measures'][measure_id] = importer.parse(entries)
  end


  entries.each_pair do |key, value|
    patient_record[key] = value.map do |e|
      if e.usable?
        e.to_hash
      else
        nil
      end
    end.compact
  end
  patient_record
end