Class: CollectionProfile

Inherits:
ApplicationRecord show all
Includes:
Housekeeping, Shared::Identifiable, Shared::IsData, Shared::Notable, Shared::Taggable, SoftValidation
Defined in:
app/models/collection_profile.rb

Overview

A collection profile, extensible, but currently sensu INHS/Colin Favret. See Favret, C., et al. “Profiling natural history collections: A method for quantitative and comparative health assessment.” Collection Forum. Vol. 22. No. 1-2. 2007.

Data is validated against a .yml definition in config/initializers/constants/data/collection_profile_indices.yml That file is loaded to COLLECTION_PROFILE_INDICES[:dry][1]

Constant Summary

Constant Summary

Constants included from SoftValidation

SoftValidation::ANCESTORS_WITH_SOFT_VALIDATIONS

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Methods included from SoftValidation

#clear_soft_validations, #fix_soft_validations, #soft_fixed?, #soft_valid?, #soft_validate, #soft_validated?, #soft_validations

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods included from ActiverecordUtilities

#trim_attributes

Instance Attribute Details

- (Integer) arrangement_level

Returns See Favret et al. (2007) (cited above)

Returns:

  • (Integer)

    See Favret et al. (2007) (cited above)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (String) collection_type

Returns one of 'wet', 'dry', 'slide' sensu Favret. Model is extensible via editing .yml and new profile types.

Returns:

  • (String)

    one of 'wet', 'dry', 'slide' sensu Favret. Model is extensible via editing .yml and new profile types.



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) computerization_level

Returns See Favret et al. (2007) (cited above)

Returns:

  • (Integer)

    See Favret et al. (2007) (cited above)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) condition_of_labels

Returns See Favret et al. (2007) (cited above)

Returns:

  • (Integer)

    See Favret et al. (2007) (cited above)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) conservation_status

Returns See Favret et al. (2007) (cited above)

Returns:

  • (Integer)

    See Favret et al. (2007) (cited above)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) container_condition

Returns See Favret et al. (2007) (cited above)

Returns:

  • (Integer)

    See Favret et al. (2007) (cited above)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) container_id

Returns the (physical) Container beign profiled

Returns:

  • (Integer)

    the (physical) Container beign profiled



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) data_quality

Returns See Favret et al. (2007) (cited above)

Returns:

  • (Integer)

    See Favret et al. (2007) (cited above)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Object) force_update

Once created the intent is that CollectionProfiles are #dup(ed), not edited. If there was a data error pass true here



77
78
79
# File 'app/models/collection_profile.rb', line 77

def force_update
  @force_update
end

- (Integer) identification_level

Returns See Favret et al. (2007) (cited above)

Returns:

  • (Integer)

    See Favret et al. (2007) (cited above)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) number_of_collection_objects

Returns a count of the number of collection objects in this container (asserted, not as calculated in TW)

Returns:

  • (Integer)

    a count of the number of collection objects in this container (asserted, not as calculated in TW)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) number_of_containers

Returns a count of the number of containers inside this container (asserted, not as calculated in TW)

Returns:

  • (Integer)

    a count of the number of containers inside this container (asserted, not as calculated in TW)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) otu_id

Returns the encompassing taxon found in this container

Returns:

  • (Integer)

    the encompassing taxon found in this container



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) processing_state

Returns See Favret et al. (2007) (cited above)

Returns:

  • (Integer)

    See Favret et al. (2007) (cited above)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

- (Integer) project_id

Returns the Project ID

