Class: ObservationMatrix

Inherits:
ApplicationRecord show all
Includes:
Housekeeping, Shared::AlternateValues, Shared::Citations, Shared::DataAttributes, Shared::Identifiers, Shared::IsData, Shared::Notes, Shared::Tags
Defined in:
app/models/observation_matrix.rb

Overview

A view to a set of Observations.

Constant Summary collapse

ALTERNATE_VALUES_FOR =
[:name].freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Shared::IsData

#errors_excepting, #full_error_messages_excepting, #identical, #is_community?, #is_destroyable?, #is_editable?, #is_in_use?, #is_in_users_projects?, #metamorphosize, #similar

Methods included from Shared::AlternateValues

#all_values_for, #alternate_valued?

Methods included from Shared::DataAttributes

#import_attributes, #internal_attributes, #keyword_value_hash, #reject_data_attributes

Methods included from Shared::Notes

#concatenated_notes_string, #reject_notes

Methods included from Shared::Tags

#reject_tags, #tag_with, #tagged?, #tagged_with?

Methods included from Shared::Identifiers

#dwc_occurrence_id, #identified?, #next_by_identifier, #previous_by_identifier, #reject_identifiers, #uri, #uuid

Methods included from Shared::Citations

#cited?, #mark_citations_for_destruction, #nomenclature_date, #origin_citation_source_id, #reject_citations, #requires_citation?, #sources_by_topic_id

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods inherited from ApplicationRecord

transaction_with_retry

Class Method Details

.batch_add(params) ⇒ Object



320
321
322
323
324
# File 'app/models/observation_matrix.rb', line 320

def self.batch_add(params)
  return false if params[:observation_matrix_id].blank?
  o = ObservationMatrix.find_by(project_id: params[:project_id], id: params[:observation_matrix_id])
  o.batch_populate(params)
end

.batch_create(params) ⇒ Object



326
327
328
329
330
331
332
333
# File 'app/models/observation_matrix.rb', line 326

def self.batch_create(params)
  o = ObservationMatrix.create(params.require(:observation_matrix).permit(:name))
  if o.persisted?
    o.batch_populate(params)
  else
    o.errors.full_messages
  end
end

Instance Method Details

#batch_populate(params) ⇒ Object



270
271
272
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
# File 'app/models/observation_matrix.rb', line 270

def batch_populate(params)
  queries = params.keys.select{|a| a =~ /_query/ }
  return false if queries.size != 1

  result = {
    rows: 0,
    columns: 0,
    observation_matrix_id: id,
    observation_matrix_name: name
  }

  otus = []
  descriptors = []
  collection_objects = []
  extracts = []

  case queries[0]

  when 'otu_query'
    otus = ::Queries::Otu::Filter.new(params[:otu_query]).all
  when 'descriptor_query'
    descriptors = ::Queries::Descriptor::Filter.new(params[:descriptor_query]).all
  when 'observation_query'

    otus = ::Queries::Otu::Filter.new(observation_query: params[:observation_query]).all
    descriptors = ::Queries::Descriptor::Filter.new(observation_query: params[:observation_query]).all
    collection_objects = ::Queries::CollectionObject::Filter.new(observation_query: params[:observation_query]).all
    extracts = ::Queries::Extract::Filter.new(observation_query: params[:observation_query]).all
  when 'collection_object_query'
    collection_objects = ::Queries::CollectionObject::Filter.new(params[:collection_object_query]).all
  when 'extract_query'
    extracts = ::Queries::Extract::Filter.new(params[:extract_query]).all
  end

  [otus, collection_objects, extracts].each do |t|
    t.each do |i|
      # Fail silently
      j = ObservationMatrixRowItem::Single.create(observation_matrix: self, observation_object: i)
      result[:rows] += 1 if j.persisted?
    end
  end

  descriptors.each do |d|
    j = ObservationMatrixColumnItem::Single::Descriptor.create(observation_matrix: self, descriptor: d)
    result[:columns] += 1 if j.persisted?
  end

  result
end

#cell_countObject

TODO: helper method



75
76
77
# File 'app/models/observation_matrix.rb', line 75

def cell_count
  observation_matrix_rows.count * observation_matrix_columns.count
end

#character_statesObject



70
71
72
# File 'app/models/observation_matrix.rb', line 70

def character_states
  CharacterState.joins(descriptor: [:observation_matrices]).merge(descriptors)
end

#continuous_descriptorsObject



44
45
46
# File 'app/models/observation_matrix.rb', line 44

def continuous_descriptors
  descriptors.where(type: 'Descriptor::Continuous').order('observation_matrix_columns.position')
end

#empty_grid(opts) ⇒ Array

Returns:

  • (Array)


213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'app/models/observation_matrix.rb', line 213

