Class: Observation

Overview

Records Qualitative, Quantitative, Statistical, free-text (Working), Media and other types of “measurements” gathered from our observations. Where we record the data behind concepts like traits, phenotypes, measurements, character matrices, and descriptive matrices.

Subclasses of Observation define the applicable attributes for its type. Type is echoed 1:1 in each corresponding Descriptor, for convenience and validation purposes.

Subclass specific attributes

Observation::Qualitative attributes

Observation::Quantiative attributes

Observation::Working

Observation::??

Observation::PresenceAbsence

Observation::Sample !! Should only apply to OTUs technically, as this is an aggregation of measurements seen in a typical statistical summary

Direct Known Subclasses

Continuous, Media, PresenceAbsence, Qualitative, Sample, Working

Defined Under Namespace

Classes: Continuous, Media, PresenceAbsence, Qualitative, Sample, Working

Instance Attribute Summary collapse

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::ProtocolRelationships

#protocolled?, #reject_protocols

Methods included from Shared::Confidences

#reject_confidences

Methods included from Shared::Tags

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

Methods included from Shared::Notes

#concatenated_notes_string, #reject_notes

Methods included from Shared::Depictions

#has_depictions?, #image_array=, #reject_depictions, #reject_images

Methods included from Shared::Identifiers

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

Methods included from Shared::DataAttributes

#import_attributes, #internal_attributes, #keyword_value_hash, #reject_data_attributes

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

Instance Attribute Details

#cachedstring

Returns !! Not used. Perhaps records a human readable short description ultimately.

Returns:

  • (string)

    !! Not used. Perhaps records a human readable short description ultimately.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#cached_column_labelstring

Returns !! Not used. Candidate for removal.

Returns:

  • (string)

    !! Not used. Candidate for removal.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#cached_row_labelstring

Returns !! Not used. Candidate for removal.

Returns:

  • (string)

    !! Not used. Candidate for removal.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#character_state_idInteger

Returns The corresponding CharacterState id for “traditional” Qualitative “characters”.

Returns:

  • (Integer)

    The corresponding CharacterState id for “traditional” Qualitative “characters”



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#continuous_unitString

Returns A controlled vocabulary from Ruby::Units, like 'm“. The unit of the quantitative measurement.

Returns:

  • (String)

    A controlled vocabulary from Ruby::Units, like 'm“. The unit of the quantitative measurement



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#continuous_valueString

Returns The value of a quantitative measurement.

Returns:

  • (String)

    The value of a quantitative measurement



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#day_madeInteger

Returns 2 digit day the observation originated.

Returns:

  • (Integer)

    2 digit day the observation originated



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#descriptionsString

Returns Free text description of an Observation.

Returns:

  • (String)

    Free text description of an Observation



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#descriptor_idDescriptor#id

Returns The type of observation according to it's Descriptor. See also `type`.

