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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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
# 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
  before_validation :set_type_from_descriptor
  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

  # TODO: Does this belong here?
  # Remove 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

  protected

  def set_type_from_descriptor
    if type.blank? && descriptor&.type
      write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
    end
  end

  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

Class Method Details

.by_matrix_and_position(observation_matrix_id, options = {}) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'app/models/observation.rb', line 179

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

.by_observation_matrix_row(observation_matrix_row_id) ⇒ Object



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

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

.copy(old_global_id, new_global_id) ⇒ Boolean

Returns:

  • (Boolean)


243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'app/models/observation.rb', line 243

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_row(observation_matrix_row_id) ⇒ Object

TODO: Does this belong here? Remove all observations for the set of descriptors in a given row



268
269
270
271
272
273
274
275
276
277
278
# File 'app/models/observation.rb', line 268

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

.human_nameObject



217
218
219
# File 'app/models/observation.rb', line 217

def self.human_name
  'YAY'
end

.in_observation_matrix(observation_matrix_id) ⇒ Object



173
174
175
176
177
# File 'app/models/observation.rb', line 173

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

.object_scope(object) ⇒ Object

TODO: deprecate or remove



212
213
214
215
# File 'app/models/observation.rb', line 212

def self.object_scope(object)
  return Observation.none if object.nil?
  Observation.where(observation_object: object)
end

Instance Method Details

#continuous?Boolean

Returns:

  • (Boolean)


169
170
171
# File 'app/models/observation.rb', line 169

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

#presence_absence?Boolean

Returns:

  • (Boolean)


165
166
167
# File 'app/models/observation.rb', line 165

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

#qualitative?Boolean

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

Returns:

  • (Boolean)


161
162
163
# File 'app/models/observation.rb', line 161

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

#set_type_from_descriptorObject (protected)



282
283
284
285
286
# File 'app/models/observation.rb', line 282

def set_type_from_descriptor
  if type.blank? && descriptor&.type
    write_attribute(:type, 'Observation::' + descriptor.type.split('::').last)
  end
end

#type_matches_descriptorObject (protected)



288
289
290
291
292
# File 'app/models/observation.rb', line 288

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