Returns:

  • (Integer)

    the Project ID



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include Shared::IsData
  include SoftValidation
  include Shared::Identifiable
  include Shared::Notable
  include Shared::Taggable

  belongs_to :container
  belongs_to :otu

  # Once created the intent is that CollectionProfiles are #dup(ed), not
  # edited.  If there was a data error pass true here
  attr_accessor :force_update

  scope :with_collection_type_string, -> (type_string) {where(collection_type: type_string)}
  scope :with_container_id, -> (container) {where(container_id: container)}
  scope :with_otu_id, -> (otu) {where(otu_id: otu)}

  # Use shared scopes lib/housekeeping/timestamps for this
  # deprecated for timestamps#created_before_date
  # scope :created_before_date, -> (date) { where('"collection_profiles"."id" in (SELECT DISTINCT ON (id) id FROM collection_profiles WHERE created_at < ? ORDER BY id, created_at DESC)', "#{date}")}

  validates :conservation_status,
            :processing_state,
            :container_condition,
            :condition_of_labels,
            :identification_level,
            :arrangement_level,
            :data_quality,
            :computerization_level,
            :collection_type, presence: true

  validate :validate_type,
           :validate_number,
           :validate_indices

  validate :prevent_editing, unless: -> {self.force_update}

  # region Profile indices

  # @return [Array]
  #   set of all provided indecies, if any is not provided return an empty array
  def collection_profile_indices
    i = [self.conservation_status,
         self.processing_state,
         self.container_condition,
         self.condition_of_labels,
         self.identification_level,
         self.arrangement_level,
         self.data_quality,
         self.computerization_level].compact
    i.size == 8 ? i : []
  end

  # @return [Float, nil]
  #    the average of all 8 indecies
  def average_profile_index
    i = self.collection_profile_indices
    i.empty? ? nil : i.sum / i.size.to_f
  end

  #endregion

  private

  def validate_type
    unless self.collection_type.blank?
      errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
    end
  end

  def validate_number
    t = self.collection_type.to_s
    if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
      errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
      errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
    elsif t == 'dry' && self.number_of_collection_objects.nil?
      errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
    elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
      errors.add(:number_of_containers, 'Number of slides or vials should be specified')
    end
  end

  def validate_indices
    unless self.collection_type.blank?
      ct = self.collection_type.to_sym

      COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
        v = self.send(a)
        unless v.nil?
          errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
        end
      end
    end
  end

  def prevent_editing
    unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
      errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
    end
  end

end

Instance Method Details

- (Float?) average_profile_index

Returns the average of all 8 indecies

Returns:

  • (Float, nil)

    the average of all 8 indecies



121
122
123
124
# File 'app/models/collection_profile.rb', line 121

def average_profile_index
  i = self.collection_profile_indices
  i.empty? ? nil : i.sum / i.size.to_f
end

- (Array) collection_profile_indices

Returns set of all provided indecies, if any is not provided return an empty array

Returns:

  • (Array)

    set of all provided indecies, if any is not provided return an empty array



107
108
109
110
111
112
113
114
115
116
117
# File 'app/models/collection_profile.rb', line 107

def collection_profile_indices
  i = [self.conservation_status,
       self.processing_state,
       self.container_condition,
       self.condition_of_labels,
       self.identification_level,
       self.arrangement_level,
       self.data_quality,
       self.computerization_level].compact
  i.size == 8 ? i : []
end

- (Object) prevent_editing (private)



161
162
163
164
165
# File 'app/models/collection_profile.rb', line 161

def prevent_editing
  unless self.new_record? || (self.created_at == self.updated_at) # a little strange, handles Factories?!
    errors.add(:base, 'Collection profile should not be updated. Updated version should be saved as a new record')
  end
end

- (Object) validate_indices (private)



148
149
150
151
152
153
154
155
156
157
158
159
# File 'app/models/collection_profile.rb', line 148

def validate_indices
  unless self.collection_type.blank?
    ct = self.collection_type.to_sym

    COLLECTION_PROFILE_INDICES[:Favret][ct].keys.each do |a|
      v = self.send(a)
      unless v.nil?
        errors.add(a.to_sym, 'Invalid entry') if COLLECTION_PROFILE_INDICES[:Favret][ct][a.to_sym][v].nil?
      end
    end
  end
end

- (Object) validate_number (private)



136
137
138
139
140
141
142
143
144
145
146
# File 'app/models/collection_profile.rb', line 136

def validate_number
  t = self.collection_type.to_s
  if self.number_of_collection_objects.nil? && self.number_of_containers.nil?
    errors.add(:number_of_collection_objects, 'At least one number of specimens or number of containers should be specified')
    errors.add(:number_of_containers, 'At least one number of specimens or number of containers should be specified')
  elsif t == 'dry' && self.number_of_collection_objects.nil?
    errors.add(:number_of_collection_objects, 'Number of specimens should be specified')
  elsif (t == 'wet' || t == 'slide') && self.number_of_containers.nil?
    errors.add(:number_of_containers, 'Number of slides or vials should be specified')
  end
end

- (Object) validate_type (private)



130
131
132
133
134
# File 'app/models/collection_profile.rb', line 130

def validate_type
  unless self.collection_type.blank?
    errors.add(:collection_type, 'Invalid profile type') if COLLECTION_PROFILE_INDICES[:Favret][self.collection_type.to_sym].nil?
  end
end