def empty_grid(opts)
  re = if opts[:row_end] == 'all'
         observation_matrix_rows.count + 1
       else
         opts[:row_end]
       end

  ce = if opts[:col_end] == 'all'
         observation_matrix_columns.count + 1
       else
         opts[:col_end]
       end

  Array.new(ce - opts[:col_start]){Array.new(re - opts[:row_start]){Array.new}}
end

#gene_descriptorsObject



56
57
58
# File 'app/models/observation_matrix.rb', line 56

def gene_descriptors
  descriptors.where(type: 'Descriptor::Gene').order('observation_matrix_columns.position')
end

#is_media_matrix?Boolean

Returns True if every descriptor is a media descriptor.

Returns:

  • (Boolean)

    True if every descriptor is a media descriptor



80
81
82
83
84
85
# File 'app/models/observation_matrix.rb', line 80

def is_media_matrix?
  observation_matrix_columns.each do |c|
    return false unless c.descriptor.type == 'Descriptor::Media'
  end
  true
end

#media_descriptorsObject



52
53
54
# File 'app/models/observation_matrix.rb', line 52

def media_descriptors
  descriptors.where(type: 'Descriptor::Media').order('observation_matrix_columns.position')
end

#media_observationsObject



132
133
134
# File 'app/models/observation_matrix.rb', line 132

def media_observations
  Observation::Media.in_observation_matrix(id)
end

#observation_depictionsObject

TODO: Railsify



137
138
139
140
141
142
143
144
# File 'app/models/observation_matrix.rb', line 137

def observation_depictions
  Depiction.select('depictions.*, observations.descriptor_id, observations.observation_object_id, observations.observation_object_type, sources.id AS source_id, sources.cached_author_string, sources.year, sources.cached AS source_cached')
    .joins('INNER JOIN observations ON observations.id = depictions.depiction_object_id')
    .joins('INNER JOIN images ON depictions.image_id = images.id')
    .joins("LEFT OUTER JOIN citations ON citations.citation_object_id = images.id AND citations.citation_object_type = 'Image' AND citations.is_original IS TRUE")
    .joins('LEFT OUTER JOIN sources ON citations.source_id = sources.id')
    .where(depiction_object: media_observations).order('depictions.position')
end

#observationsObject



128
129
130
# File 'app/models/observation_matrix.rb', line 128

def observations
  Observation.in_observation_matrix(id)
end

#observations_hashHash

Returns a hash of hashes of arrays with the coding objects nicely organized

descriptor_id1 =>{ "Otu1" => [observation1, observation2], descriptor_id: nil}

was ‘codings_mx` in mx where this: “likely should add scope and merge with above, though this seems to be slower”.

Returns:

  • (Hash)

    a hash of hashes of arrays with the coding objects nicely organized

    descriptor_id1 =>{ "Otu1" => [observation1, observation2], descriptor_id: nil}
    

    was ‘codings_mx` in mx where this: “likely should add scope and merge with above, though this seems to be slower”



263
264
265
266
267
268
# File 'app/models/observation_matrix.rb', line 263

def observations_hash
  h = Hash.new{|hash, key| hash[key] = Hash.new{|hash2, key2| hash2[key2] = Array.new}}
  observations.each {|o| h[o.descriptor_id][o.observation_object_type + o.observation_object_id.to_s].push(o) }
  #observations.each {|o| h[o.descriptor_id][o.observation_object_global_id].push(o) } ### potentially useful but extra compute slower
  h
end

#observations_in_grid(options = {}) ⇒ Hash

Note: old mx version had additional, at present not needed, they can be added via the row/column_index to get:

rows: [Otu1, Otu2... CollectonObject1]  (was a global ID in mx)
columns: [descriptor.id, desriptor.id]

!! :position attribute starts at 1 !! Grid starts at 0 !!

Returns:

  • (Hash)

    grid: [columns][observations]



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'app/models/observation_matrix.rb', line 155

def observations_in_grid(options = {})
  opts = {
    row_start:  1,
    row_end: 'all',
    col_start: 1,
    col_end: 'all',
    row_index: false,
    column_index: false,
  }.merge!(options.symbolize_keys)

  return false if (opts[:row_start] == 0) || (opts[:col_start] == 0) # catch problems with forgetting index starts at 1

  grid = empty_grid(opts)

  reindex_row_order if observation_matrix_rows.first.position != 1 || (observation_matrix_rows.last.position != observation_matrix_rows.size)
  reindex_column_order if observation_matrix_columns.first.position != 1 || (observation_matrix_columns.last.position != observation_matrix_columns.size)

  # Dump the observations into bins
  obs = Observation.by_matrix_and_position(self.id, opts)
    .select('omc.position as column_index, omr.position as row_index, observations.*')

  rows, cols = [], []

  obs.each do |o|
    grid[o.column_index - 1][o.row_index - 1].push(o)

    # These might not ever be needed, they were used in MX
    if opts[:row_index]
      rows[o.row_index - 1] = o.observation_object_type + o.observation_object_id.to_s if rows[o.row_index - 1].nil?
    end

    if opts[:column_index]
      cols[o.column_index - 1] = o.descriptor_id if cols[o.column_index - 1].nil?
    end
  end

  {grid:, rows:, cols:}
