Class: Extract

Overview

An Extract is the quantified physical entity that originated from a CollectionObject. Extracts are linked to their origin through an OriginRelationship.

Constant Summary

Constants included from SoftValidation

SoftValidation::ANCESTORS_WITH_SOFT_VALIDATIONS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Shared::AutoUuid

#create_object_uuid, #generate_uuid_if_required

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 SoftValidation

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

Methods included from Shared::Tags

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

Methods included from Shared::DataAttributes

#import_attributes, #internal_attributes, #keyword_value_hash, #reject_data_attributes

Methods included from Shared::Citations

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

Methods included from Shared::Confidences

#reject_confidences

Methods included from Shared::Containable

#contain, #containable?, #contained?, #contained_by?, #contained_siblings, #enclosing_containers, #put_in_container

Methods included from Shared::OriginRelationship

#new_objects, #old_objects, #reject_origin_relationships, #set_origin

Methods included from Shared::ProtocolRelationships

#protocolled?, #reject_protocols

Methods included from Shared::Identifiers

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

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods inherited from ApplicationRecord

transaction_with_retry

Instance Attribute Details

#day_madeInteger

Returns 2 digit day the extract originated.

Returns:

  • (Integer)

    2 digit day the extract originated



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
168
169
170
171
172
173
174
175
176
177
178
179
# File 'app/models/extract.rb', line 20

class Extract < ApplicationRecord
  include Housekeeping
  include Shared::Identifiers
  include Shared::ProtocolRelationships
  include Shared::OriginRelationship
  include Shared::Containable
  include Shared::Confidences
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Observations
  include Shared::Tags
  include SoftValidation
  include Shared::IsData
  include Shared::AutoUuid

  # TODO: make loanable
  # TODO: auto-UUID

  is_origin_for 'Extract', 'Sequence'
  originates_from 'Extract', 'Specimen', 'Lot', 'RangedLot', 'Otu', 'CollectionObject', 'FieldOccurrence'

  belongs_to :repository, inverse_of: :extracts

  has_many :extractor_roles, -> { order('roles.position ASC') }, class_name: 'Extractor', as: :role_object, dependent: :destroy, validate: true, inverse_of: :role_object
  has_many :extractors, -> { order('roles.position ASC') }, through: :extractor_roles, source: :person, validate: true

  # Upstream - aliases of `origin_otus` and `origin_collection_objects` TODO remove
  has_many :otus, through: :related_origin_relationships, source: :old_object, source_type: 'Otu'
  has_many :collection_objects, through: :related_origin_relationships, source: :old_object, source_type: 'CollectionObject'

  # Downstresm - aliases of `derived_*`, TODO: remove
  has_many :sequences, through: :origin_relationships, source: :new_object, source_type: 'Sequence'
  has_many :extracts, through: :related_origin_relationships, source: :old_object, source_type: 'Extract'

  attr_accessor :is_made_now

  # TODO: Unify in concern, with CO too
  # Identifier delegations
  # .catalog_number_cached
  delegate :cached, to: :preferred_catalog_number, prefix: :catalog_number, allow_nil: true
  # .catalog_number_namespace
  delegate :namespace, to: :preferred_catalog_number, prefix: :catalog_number, allow_nil: true

  before_validation :set_made, if: -> {is_made_now}

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

  # @return Array
  #   all inferred or asserted OTUs that this OTU came from
  def referenced_otus
    [
      [otus],
      [collection_objects.collect{|o| o.current_otu} ]
    ].flatten.compact.uniq
  end

  # TODO: Unify with CollectionObject in concern
  # @return [Identifier::Local::CatalogNumber, nil]
  #   the first (position) catalog number for this collection object, either on specimen, or container
  def preferred_catalog_number
    if i = Identifier::Local::CatalogNumber.where(identifier_object: self).order(:position).first
      i
    else
      if container
        container.identifiers.where(identifiers: {type: 'Identifier::Local::CatalogNumber'}).order(:position).first
      else
        nil
      end
    end
  end

  # In anticipation of DwC handling
  def dwc_catalog_number
    catalog_number_cached
  end

  protected

  def set_made
    write_attribute(:year_made, Time.now.year)
    write_attribute(:month_made, Time.now.month)
    write_attribute(:day_made, Time.now.day)
  end

  # @param used_on [String] required, one of `Protocol`, `OriginRelationship`
  # @return [Scope]
  #    the max 10 most recently used collection_objects, as `used_on`
  def self.used_recently(user_id, project_id, used_on = '')
    return [] if used_on != 'TaxonDetermination' && used_on != 'BiologicalAssociation'
    t = case used_on
        when 'TaxonDetermination'
          TaxonDetermination.arel_table
        when 'BiologicalAssociation'
          BiologicalAssociation.arel_table
        end

    p = CollectionObject.arel_table

    # i is a select manager
    i = case used_on
        when 'BiologicalAssociation'
          t.project(t['biological_association_subject_id'], t['updated_at']).from(t)
            .where(
              t['updated_at'].gt(1.week.ago).and(
                t['biological_association_subject_type'].eq('CollectionObject')
              )
            )
              .where(t['updated_by_id'].eq(user_id))
              .where(t['project_id'].eq(project_id))
            .order(t['updated_at'].desc)
        else
          t.project(t['taxon_determination_object_id'], t['taxon_determination_object_type'], t['updated_at']).from(t)
            .where(t['updated_at'].gt( 1.week.ago ))
            .where(t['updated_by_id'].eq(user_id))
            .where(t['project_id'].eq(project_id))
            .order(t['updated_at'].desc)
        end

    # z is a table alias
    z = i.as('recent_t')

    j = case used_on
        when 'BiologicalAssociation'
          Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(
            z['biological_association_subject_id'].eq(p['id'])
          ))
        else
          # TODO fix biological_collection_object_id transition scoping
          Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(z['taxon_determination_object_id'].eq(p['id'])))
        end

    CollectionObject.joins(j).pluck(:id).uniq
  end

  # @params target [String] one of `TaxonDetermination`, `BiologicalAssociation` , nil
  # @return [Hash] otus optimized for user selection
  def self.select_optimized(user_id, project_id, target = nil)
    r = used_recently(user_id, project_id, target)
    h = {
      quick: [],
      pinboard: Extract.pinned_by(user_id).where(project_id:).to_a,
      recent: []
    }

    if target && !r.empty?
      n = target.tableize.to_sym
      h[:recent] = Extract.where('"extracts"."id" IN (?)', r.first(10) ).to_a
      h[:quick] = (Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a  +
          Extract.where('"extracts"."id" IN (?)', r.first(4) ).to_a).uniq
    else
      h[:recent] = Extract.where(project_id:, updated_by_id: user_id).order('updated_at DESC').limit(10).to_a
      h[:quick] = Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a
    end

    h
  end

