Class: Measure

Inherits:
Object
  • Object
show all
Includes:
Mongoid::Attributes::Dynamic, Mongoid::Document, Mongoid::Timestamps
Defined in:
lib/models/measure.rb

Constant Summary collapse

DEFAULT_EFFECTIVE_DATE =
Time.gm(2012,12,31,23,59).to_i
MP_START_DATE =
Time.gm(2012,1,1,0,0).to_i
TYPES =
["ep", "eh"]
TYPE_MAP =
{
  'problem' => 'conditions',
  'encounter' => 'encounters',
  'labresults' => 'results',
  'procedure' => 'procedures',
  'medication' => 'medications',
  'rx' => 'medications',
  'demographics' => 'characteristic',
  'derived' => 'derived'
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.for_patient(record) ⇒ Object

Find the measures matching a patient



85
86
87
# File 'lib/models/measure.rb', line 85

def self.for_patient(record)
  where user_id: record.user_id, hqmf_set_id: { '$in' => record.measure_ids }
end

Instance Method Details

#all_data_criteriaObject



129
130
131
# File 'lib/models/measure.rb', line 129

def all_data_criteria
  as_hqmf_model.all_data_criteria
end

#as_hqmf_modelObject

Returns the hqmf-parser’s ruby implementation of an HQMF document. Rebuild from population_criteria, data_criteria, and measure_period JSON



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/models/measure.rb', line 102

def as_hqmf_model
  json = {
    "id" => self.measure_id,
    "title" => self.title,
    "description" => self.description,
    "population_criteria" => self.population_criteria,
    "data_criteria" => self.data_criteria,
    "source_data_criteria" => self.source_data_criteria,
    "measure_period" => self.measure_period,
    "attributes" => self.measure_attributes,
    "populations" => self.populations,
    "hqmf_id" => self.hqmf_id,
    "hqmf_set_id" => self.hqmf_set_id,
    "hqmf_version_number" => self.hqmf_version_number,
    "cms_id" => self.cms_id
  }

  HQMF::Document.from_json(json)
end

#as_javascript(population_index, check_crosswalk = false) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/models/measure.rb', line 133

def as_javascript(population_index, check_crosswalk=false)
  options = {
    value_sets: value_sets,
    episode_ids: episode_ids,
    continuous_variable: continuous_variable,
    force_sources: force_sources,
    custom_functions: custom_functions,
    check_crosswalk: check_crosswalk
  }

  HQMF2JS::Generator::Execution.logic(as_hqmf_model, population_index, options)
end

#calculate_complexityObject



233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/models/measure.rb', line 233

def calculate_complexity
  self.complexity = { populations: [], variables: [] }
  self.population_criteria.each do |name, precondition|
    complexity = precondition_complexity(precondition)
    self.complexity[:populations] << { name: name, complexity: complexity }
  end
  self.source_data_criteria.each do |reference, criteria|
    next unless criteria['variable']
    name = criteria['description']
    complexity = data_criteria_complexity(reference, calculating_variable: true)
    self.complexity[:variables] << { name: name, complexity: complexity }
  end
  self.complexity
end

#clear_cached_jsObject

Clear any cached JavaScript, forcing it to be generated next time it’s requested



70
71
72
73
# File 'lib/models/measure.rb', line 70

def clear_cached_js
  self.map_fns.map! { nil }
  self.save
end

#criteria_keys_by_populationObject

Return the list of all data criteria keys in this measure, indexed by population code



180
181
182
183
184
185
186
# File 'lib/models/measure.rb', line 180

def criteria_keys_by_population
  criteria_keys_by_population = {}
  population_criteria.each do |name, precondition|
    criteria_keys_by_population[name] = precondition_criteria_keys(precondition).reject { |ck| ck == 'MeasurePeriod' }
  end
  criteria_keys_by_population
end

#data_criteria_complexity(criteria_reference, options = {}) ⇒ Object



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/models/measure.rb', line 203

def data_criteria_complexity(criteria_reference, options = {})
  options.reverse_merge! calculating_variable: false
  # We want to calculate the number of branching paths, which we can normally do by counting leaf nodes.
  # This is more complicated for data criteria because, in addition to direct children, the criteria can
  # also have temporal references, which can themselves branch. Our approach is to calculate an initial
  # number of leaf nodes through looking at direct children and then seeing if any additional leaves are
  # added through temporal references. A temporal reference that doesn't branch doesn't add a leaf node.
  # Finally, this reference may be a variable, in which case we consider this a leaf node *unless* we are
  # explicitly calculating the complexity of the variable itself
  if criteria = self.data_criteria[criteria_reference]
    complexity = if criteria['children_criteria'].present? && (!criteria['variable'] || options[:calculating_variable])
                   criteria['children_criteria'].map { |c| data_criteria_complexity(c) }.sum
                 else
                   1
                 end
    complexity + if criteria['temporal_references'].present?
                   criteria['temporal_references'].map { |tr| data_criteria_complexity(tr['reference']) - 1 }.sum
                 else
                   0
                 end
  else
    1
  end
end

#data_criteria_criteria_keys(criteria_reference) ⇒ Object

Given a data criteria, return the list of all data criteria keys referenced within, either through children criteria or temporal references; this includes the passed in criteria reference



155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/models/measure.rb', line 155

def data_criteria_criteria_keys(criteria_reference)
  criteria_keys = [criteria_reference]
  if criteria = self.data_criteria[criteria_reference]
    if criteria['children_criteria'].present?
      criteria_keys.concat(criteria['children_criteria'].map { |c| data_criteria_criteria_keys(c) }.flatten)
    end
    if criteria['temporal_references'].present?
      criteria_keys.concat(criteria['temporal_references'].map { |tr| data_criteria_criteria_keys(tr['reference']) }.flatten)
    end
  end
  criteria_keys
end

#diff(other) ⇒ Object

Compute a simplified diff hash for Complexity Dashboard usage; stored within measure.latest_diff



267
268
269
# File 'lib/models/measure.rb', line 267

def diff(other)
  HQMF::Measure::LogicExtractor.get_measure_logic_diff(self,other,true)
end

#extract_measure_logicObject



254
255
256
257
258
259
260
261
262
263
264
# File 'lib/models/measure.rb', line 254

def extract_measure_logic
  self.measure_logic = []
  # There are occasional issues extracting measure logic; while we want to fix them we also don't want logic
  # extraction issues to hold up loading or updating a measure
  begin
    self.measure_logic.concat HQMF::Measure::LogicExtractor.new().population_logic(self)
  rescue => e
    self.measure_logic << "Error parsing measure logic: #{e.message}"
  end
  self.measure_logic
end

#generate_js(options = {}) ⇒ Object

Generate and cache all the javascript for the measure, optionally clearing the cache first



65
66
67
# File 'lib/models/measure.rb', line 65

def generate_js(options = {})
  populations.each_with_index { |p, idx| map_fn(idx, options) }
end

#map_fn(population_index, options = {}) ⇒ Object

Cache the generated JS code, with optional options to manipulate cached result



54
55
56
57
58
59
60
61
62
# File 'lib/models/measure.rb', line 54

def map_fn(population_index, options = {})
  options.assert_valid_keys :clear_db_cache, :cache_result_in_db, :check_crosswalk
  # Defaults are: don't clear the cache, do cache the result in the DB, use user specified crosswalk setting
  options.reverse_merge! clear_db_cache: false, cache_result_in_db: true, check_crosswalk: !!self.user.try(:crosswalk_enabled)
  self.map_fns[population_index] = nil if options[:clear_db_cache]
  self.map_fns[population_index] ||= as_javascript(population_index, options[:check_crosswalk])
  save if changed? && options[:cache_result_in_db]
  self.map_fns[population_index]
end

#measure_json(population_index = 0, check_crosswalk = false) ⇒ Object



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/models/measure.rb', line 273

def measure_json(population_index=0,check_crosswalk=false)
    options = {
value_sets: value_sets,
episode_ids: episode_ids,
continuous_variable: continuous_variable,
force_sources: force_sources,
custom_functions: custom_functions,
check_crosswalk: check_crosswalk
    }
  population_index ||= 0
  json = {
    id: self.hqmf_id,
    nqf_id: self.measure_id,
    hqmf_id: self.hqmf_id,
    hqmf_set_id: self.hqmf_set_id,
    hqmf_version_number: self.hqmf_version_number,
    cms_id: self.cms_id,
    name: self.title,
    description: self.description,
    type: self.type,
    category: self.category,
    map_fn: HQMF2JS::Generator::Execution.measure_js(self.as_hqmf_model, population_index, options),
    continuous_variable: self.continuous_variable,
    episode_of_care: self.episode_of_care,
    hqmf_document:  self.as_hqmf_model.to_json
  }
  
  if (self.populations.count > 1)
    sub_ids = ('a'..'az').to_a
    json[:sub_id] = sub_ids[population_index]
    population_title = self.populations[population_index]['title']
    json[:subtitle] = population_title
    json[:short_subtitle] = population_title
  end

  if self.continuous_variable
    observation = self.population_criteria[self.populations[population_index][HQMF::PopulationCriteria::OBSERV]]
    json[:aggregator] = observation['aggregator']
  end
  
  json[:oids] = self.value_sets.map{|value_set| value_set.oid}.uniq
  
  population_ids = {}
  HQMF::PopulationCriteria::ALL_POPULATION_CODES.each do |type|
    population_key = self.populations[population_index][type]
    population_criteria = self.population_criteria[population_key]
    if (population_criteria)
      population_ids[type] = population_criteria['hqmf_id']
    end
  end
  stratification = self['populations'][population_index]['stratification']
  if stratification
    population_ids['stratification'] = stratification 
  end
  json[:population_ids] = population_ids
  json
end

#precondition_complexity(precondition) ⇒ Object

Measure Complexity Analysis ##############################



190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/models/measure.rb', line 190

def precondition_complexity(precondition)
  # We want to calculate the number of branching paths; we can do that by simply counting the leaf nodes.
  # Any children of this particular node can appear either through child preconditions or by reference to a
  # data criteria. ASSERTION: a precondition can never both have child preconditions and a data criteria.
  if precondition['preconditions'] && precondition['preconditions'].size > 0
    precondition['preconditions'].map { |p| precondition_complexity(p) }.sum
  elsif precondition['reference']
    data_criteria_complexity(precondition['reference'])
  else
    1
  end
end

#precondition_criteria_keys(precondition) ⇒ Object

Given a precondition, return the list of all data criteria keys referenced within



169
170
171
172
173
174
175
176
177
# File 'lib/models/measure.rb', line 169

def precondition_criteria_keys(precondition)
  if precondition['preconditions'] && precondition['preconditions'].size > 0
    precondition['preconditions'].map { |p| precondition_criteria_keys(p) }.flatten
  elsif precondition['reference']
    data_criteria_criteria_keys(precondition['reference'])
  else
    []
  end
end

#set_continuous_variableObject



146
147
148
149
# File 'lib/models/measure.rb', line 146

def set_continuous_variable
  self.continuous_variable = populations.map {|x| x.keys}.flatten.uniq.include? HQMF::PopulationCriteria::MSRPOPL
  true
end

#value_setsObject



122
123
124
125
126
127
# File 'lib/models/measure.rb', line 122

def value_sets
  options = { oid: value_set_oids }
  options[:user_id] = user.id if user?
  @value_sets ||= HealthDataStandards::SVS::ValueSet.in(options)
  @value_sets
end