end

#polymorphic_cells_for_descriptor(symbol_start: 0, descriptor_id:) ⇒ Object

Used soley as a indexing method for nexml output Original code in mx

Parameters:

  • descriptor_id (Descriptor)
  • symbol_start (Integer) (defaults to: 0)

    # takes :chr => Chr, :symbol_start => Int

Returns:

  • Hash 1 => [character_state.id, charater_state.id]



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'app/models/observation_matrix.rb', line 235

def polymorphic_cells_for_descriptor(symbol_start: 0, descriptor_id:)
  symbol_start ||= 0
  cells = Hash.new{|hash, key| hash[key] = Array.new}
  observations.where(descriptor_id:).each do |o|
    g = "#{o.observation_object_type}|#{o.observation_object_id}"
    cells[g].push(
      o.qualitative? ? o.character_state_id : "#{o.descriptor_id}_#{o.presence_absence? ? '1' : '0'}"
    )
  end

  r = Hash.new{|hash, key| hash[key] = Array.new}
  i = 0
  cells.keys.each do |k|
    if r # must be some other idiom
      if cells[k].size > 1
        r[symbol_start + i] = cells[k].sort
        i += 1
      end
    end
  end
  r
end

#presence_absence_descriptorsObject



40
41
42
# File 'app/models/observation_matrix.rb', line 40

def presence_absence_descriptors
  descriptors.where(type: 'Descriptor::PresenceAbsence').order('observation_matrix_columns.position')
end

#qualitative_descriptorsObject



36
37
38
# File 'app/models/observation_matrix.rb', line 36

def qualitative_descriptors
  descriptors.where(type: 'Descriptor::Qualitative').order('observation_matrix_columns.position')
end

#reindex_column_orderObject



203
204
205
206
207
208
209
210
# File 'app/models/observation_matrix.rb', line 203

def reindex_column_order
  i = 1
  observation_matrix_columns.order(:position).find_each do |o|
    o.update_column(:position, i)
    i += 1
  end
  true
end

#reindex_row_orderObject



194
195
196
197
198
199
200
201
# File 'app/models/observation_matrix.rb', line 194

def reindex_row_order
  i = 1
  observation_matrix_rows.order(:position).find_each do |o|
    o.update_column(:position, i)
    i += 1
  end
  true
end

#reorder_columns(by = 'reindex') ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'app/models/observation_matrix.rb', line 112

def reorder_columns(by = 'reindex')
  case by
  when 'reindex'
    observation_matrix_columns.order('observation_matrix_columns.position').each.with_index do |c,i|
      c.update_column(:position, i)
    end
  when 'name'
    observation_matrix_columns.order('descriptors.name').each.with_index do |c,i|
      c.update_column(:position, i)
    end
  else
    return false
  end
  true
end

#reorder_rows(by = 'reindex') ⇒ Boolean

Returns reorders all rows and returns true or false.

Returns:

  • (Boolean)

    reorders all rows and returns true or false



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'app/models/observation_matrix.rb', line 89

def reorder_rows(by = 'reindex')
  case by
  when 'reindex'
    observation_matrix_rows.order('observation_matrix_rows.position').each.with_index do |r,i|
      r.update_column(:position, i)
    end
  when 'nomenclature'
    objects = []
    observation_matrix_rows.each do |r|
      t = r.current_taxon_name # not all rows have reference to a taxon name
      objects.push [r, (t ? TaxonName.self_and_ancestors_of(t).order('taxon_name_hierarchies.generations DESC').pluck(:name).to_s : '')]
    end

    objects.sort!{|a, b| a[1] <=> b[1]} # add internal loop on name
    objects.each_with_index do |r,i|
      r[0].update_column(:position, i)
    end
  else
    return false
  end
  true
end

#sample_descriptorsObject



48
49
50
# File 'app/models/observation_matrix.rb', line 48

def sample_descriptors
  descriptors.where(type: 'Descriptor::Sample').order('observation_matrix_columns.position')
end

#symbol_descriptorsObject

As handled in export/parsing by external tools !! Note order() is applied !!



66
67
68
# File 'app/models/observation_matrix.rb', line 66

def symbol_descriptors
  descriptors.where(type: ['Descriptor::PresenceAbsence', 'Descriptor::Qualitative']).order('observation_matrix_columns.position')
end

#working_descriptorsObject



60
61
62
# File 'app/models/observation_matrix.rb', line 60

def working_descriptors
  descriptors.where(type: 'Descriptor::Working').order('observation_matrix_columns.position')
end