end

#is_made_nowObject

Returns the value of attribute is_made_now.



54
55
56
# File 'app/models/extract.rb', line 54

def is_made_now
  @is_made_now
end

#month_madeInteger

Returns 2 digit month the extract originated.

Returns:

  • (Integer)

    2 digit month the extract originated



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
168
169
170
171
172
173
174
175
176
177
178
179
# File 'app/models/extract.rb', line 20

class Extract < ApplicationRecord
  include Housekeeping
  include Shared::Identifiers
  include Shared::ProtocolRelationships
  include Shared::OriginRelationship
  include Shared::Containable
  include Shared::Confidences
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Observations
  include Shared::Tags
  include SoftValidation
  include Shared::IsData
  include Shared::AutoUuid

  # TODO: make loanable
  # TODO: auto-UUID

  is_origin_for 'Extract', 'Sequence'
  originates_from 'Extract', 'Specimen', 'Lot', 'RangedLot', 'Otu', 'CollectionObject', 'FieldOccurrence'

  belongs_to :repository, inverse_of: :extracts

  has_many :extractor_roles, -> { order('roles.position ASC') }, class_name: 'Extractor', as: :role_object, dependent: :destroy, validate: true, inverse_of: :role_object
  has_many :extractors, -> { order('roles.position ASC') }, through: :extractor_roles, source: :person, validate: true

  # Upstream - aliases of `origin_otus` and `origin_collection_objects` TODO remove
  has_many :otus, through: :related_origin_relationships, source: :old_object, source_type: 'Otu'
  has_many :collection_objects, through: :related_origin_relationships, source: :old_object, source_type: 'CollectionObject'

  # Downstresm - aliases of `derived_*`, TODO: remove
  has_many :sequences, through: :origin_relationships, source: :new_object, source_type: 'Sequence'
  has_many :extracts, through: :related_origin_relationships, source: :old_object, source_type: 'Extract'

  attr_accessor :is_made_now

  # TODO: Unify in concern, with CO too
  # Identifier delegations
  # .catalog_number_cached
  delegate :cached, to: :preferred_catalog_number, prefix: :catalog_number, allow_nil: true
  # .catalog_number_namespace
  delegate :namespace, to: :preferred_catalog_number, prefix: :catalog_number, allow_nil: true

  before_validation :set_made, if: -> {is_made_now}

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

  # @return Array
  #   all inferred or asserted OTUs that this OTU came from
  def referenced_otus
    [
      [otus],
      [collection_objects.collect{|o| o.current_otu} ]
    ].flatten.compact.uniq
  end

  # TODO: Unify with CollectionObject in concern
  # @return [Identifier::Local::CatalogNumber, nil]
  #   the first (position) catalog number for this collection object, either on specimen, or container
  def preferred_catalog_number
    if i = Identifier::Local::CatalogNumber.where(identifier_object: self).order(:position).first
      i
    else
      if container
        container.identifiers.where(identifiers: {type: 'Identifier::Local::CatalogNumber'}).order(:position).first
      else
        nil
      end
    end
  end

  # In anticipation of DwC handling
  def dwc_catalog_number
    catalog_number_cached
  end

  protected

  def set_made
    write_attribute(:year_made, Time.now.year)
    write_attribute(:month_made, Time.now.month)
    write_attribute(:day_made, Time.now.day)
  end

  # @param used_on [String] required, one of `Protocol`, `OriginRelationship`
  # @return [Scope]
  #    the max 10 most recently used collection_objects, as `used_on`
  def self.used_recently(user_id, project_id, used_on = '')
    return [] if used_on != 'TaxonDetermination' && used_on != 'BiologicalAssociation'
    t = case used_on
        when 'TaxonDetermination'
          TaxonDetermination.arel_table
        when 'BiologicalAssociation'
          BiologicalAssociation.arel_table
        end

    p = CollectionObject.arel_table

    # i is a select manager
    i = case used_on
        when 'BiologicalAssociation'
          t.project(t['biological_association_subject_id'], t['updated_at']).from(t)
            .where(
              t['updated_at'].gt(1.week.ago).and(
                t['biological_association_subject_type'].eq('CollectionObject')
              )
            )
              .where(t['updated_by_id'].eq(user_id))
              .where(t['project_id'].eq(project_id))
            .order(t['updated_at'].desc)
        else
          t.project(t['taxon_determination_object_id'], t['taxon_determination_object_type'], t['updated_at']).from(t)
            .where(t['updated_at'].gt( 1.week.ago ))
            .where(t['updated_by_id'].eq(user_id))
            .where(t['project_id'].eq(project_id))
            .order(t['updated_at'].desc)
        end

    # z is a table alias
    z = i.as('recent_t')

    j = case used_on
        when 'BiologicalAssociation'
          Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(
            z['biological_association_subject_id'].eq(p['id'])
          ))
        else
          # TODO fix biological_collection_object_id transition scoping
          Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(z['taxon_determination_object_id'].eq(p['id'])))
        end

    CollectionObject.joins(j).pluck(:id).uniq
  end

  # @params target [String] one of `TaxonDetermination`, `BiologicalAssociation` , nil
  # @return [Hash] otus optimized for user selection
  def self.select_optimized(user_id, project_id, target = nil)
    r = used_recently(user_id, project_id, target)
    h = {
      quick: [],
      pinboard: Extract.pinned_by(user_id).where(project_id:).to_a,
      recent: []
    }

    if target && !r.empty?
      n = target.tableize.to_sym
      h[:recent] = Extract.where('"extracts"."id" IN (?)', r.first(10) ).to_a
      h[:quick] = (Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a  +
          Extract.where('"extracts"."id" IN (?)', r.first(4) ).to_a).uniq
    else
      h[:recent] = Extract.where(project_id:, updated_by_id: user_id).order('updated_at DESC').limit(10).to_a
      h[:quick] = Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a
    end

    h
  end