Returns:

  • (Descriptor#id)

    The type of observation according to it's Descriptor. See also `type`.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#frequencyDescriptor#id

Returns ?! Candidate or removal ?!.

Returns:



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#month_madeInteger

Returns 2 digit month the observation originated.

Returns:

  • (Integer)

    2 digit month the observation originated



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#observation_object_global_idString

TODO: this is not memoized correctly ?!

Returns:

  • (String)


142
143
144
# File 'app/models/observation.rb', line 142

def observation_object_global_id
  @observation_object_global_id
end

#observation_object_idObject#id

Returns The id of the observed object.

Returns:

  • (Object#id)

    The id of the observed object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#observation_object_typeObject#class.name

Returns The type of the observed object.

Returns:

  • (Object#class.name)

    The type of the observed object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#presenceBoolean

Returns:

  • (Boolean)


123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#sample_maxBoolean

Returns statistical max.

Returns:

  • (Boolean)

    statistical max



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#sample_meanBoolean

Returns statistical mean.

Returns:

  • (Boolean)

    statistical mean



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#sample_medianBoolean

Returns statistical median.

Returns:

  • (Boolean)

    statistical median



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#sample_minBoolean

Returns statistical median.

Returns:

  • (Boolean)

    statistical median



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#sample_nBoolean

Returns statistical n.

Returns:

  • (Boolean)

    statistical n



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#sample_standard_deviationBoolean

Returns statistical standard deviation.

Returns:

  • (Boolean)

    statistical standard deviation



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#sample_standard_errorBoolean

Returns statistical standard error.

Returns:

  • (Boolean)

    statistical standard error



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#sample_standard_unitsBoolean

Returns A controlled vocabulary from Ruby::Units, like 'm“. The unit of the sample observations.

Returns:

  • (Boolean)

    A controlled vocabulary from Ruby::Units, like 'm“. The unit of the sample observations



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#time_madeInteger

Returns time without time zone.

Returns:

  • (Integer)

    time without time zone



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#typeString

Returns The type of observation. Defines the attribute set that is applicable to it.

Returns:

  • (String)

    The type of observation. Defines the attribute set that is applicable to it.



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validate :type_matches_descriptor

  validates :year_made, date_year: { min_year: 1757, max_year: -> {Time.now.year} }
  validates :month_made, date_month: true
  validates :day_made, date_day: {year_sym: :year_made, month_sym: :month_made}, unless: -> {year_made.nil? || month_made.nil?}

  # depends on timeliness 6.0, which is breaking something
  # validates_time :time_made, allow_nil: true

  def qualitative?
    type == 'Observation::Qualitative'
  end

  def presence_absence?
    type == 'Observation::PresenceAbsence'
  end

  def continuous?
    type == 'Observation::Continuous'
  end

  def self.in_observation_matrix(observation_matrix_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)
  end

  def self.by_matrix_and_position(observation_matrix_id, options = {})
    opts = {
      row_start:  1,
      row_end: 'all',
      col_start: 1,
      col_end: 'all'
    }.merge!(options.symbolize_keys)

    return in_observation_matrix(observation_matrix_id).order('omc.position, omr.position') if opts[:row_start] == 1 && opts[:row_end] == 'all' && opts[:col_start] == 1 && opts[:col_end] == 'all'

    base = Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.observation_matrix_id = ? AND omc.observation_matrix_id = ?', observation_matrix_id, observation_matrix_id)

    # row scope
    base = base.where('omr.position >= ?', opts[:row_start])
    base = base.where('omr.position <= ?', opts[:row_end]) if !(opts[:row_end] == 'all')

    # col scope
    base = base.where('omc.position >= ?', opts[:col_start])
    base = base.where('omc.position <= ?', opts[:col_end]) if !(opts[:col_end] == 'all')

    base
  end

  def self.by_observation_matrix_row(observation_matrix_row_id)
    Observation.joins('JOIN observation_matrix_rows omr on (omr.observation_object_type = observations.observation_object_type AND omr.observation_object_id = observations.observation_object_id)')
      .joins('JOIN observation_matrix_columns omc on omc.descriptor_id = observations.descriptor_id')
      .where('omr.id = ?', observation_matrix_row_id)
      .order('omc.position')
  end

  # TODO: deprecate or remove
  def self.object_scope(object)
    return Observation.none if object.nil?
    Observation.where(observation_object: object)
  end

  def self.human_name
    'YAY'
  end

  def observation_object_global_id=(value)
    self.observation_object = GlobalID::Locator.locate(value)
    @observation_object_global_id = value
  end

  # @return [String]
  # TODO: this is not memoized correctly ?!
  def observation_object_global_id
    if observation_object
      observation_object.to_global_id.to_s
    else
      @observation_object_global_id
    end
  end

  # @return [Boolean]
  # @params old_global_id [String]
  #    global_id of collection object or Otu
  #
  # @params new_global_id [String]
  #    global_id of collection object or Otu
  #
  def self.copy(old_global_id, new_global_id)
    begin
      old = GlobalID::Locator.locate(old_global_id)

      Observation.transaction do
        old.observations.each do |o|
          d = o.dup
          d.update(observation_object_global_id: new_global_id)

          # Copy depictions
          o.depictions.each do |i|
            j = i.dup
            j.update(depiction_object: d)
          end
        end
      end
      true
    rescue
      return false
    end
    true
  end

  # Destroy all observations for the set of descriptors in a given row
  def self.destroy_row(observation_matrix_row_id)
    r = ObservationMatrixRow.find(observation_matrix_row_id)
    begin
      Observation.transaction do
        r.observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # Destroy observations for the set of descriptors in a given column
  def self.destroy_column(observation_matrix_column_id)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    observations = self.in_observation_matrix(c.observation_matrix_id).where(descriptor_id: c.descriptor_id)
    begin
      Observation.transaction do
        observations.destroy_all
      end
    rescue
      raise
    end
    true
  end

  # @return [Hash]
  #  { created: 1, failed: 2 }
  def self.code_column(observation_matrix_column_id, observation_params)
    c = ObservationMatrixColumn.find(observation_matrix_column_id)
    o = ObservationMatrix.find(c.observation_matrix_id)

    descriptor = c.descriptor

    # Type is required on the onset
    p = observation_params.merge(
      type: descriptor.observation_type
    )

    h = Hash.new(0)

    Observation.transaction do
      o.observation_matrix_rows.each do |r|
        begin
          if !Observation.where(observation_object: r.observation_object, descriptor: descriptor).any?
            Observation.create!(
              p.merge(
                observation_object: r.observation_object,
                descriptor: descriptor,
              )
            )
            h[:passed] += 1
          else
            h[:exists] += 1
          end
        rescue ActiveRecord::RecordInvalid
          h[:failed] += 1
        end
      end
    end
    h
  end

  protected

  def type_matches_descriptor
    a = type&.split('::')&.last
    b = descriptor&.type&.split('::')&.last
    errors.add(:type, 'type of Observation does not match type of Descriptor') if a && b && a != b
  end

end

#year_madeInteger

Returns 4 digit year the observation originated (not when it was recorded in TaxonWorks, though these might be the close to the same).

Returns:

  • (Integer)

    4 digit year the observation originated (not when it was recorded in TaxonWorks, though these might be the close to the same)



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'app/models/observation.rb', line 123

class Observation < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Depictions
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Confidences
  include Shared::ProtocolRelationships
  include Shared::IsData
  include Shared::ObservationIndex

  ignore_whitespace_on(:description)

  self.skip_time_zone_conversion_for_attributes = [:time_made]

  # String, not GlobalId
  attr_accessor :observation_object_global_id

  belongs_to :character_state, inverse_of: :observations
  belongs_to :descriptor, inverse_of: :observations
  belongs_to :observation_object, polymorphic: true

  # before_validation :convert_observation_object_global_id

  validates_presence_of :descriptor_id, :type
  validates_presence_of :observation_object
  validat