Class: CollectionProfile

Inherits:
ApplicationRecord show all
Includes:
Housekeeping, Shared::Identifiers, Shared::IsData, Shared::Notes, Shared::Tags, 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

Constants included from SoftValidation

SoftValidation::ANCESTORS_WITH_SOFT_VALIDATIONS

Instance Attribute 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::Tags

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

Methods included from Shared::Notes

#concatenated_notes_string, #reject_notes

Methods included from Shared::Identifiers

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

Methods included from SoftValidation

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

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods inherited from ApplicationRecord

transaction_with_retry

Instance Attribute Details

#arrangement_levelInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#collection_typeString

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#computerization_levelInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#condition_of_labelsInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#conservation_statusInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#container_conditionInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#container_idInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#data_qualityInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#force_updateObject

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

#identification_levelInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#number_of_collection_objectsInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#number_of_containersInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#otu_idInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#processing_stateInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#project_idInteger

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
# File 'app/models/collection_profile.rb', line 64

class CollectionProfile < ApplicationRecord
  include Housekeeping
  include SoftValidation
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::IsData

  belongs_to :container, inverse_of: :collection_profiles
  belongs_to :otu, inverse_of: :collection_profiles

  # 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)}

  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].each_key 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

#average_profile_indexFloat?

Returns the average of all 8 indecies.

Returns:

  • (Float, nil)

    the average of all 8 indecies



117
118
119
120
# File 'app/models/collection_profile.rb', line 117

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

#collection_profile_indicesArray

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



103
104
105
106
107
108
109
110
111
112
113
# File 'app/models/collection_profile.rb', line 103

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

#prevent_editingObject (private)



157
158
159
160
161
# File 'app/models/collection_profile.rb', line 157

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

#validate_indicesObject (private)



144
145
146
147
148
149
150
151
152
153
154
155
# File 'app/models/collection_profile.rb', line 144

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

    COLLECTION_PROFILE_INDICES[:Favret][ct].each_key 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

#validate_numberObject (private)



132
133
134
135
136
137
138
139
140
141
142
# File 'app/models/collection_profile.rb', line 132

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

#validate_typeObject (private)



126
127
128
129
130
# File 'app/models/collection_profile.rb', line 126

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