end

#verbatim_anatomical_originString

Returns proxy for a OriginRelationship to an AnatomicalClass.

Returns:

  • (String)

    proxy for a OriginRelationship to an AnatomicalClass



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
168
169
170
171
172
173
174
175
176
177
178
179
# File 'app/models/extract.rb', line 20

class Extract < ApplicationRecord
  include Housekeeping
  include Shared::Identifiers
  include Shared::ProtocolRelationships
  include Shared::OriginRelationship
  include Shared::Containable
  include Shared::Confidences
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Observations
  include Shared::Tags
  include SoftValidation
  include Shared::IsData
  include Shared::AutoUuid

  # TODO: make loanable
  # TODO: auto-UUID

  is_origin_for 'Extract', 'Sequence'
  originates_from 'Extract', 'Specimen', 'Lot', 'RangedLot', 'Otu', 'CollectionObject', 'FieldOccurrence'

  belongs_to :repository, inverse_of: :extracts

  has_many :extractor_roles, -> { order('roles.position ASC') }, class_name: 'Extractor', as: :role_object, dependent: :destroy, validate: true, inverse_of: :role_object
  has_many :extractors, -> { order('roles.position ASC') }, through: :extractor_roles, source: :person, validate: true

  # Upstream - aliases of `origin_otus` and `origin_collection_objects` TODO remove
  has_many :otus, through: :related_origin_relationships, source: :old_object, source_type: 'Otu'
  has_many :collection_objects, through: :related_origin_relationships, source: :old_object, source_type: 'CollectionObject'

  # Downstresm - aliases of `derived_*`, TODO: remove
  has_many :sequences, through: :origin_relationships, source: :new_object, source_type: 'Sequence'
  has_many :extracts, through: :related_origin_relationships, source: :old_object, source_type: 'Extract'

  attr_accessor :is_made_now

  # TODO: Unify in concern, with CO too
  # Identifier delegations
  # .catalog_number_cached
  delegate :cached, to: :preferred_catalog_number, prefix: :catalog_number, allow_nil: true
  # .catalog_number_namespace
  delegate :namespace, to: :preferred_catalog_number, prefix: :catalog_number, allow_nil: true

  before_validation :set_made, if: -> {is_made_now}

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

  # @return Array
  #   all inferred or asserted OTUs that this OTU came from
  def referenced_otus
    [
      [otus],
      [collection_objects.collect{|o| o.current_otu} ]
    ].flatten.compact.uniq
  end

  # TODO: Unify with CollectionObject in concern
  # @return [Identifier::Local::CatalogNumber, nil]
  #   the first (position) catalog number for this collection object, either on specimen, or container
  def preferred_catalog_number
    if i = Identifier::Local::CatalogNumber.where(identifier_object: self).order(:position).first
      i
    else
      if container
        container.identifiers.where(identifiers: {type: 'Identifier::Local::CatalogNumber'}).order(:position).first
      else
        nil
      end
    end
  end

  # In anticipation of DwC handling
  def dwc_catalog_number
    catalog_number_cached
  end

  protected

  def set_made
    write_attribute(:year_made, Time.now.year)
    write_attribute(:month_made, Time.now.month)
    write_attribute(:day_made, Time.now.day)
  end

  # @param used_on [String] required, one of `Protocol`, `OriginRelationship`
  # @return [Scope]
  #    the max 10 most recently used collection_objects, as `used_on`
  def self.used_recently(user_id, project_id, used_on = '')
    return [] if used_on != 'TaxonDetermination' && used_on != 'BiologicalAssociation'
    t = case used_on
        when 'TaxonDetermination'
          TaxonDetermination.arel_table
        when 'BiologicalAssociation'
          BiologicalAssociation.arel_table
        end

    p = CollectionObject.arel_table

    # i is a select manager
    i = case used_on
        when 'BiologicalAssociation'
          t.project(t['biological_association_subject_id'], t['updated_at']).from(t)
            .where(
              t['updated_at'].gt(1.week.ago).and(
                t['biological_association_subject_type'].eq('CollectionObject')
              )
            )
              .where(t['updated_by_id'].eq(user_id))
              .where(t['project_id'].eq(project_id))
            .order(t['updated_at'].desc)
        else
          t.project(t['taxon_determination_object_id'], t['taxon_determination_object_type'], t['updated_at']).from(t)
            .where(t['updated_at'].gt( 1.week.ago ))
            .where(t['updated_by_id'].eq(user_id))
            .where(t['project_id'].eq(project_id))
            .order(t['updated_at'].desc)
        end

    # z is a table alias
    z = i.as('recent_t')

    j = case used_on
        when 'BiologicalAssociation'
          Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(
            z['biological_association_subject_id'].eq(p['id'])
          ))
        else
          # TODO fix biological_collection_object_id transition scoping
          Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(z['taxon_determination_object_id'].eq(p['id'])))
        end

    CollectionObject.joins(j).pluck(:id).uniq
  end

  # @params target [String] one of `TaxonDetermination`, `BiologicalAssociation` , nil
  # @return [Hash] otus optimized for user selection
  def self.select_optimized(user_id, project_id, target = nil)
    r = used_recently(user_id, project_id, target)
    h = {
      quick: [],
      pinboard: Extract.pinned_by(user_id).where(project_id:).to_a,
      recent: []
    }

    if target && !r.empty?
      n = target.tableize.to_sym
      h[:recent] = Extract.where('"extracts"."id" IN (?)', r.first(10) ).to_a
      h[:quick] = (Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a  +
          Extract.where('"extracts"."id" IN (?)', r.first(4) ).to_a).uniq
    else
      h[:recent] = Extract.where(project_id:, updated_by_id: user_id).order('updated_at DESC').limit(10).to_a
      h[:quick] = Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a
    end

    h
  end

end

#year_madeInteger

Returns 4 digit year the extract originated.

Returns:

  • (Integer)

    4 digit year the extract originated



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
168
169
170
171
172
173
174
175
176
177
178
179
# File 'app/models/extract.rb', line 20

class Extract < ApplicationRecord
  include Housekeeping
  include Shared::Identifiers
  include Shared::ProtocolRelationships
  include Shared::OriginRelationship
  include Shared::Containable
  include Shared::Confidences
  include Shared::Citations
  include Shared::DataAttributes
  include Shared::Observations
  include Shared::Tags
  include SoftValidation
  include Shared::IsData
  include Shared::AutoUuid

  # TODO: make loanable
  # TODO: auto-UUID

  is_origin_for 'Extract', 'Sequence'
  originates_from 'Extract', 'Specimen', 'Lot', 'RangedLot', 'Otu', 'CollectionObject', 'FieldOccurrence'

  belongs_to :repository, inverse_of: :extracts

  has_many :extractor_roles, -> { order('roles.position ASC') }, class_name: 'Extractor', as: :role_object, dependent: :destroy, validate: true, inverse_of: :role_object
  has_many :extractors, -> { order('roles.position ASC') }, through: :extractor_roles, source: :person, validate: true

  # Upstream - aliases of `origin_otus` and `origin_collection_objects` TODO remove
  has_many :otus, through: :related_origin_relationships, source: :old_object, source_type: 'Otu'
  has_many :collection_objects, through: :related_origin_relationships, source: :old_object, source_type: 'CollectionObject'

  # Downstresm - aliases of `derived_*`, TODO: remove
  has_many :sequences, through: :origin_relationships, source: :new_object, source_type: 'Sequence'
  has_many :extracts, through: :related_origin_relationships, source: :old_object, source_type: 'Extract'

  attr_accessor :is_made_now

  # TODO: Unify in concern, with CO too
  # Identifier delegations
  # .catalog_number_cached
  delegate :cached, to: :preferred_catalog_number, prefix: :catalog_number, allow_nil: true
  # .catalog_number_namespace
  delegate :namespace, to: :preferred_catalog_number, prefix: :catalog_number, allow_nil: true

  before_validation :set_made, if: -> {is_made_now}

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

  # @return Array
  #   all inferred or asserted OTUs that this OTU came from
  def referenced_otus
    [
      [otus],
      [collection_objects.collect{|o| o.current_otu} ]
    ].flatten.compact.uniq
  end

  # TODO: Unify with CollectionObject in concern
  # @return [Identifier::Local::CatalogNumber, nil]
  #   the first (position) catalog number for this collection object, either on specimen, or container
  def preferred_catalog_number
    if i = Identifier::Local::CatalogNumber.where(identifier_object: self).order(:position).first
      i
    else
      if container
        container.identifiers.where(identifiers: {type: 'Identifier::Local::CatalogNumber'}).order(:position).first
      else
        nil
      end
    end
  end

  # In anticipation of DwC handling
  def dwc_catalog_number
    catalog_number_cached
  end

  protected

  def set_made
    write_attribute(:year_made, Time.now.year)
    write_attribute(:month_made, Time.now.month)
    write_attribute(:day_made, Time.now.day)
  end

  # @param used_on [String] required, one of `Protocol`, `OriginRelationship`
  # @return [Scope]
  #    the max 10 most recently used collection_objects, as `used_on`
  def self.used_recently(user_id, project_id, used_on = '')
    return [] if used_on != 'TaxonDetermination' && used_on != 'BiologicalAssociation'
    t = case used_on
        when 'TaxonDetermination'
          TaxonDetermination.arel_table
        when 'BiologicalAssociation'
          BiologicalAssociation.arel_table
        end

    p = CollectionObject.arel_table

    # i is a select manager
    i = case used_on
        when 'BiologicalAssociation'
          t.project(t['biological_association_subject_id'], t['updated_at']).from(t)
            .where(
              t['updated_at'].gt(1.week.ago).and(
                t['biological_association_subject_type'].eq('CollectionObject')
              )
            )
              .where(t['updated_by_id'].eq(user_id))
              .where(t['project_id'].eq(project_id))
            .order(t['updated_at'].desc)
        else
          t.project(t['taxon_determination_object_id'], t['taxon_determination_object_type'], t['updated_at']).from(t)
            .where(t['updated_at'].gt( 1.week.ago ))
            .where(t['updated_by_id'].eq(user_id))
            .where(t['project_id'].eq(project_id))
            .order(t['updated_at'].desc)
        end

    # z is a table alias
    z = i.as('recent_t')

    j = case used_on
        when 'BiologicalAssociation'
          Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(
            z['biological_association_subject_id'].eq(p['id'])
          ))
        else
          # TODO fix biological_collection_object_id transition scoping
          Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(z['taxon_determination_object_id'].eq(p['id'])))
        end

    CollectionObject.joins(j).pluck(:id).uniq
  end

  # @params target [String] one of `TaxonDetermination`, `BiologicalAssociation` , nil
  # @return [Hash] otus optimized for user selection
  def self.select_optimized(user_id, project_id, target = nil)
    r = used_recently(user_id, project_id, target)
    h = {
      quick: [],
      pinboard: Extract.pinned_by(user_id).where(project_id:).to_a,
      recent: []
    }

    if target && !r.empty?
      n = target.tableize.to_sym
      h[:recent] = Extract.where('"extracts"."id" IN (?)', r.first(10) ).to_a
      h[:quick] = (Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a  +
          Extract.where('"extracts"."id" IN (?)', r.first(4) ).to_a).uniq
    else
      h[:recent] = Extract.where(project_id:, updated_by_id: user_id).order('updated_at DESC').limit(10).to_a
      h[:quick] = Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a
    end

    h
  end

end

Class Method Details

.select_optimized(user_id, project_id, target = nil) ⇒ Hash (protected)

Returns otus optimized for user selection.

Returns:

  • (Hash)

    otus optimized for user selection



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'app/models/extract.rb', line 158

def self.select_optimized(user_id, project_id, target = nil)
  r = used_recently(user_id, project_id, target)
  h = {
    quick: [],
    pinboard: Extract.pinned_by(user_id).where(project_id:).to_a,
    recent: []
  }

  if target && !r.empty?
    n = target.tableize.to_sym
    h[:recent] = Extract.where('"extracts"."id" IN (?)', r.first(10) ).to_a
    h[:quick] = (Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a  +
        Extract.where('"extracts"."id" IN (?)', r.first(4) ).to_a).uniq
  else
    h[:recent] = Extract.where(project_id:, updated_by_id: user_id).order('updated_at DESC').limit(10).to_a
    h[:quick] = Extract.pinned_by(user_id).pinboard_inserted.where(project_id:).to_a
  end

  h
end

.used_recently(user_id, project_id, used_on = '') ⇒ Scope (protected)

Returns the max 10 most recently used collection_objects, as ‘used_on`.

Parameters:

  • used_on (String) (defaults to: '')

    required, one of ‘Protocol`, `OriginRelationship`

Returns:

  • (Scope)

    the max 10 most recently used collection_objects, as ‘used_on`



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

def self.used_recently(user_id, project_id, used_on = '')
  return [] if used_on != 'TaxonDetermination' && used_on != 'BiologicalAssociation'
  t = case used_on
      when 'TaxonDetermination'
        TaxonDetermination.arel_table
      when 'BiologicalAssociation'
        BiologicalAssociation.arel_table
      end

  p = CollectionObject.arel_table

  # i is a select manager
  i = case used_on
      when 'BiologicalAssociation'
        t.project(t['biological_association_subject_id'], t['updated_at']).from(t)
          .where(
            t['updated_at'].gt(1.week.ago).and(
              t['biological_association_subject_type'].eq('CollectionObject')
            )
          )
            .where(t['updated_by_id'].eq(user_id))
            .where(t['project_id'].eq(project_id))
          .order(t['updated_at'].desc)
      else
        t.project(t['taxon_determination_object_id'], t['taxon_determination_object_type'], t['updated_at']).from(t)
          .where(t['updated_at'].gt( 1.week.ago ))
          .where(t['updated_by_id'].eq(user_id))
          .where(t['project_id'].eq(project_id))
          .order(t['updated_at'].desc)
      end

  # z is a table alias
  z = i.as('recent_t')

  j = case used_on
      when 'BiologicalAssociation'
        Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(
          z['biological_association_subject_id'].eq(p['id'])
        ))
      else
        # TODO fix biological_collection_object_id transition scoping
        Arel::Nodes::InnerJoin.new(z, Arel::Nodes::On.new(z['taxon_determination_object_id'].eq(p['id'])))
      end

  CollectionObject.joins(j).pluck(:id).uniq
end

Instance Method Details

#dwc_catalog_numberObject

In anticipation of DwC handling



94
95
96
# File 'app/models/extract.rb', line 94

def dwc_catalog_number
  catalog_number_cached
end

#preferred_catalog_numberIdentifier::Local::CatalogNumber?

TODO: Unify with CollectionObject in concern

Returns:



81
82
83
84
85
86
87
88
89
90
91
# File 'app/models/extract.rb', line 81

def preferred_catalog_number
  if i = Identifier::Local::CatalogNumber.where(identifier_object: self).order(:position).first
    i
  else
    if container
      container.identifiers.where(identifiers: {type: 'Identifier::Local::CatalogNumber'}).order(:position).first
    else
      nil
    end
  end
end

#referenced_otusObject

Returns Array all inferred or asserted OTUs that this OTU came from.

Returns:

  • Array all inferred or asserted OTUs that this OTU came from



71
72
73
74
75
76
# File 'app/models/extract.rb', line 71

def referenced_otus
  [
    [otus],
    [collection_objects.collect{|o| o.current_otu} ]
  ].flatten.compact.uniq
end

#set_madeObject (protected)



100
101
102
103
104
# File 'app/models/extract.rb', line 100

def set_made
  write_attribute(:year_made, Time.now.year)
  write_attribute(:month_made, Time.now.month)
  write_attribute(:day_made, Time.now.day)
end