Class: ObservationMatrixRowItem

Inherits:
ApplicationRecord show all
Includes:
Housekeeping, Shared::Citations, Shared::Identifiers, Shared::IsData, Shared::Notes, Shared::ObservationIndex, Shared::Tags, SoftValidation
Defined in:
app/models/observation_matrix_row_item.rb

Overview

Each ObservationMatrixRowItem determines a set of ObservationMatrixRows (1 if type is Single, 0 or more if type is Dynamic). Thus ObservationMatrixRowItems generate ObservationMatrixRows (not the other way around).

Direct Known Subclasses

Dynamic, Single

Defined Under Namespace

Classes: Dynamic, Single

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

#concatenated_notes_string, #reject_notes

Methods included from Shared::Tags

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

Methods included from Shared::Identifiers

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

Methods included from Shared::Citations

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

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods inherited from ApplicationRecord

transaction_with_retry

Instance Attribute Details

#controlled_vocabulary_term_idObject

Returns tag id if this row item is a ObservationMatrixRowItem::Dynamic::Tag.

Returns:

  • tag id if this row item is a ObservationMatrixRowItem::Dynamic::Tag



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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/observation_matrix_row_item.rb', line 24

class ObservationMatrixRowItem < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::Identifiers
  include Shared::Tags
  include Shared::Notes
  include Shared::IsData
  include SoftValidation
  include Shared::ObservationIndex

  acts_as_list scope: [:observation_matrix_id, :project_id]

  belongs_to :observation_matrix, inverse_of: :observation_matrix_row_items
  belongs_to :observation_object, polymorphic: true, inverse_of: :observation_matrix_row_items

  validates_presence_of :observation_matrix, :observation_object

  after_save :update_matrix_rows
  after_destroy :cleanup_matrix_rows

  # @return [Array]
  #   of all objects this row references
  # required/defined in subclasses
  def observation_objects
    []
  end

  def update_matrix_rows
    observation_objects.each do |o|
      update_single_matrix_row o
    end
  end

  def cleanup_matrix_rows
    return true if observation_objects.count == 0
    ObservationMatrixRow.where(observation_matrix:, observation_object: observation_objects).each do |mr|
      decrement_matrix_row_reference_count(mr)
    end
    true
  end

  def find_or_build_row(object)
    ObservationMatrixRow.find_or_initialize_by(observation_matrix:, observation_object: object)
  end

  def update_single_matrix_row(object)
    mr = find_or_build_row(object)
    mr.save! if !mr.persisted?
    increment_matrix_row_reference_count(mr)
  end

  # Not named "destroy_" because it doesn't always delete row
  def cleanup_single_matrix_row(object)
    mr = ObservationMatrixRow.where(observation_matrix:, observation_object: object).first
    decrement_matrix_row_reference_count(mr) if !mr.nil?
  end

  def self.human_name
    self.name.demodulize.humanize
  end

  # @return [Array]
  #   the required attributes for this subclass
  # override
  def self.subclass_attributes
    []
  end

  # @return [Object]
  #   the object used to define the set of matrix rows
  # override
  def matrix_row_item_object
    nil
  end

  # @return [matrix_row_item_object, nil]
  def object_is?(object_type)
    matrix_row_item_object.class.name == object_type ? matrix_row_item_object : nil
  end


  # @return [Array] of ObservationMatrixRowItems
  # @raise [TaxonWorks::Error]
  def self.batch_create(params)
    case params[:batch_type]
    when 'tags'
      batch_create_from_tags(params[:keyword_id], params[:klass], params[:observation_matrix_id])
    when 'pinboard'
      batch_create_from_pinboard(params[:observation_matrix_id], params[:project_id], params[:user_id], params[:klass])
    end
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_tags(keyword_id, klass, observation_matrix_id)
    created = []
    ObservationMatrixRowItem.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:tags).where(tags: {keyword_id:} ).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_tags(
            Tag.where(keyword_id:, tag_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
      raise TaxonWorks::Error, 'Internal error, missing required batch create data'
    end

    created = []
    ObservationMatrixRow.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:pinboard_items).where(pinboard_items: {user_id:, project_id:}).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_pinboard_items(
            PinboardItem.where(project_id:, user_id:, pinned_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  private

  # @return [Array]
  def self.create_for_tags(tag_scope, observation_matrix_id)
    a = []
    tag_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.tag_object)
    end
    a
  end

  # @param pinboard_item_scope [PinboardItem Scope]
  # @return [Array]
  #   create observation matrix row items for all scope items
  def self.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id)
    a = []
    pinboard_item_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.pinned_object)
    end
    a
  end

  def decrement_matrix_row_reference_count(mr)
    current = mr.reference_count - 1

    if current == 0
      mr.delete
    else
      mr.update_columns(reference_count: current)
      mr.update_columns(cached_observation_matrix_row_item_id: nil) if current == 1 && type =~ /Single/ # we've deleted the only single, so the last must be a Dynamic/Tagged
    end
  end

  # TODO: Should change behaviour of cached_
  # to only populate with id when reference count == 1
  # that way we could delete rows
  def increment_matrix_row_reference_count(mr)
    mr.update_columns(reference_count: (mr.reference_count || 0) +  1)
    mr.update_columns(cached_observation_matrix_row_item_id: id) if type =~ /Single/
  end
end

#observation_matrix_idInteger

Returns id of the matrix.

Returns:

  • (Integer)

    id of the matrix



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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/observation_matrix_row_item.rb', line 24

class ObservationMatrixRowItem < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::Identifiers
  include Shared::Tags
  include Shared::Notes
  include Shared::IsData
  include SoftValidation
  include Shared::ObservationIndex

  acts_as_list scope: [:observation_matrix_id, :project_id]

  belongs_to :observation_matrix, inverse_of: :observation_matrix_row_items
  belongs_to :observation_object, polymorphic: true, inverse_of: :observation_matrix_row_items

  validates_presence_of :observation_matrix, :observation_object

  after_save :update_matrix_rows
  after_destroy :cleanup_matrix_rows

  # @return [Array]
  #   of all objects this row references
  # required/defined in subclasses
  def observation_objects
    []
  end

  def update_matrix_rows
    observation_objects.each do |o|
      update_single_matrix_row o
    end
  end

  def cleanup_matrix_rows
    return true if observation_objects.count == 0
    ObservationMatrixRow.where(observation_matrix:, observation_object: observation_objects).each do |mr|
      decrement_matrix_row_reference_count(mr)
    end
    true
  end

  def find_or_build_row(object)
    ObservationMatrixRow.find_or_initialize_by(observation_matrix:, observation_object: object)
  end

  def update_single_matrix_row(object)
    mr = find_or_build_row(object)
    mr.save! if !mr.persisted?
    increment_matrix_row_reference_count(mr)
  end

  # Not named "destroy_" because it doesn't always delete row
  def cleanup_single_matrix_row(object)
    mr = ObservationMatrixRow.where(observation_matrix:, observation_object: object).first
    decrement_matrix_row_reference_count(mr) if !mr.nil?
  end

  def self.human_name
    self.name.demodulize.humanize
  end

  # @return [Array]
  #   the required attributes for this subclass
  # override
  def self.subclass_attributes
    []
  end

  # @return [Object]
  #   the object used to define the set of matrix rows
  # override
  def matrix_row_item_object
    nil
  end

  # @return [matrix_row_item_object, nil]
  def object_is?(object_type)
    matrix_row_item_object.class.name == object_type ? matrix_row_item_object : nil
  end


  # @return [Array] of ObservationMatrixRowItems
  # @raise [TaxonWorks::Error]
  def self.batch_create(params)
    case params[:batch_type]
    when 'tags'
      batch_create_from_tags(params[:keyword_id], params[:klass], params[:observation_matrix_id])
    when 'pinboard'
      batch_create_from_pinboard(params[:observation_matrix_id], params[:project_id], params[:user_id], params[:klass])
    end
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_tags(keyword_id, klass, observation_matrix_id)
    created = []
    ObservationMatrixRowItem.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:tags).where(tags: {keyword_id:} ).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_tags(
            Tag.where(keyword_id:, tag_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
      raise TaxonWorks::Error, 'Internal error, missing required batch create data'
    end

    created = []
    ObservationMatrixRow.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:pinboard_items).where(pinboard_items: {user_id:, project_id:}).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_pinboard_items(
            PinboardItem.where(project_id:, user_id:, pinned_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  private

  # @return [Array]
  def self.create_for_tags(tag_scope, observation_matrix_id)
    a = []
    tag_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.tag_object)
    end
    a
  end

  # @param pinboard_item_scope [PinboardItem Scope]
  # @return [Array]
  #   create observation matrix row items for all scope items
  def self.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id)
    a = []
    pinboard_item_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.pinned_object)
    end
    a
  end

  def decrement_matrix_row_reference_count(mr)
    current = mr.reference_count - 1

    if current == 0
      mr.delete
    else
      mr.update_columns(reference_count: current)
      mr.update_columns(cached_observation_matrix_row_item_id: nil) if current == 1 && type =~ /Single/ # we've deleted the only single, so the last must be a Dynamic/Tagged
    end
  end

  # TODO: Should change behaviour of cached_
  # to only populate with id when reference count == 1
  # that way we could delete rows
  def increment_matrix_row_reference_count(mr)
    mr.update_columns(reference_count: (mr.reference_count || 0) +  1)
    mr.update_columns(cached_observation_matrix_row_item_id: id) if type =~ /Single/
  end
end

#observation_object_idObject

Returns id of the object being observed.

Returns:

  • id of the object being observed



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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/observation_matrix_row_item.rb', line 24

class ObservationMatrixRowItem < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::Identifiers
  include Shared::Tags
  include Shared::Notes
  include Shared::IsData
  include SoftValidation
  include Shared::ObservationIndex

  acts_as_list scope: [:observation_matrix_id, :project_id]

  belongs_to :observation_matrix, inverse_of: :observation_matrix_row_items
  belongs_to :observation_object, polymorphic: true, inverse_of: :observation_matrix_row_items

  validates_presence_of :observation_matrix, :observation_object

  after_save :update_matrix_rows
  after_destroy :cleanup_matrix_rows

  # @return [Array]
  #   of all objects this row references
  # required/defined in subclasses
  def observation_objects
    []
  end

  def update_matrix_rows
    observation_objects.each do |o|
      update_single_matrix_row o
    end
  end

  def cleanup_matrix_rows
    return true if observation_objects.count == 0
    ObservationMatrixRow.where(observation_matrix:, observation_object: observation_objects).each do |mr|
      decrement_matrix_row_reference_count(mr)
    end
    true
  end

  def find_or_build_row(object)
    ObservationMatrixRow.find_or_initialize_by(observation_matrix:, observation_object: object)
  end

  def update_single_matrix_row(object)
    mr = find_or_build_row(object)
    mr.save! if !mr.persisted?
    increment_matrix_row_reference_count(mr)
  end

  # Not named "destroy_" because it doesn't always delete row
  def cleanup_single_matrix_row(object)
    mr = ObservationMatrixRow.where(observation_matrix:, observation_object: object).first
    decrement_matrix_row_reference_count(mr) if !mr.nil?
  end

  def self.human_name
    self.name.demodulize.humanize
  end

  # @return [Array]
  #   the required attributes for this subclass
  # override
  def self.subclass_attributes
    []
  end

  # @return [Object]
  #   the object used to define the set of matrix rows
  # override
  def matrix_row_item_object
    nil
  end

  # @return [matrix_row_item_object, nil]
  def object_is?(object_type)
    matrix_row_item_object.class.name == object_type ? matrix_row_item_object : nil
  end


  # @return [Array] of ObservationMatrixRowItems
  # @raise [TaxonWorks::Error]
  def self.batch_create(params)
    case params[:batch_type]
    when 'tags'
      batch_create_from_tags(params[:keyword_id], params[:klass], params[:observation_matrix_id])
    when 'pinboard'
      batch_create_from_pinboard(params[:observation_matrix_id], params[:project_id], params[:user_id], params[:klass])
    end
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_tags(keyword_id, klass, observation_matrix_id)
    created = []
    ObservationMatrixRowItem.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:tags).where(tags: {keyword_id:} ).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_tags(
            Tag.where(keyword_id:, tag_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
      raise TaxonWorks::Error, 'Internal error, missing required batch create data'
    end

    created = []
    ObservationMatrixRow.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:pinboard_items).where(pinboard_items: {user_id:, project_id:}).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_pinboard_items(
            PinboardItem.where(project_id:, user_id:, pinned_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  private

  # @return [Array]
  def self.create_for_tags(tag_scope, observation_matrix_id)
    a = []
    tag_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.tag_object)
    end
    a
  end

  # @param pinboard_item_scope [PinboardItem Scope]
  # @return [Array]
  #   create observation matrix row items for all scope items
  def self.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id)
    a = []
    pinboard_item_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.pinned_object)
    end
    a
  end

  def decrement_matrix_row_reference_count(mr)
    current = mr.reference_count - 1

    if current == 0
      mr.delete
    else
      mr.update_columns(reference_count: current)
      mr.update_columns(cached_observation_matrix_row_item_id: nil) if current == 1 && type =~ /Single/ # we've deleted the only single, so the last must be a Dynamic/Tagged
    end
  end

  # TODO: Should change behaviour of cached_
  # to only populate with id when reference count == 1
  # that way we could delete rows
  def increment_matrix_row_reference_count(mr)
    mr.update_columns(reference_count: (mr.reference_count || 0) +  1)
    mr.update_columns(cached_observation_matrix_row_item_id: id) if type =~ /Single/
  end
end

#observation_object_typeObject

Returns type of the object being observed.

Returns:

  • type of the object being observed



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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/observation_matrix_row_item.rb', line 24

class ObservationMatrixRowItem < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::Identifiers
  include Shared::Tags
  include Shared::Notes
  include Shared::IsData
  include SoftValidation
  include Shared::ObservationIndex

  acts_as_list scope: [:observation_matrix_id, :project_id]

  belongs_to :observation_matrix, inverse_of: :observation_matrix_row_items
  belongs_to :observation_object, polymorphic: true, inverse_of: :observation_matrix_row_items

  validates_presence_of :observation_matrix, :observation_object

  after_save :update_matrix_rows
  after_destroy :cleanup_matrix_rows

  # @return [Array]
  #   of all objects this row references
  # required/defined in subclasses
  def observation_objects
    []
  end

  def update_matrix_rows
    observation_objects.each do |o|
      update_single_matrix_row o
    end
  end

  def cleanup_matrix_rows
    return true if observation_objects.count == 0
    ObservationMatrixRow.where(observation_matrix:, observation_object: observation_objects).each do |mr|
      decrement_matrix_row_reference_count(mr)
    end
    true
  end

  def find_or_build_row(object)
    ObservationMatrixRow.find_or_initialize_by(observation_matrix:, observation_object: object)
  end

  def update_single_matrix_row(object)
    mr = find_or_build_row(object)
    mr.save! if !mr.persisted?
    increment_matrix_row_reference_count(mr)
  end

  # Not named "destroy_" because it doesn't always delete row
  def cleanup_single_matrix_row(object)
    mr = ObservationMatrixRow.where(observation_matrix:, observation_object: object).first
    decrement_matrix_row_reference_count(mr) if !mr.nil?
  end

  def self.human_name
    self.name.demodulize.humanize
  end

  # @return [Array]
  #   the required attributes for this subclass
  # override
  def self.subclass_attributes
    []
  end

  # @return [Object]
  #   the object used to define the set of matrix rows
  # override
  def matrix_row_item_object
    nil
  end

  # @return [matrix_row_item_object, nil]
  def object_is?(object_type)
    matrix_row_item_object.class.name == object_type ? matrix_row_item_object : nil
  end


  # @return [Array] of ObservationMatrixRowItems
  # @raise [TaxonWorks::Error]
  def self.batch_create(params)
    case params[:batch_type]
    when 'tags'
      batch_create_from_tags(params[:keyword_id], params[:klass], params[:observation_matrix_id])
    when 'pinboard'
      batch_create_from_pinboard(params[:observation_matrix_id], params[:project_id], params[:user_id], params[:klass])
    end
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_tags(keyword_id, klass, observation_matrix_id)
    created = []
    ObservationMatrixRowItem.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:tags).where(tags: {keyword_id:} ).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_tags(
            Tag.where(keyword_id:, tag_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
      raise TaxonWorks::Error, 'Internal error, missing required batch create data'
    end

    created = []
    ObservationMatrixRow.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:pinboard_items).where(pinboard_items: {user_id:, project_id:}).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_pinboard_items(
            PinboardItem.where(project_id:, user_id:, pinned_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  private

  # @return [Array]
  def self.create_for_tags(tag_scope, observation_matrix_id)
    a = []
    tag_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.tag_object)
    end
    a
  end

  # @param pinboard_item_scope [PinboardItem Scope]
  # @return [Array]
  #   create observation matrix row items for all scope items
  def self.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id)
    a = []
    pinboard_item_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.pinned_object)
    end
    a
  end

  def decrement_matrix_row_reference_count(mr)
    current = mr.reference_count - 1

    if current == 0
      mr.delete
    else
      mr.update_columns(reference_count: current)
      mr.update_columns(cached_observation_matrix_row_item_id: nil) if current == 1 && type =~ /Single/ # we've deleted the only single, so the last must be a Dynamic/Tagged
    end
  end

  # TODO: Should change behaviour of cached_
  # to only populate with id when reference count == 1
  # that way we could delete rows
  def increment_matrix_row_reference_count(mr)
    mr.update_columns(reference_count: (mr.reference_count || 0) +  1)
    mr.update_columns(cached_observation_matrix_row_item_id: id) if type =~ /Single/
  end
end

#positionInteger

Returns a sort order; determines the order in which the ObservationMatrixRows of ObservationMatrixRowItems are listed in the matrix.

Returns:

  • (Integer)

    a sort order; determines the order in which the ObservationMatrixRows of ObservationMatrixRowItems are listed in the matrix



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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/observation_matrix_row_item.rb', line 24

class ObservationMatrixRowItem < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::Identifiers
  include Shared::Tags
  include Shared::Notes
  include Shared::IsData
  include SoftValidation
  include Shared::ObservationIndex

  acts_as_list scope: [:observation_matrix_id, :project_id]

  belongs_to :observation_matrix, inverse_of: :observation_matrix_row_items
  belongs_to :observation_object, polymorphic: true, inverse_of: :observation_matrix_row_items

  validates_presence_of :observation_matrix, :observation_object

  after_save :update_matrix_rows
  after_destroy :cleanup_matrix_rows

  # @return [Array]
  #   of all objects this row references
  # required/defined in subclasses
  def observation_objects
    []
  end

  def update_matrix_rows
    observation_objects.each do |o|
      update_single_matrix_row o
    end
  end

  def cleanup_matrix_rows
    return true if observation_objects.count == 0
    ObservationMatrixRow.where(observation_matrix:, observation_object: observation_objects).each do |mr|
      decrement_matrix_row_reference_count(mr)
    end
    true
  end

  def find_or_build_row(object)
    ObservationMatrixRow.find_or_initialize_by(observation_matrix:, observation_object: object)
  end

  def update_single_matrix_row(object)
    mr = find_or_build_row(object)
    mr.save! if !mr.persisted?
    increment_matrix_row_reference_count(mr)
  end

  # Not named "destroy_" because it doesn't always delete row
  def cleanup_single_matrix_row(object)
    mr = ObservationMatrixRow.where(observation_matrix:, observation_object: object).first
    decrement_matrix_row_reference_count(mr) if !mr.nil?
  end

  def self.human_name
    self.name.demodulize.humanize
  end

  # @return [Array]
  #   the required attributes for this subclass
  # override
  def self.subclass_attributes
    []
  end

  # @return [Object]
  #   the object used to define the set of matrix rows
  # override
  def matrix_row_item_object
    nil
  end

  # @return [matrix_row_item_object, nil]
  def object_is?(object_type)
    matrix_row_item_object.class.name == object_type ? matrix_row_item_object : nil
  end


  # @return [Array] of ObservationMatrixRowItems
  # @raise [TaxonWorks::Error]
  def self.batch_create(params)
    case params[:batch_type]
    when 'tags'
      batch_create_from_tags(params[:keyword_id], params[:klass], params[:observation_matrix_id])
    when 'pinboard'
      batch_create_from_pinboard(params[:observation_matrix_id], params[:project_id], params[:user_id], params[:klass])
    end
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_tags(keyword_id, klass, observation_matrix_id)
    created = []
    ObservationMatrixRowItem.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:tags).where(tags: {keyword_id:} ).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_tags(
            Tag.where(keyword_id:, tag_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
      raise TaxonWorks::Error, 'Internal error, missing required batch create data'
    end

    created = []
    ObservationMatrixRow.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:pinboard_items).where(pinboard_items: {user_id:, project_id:}).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_pinboard_items(
            PinboardItem.where(project_id:, user_id:, pinned_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  private

  # @return [Array]
  def self.create_for_tags(tag_scope, observation_matrix_id)
    a = []
    tag_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.tag_object)
    end
    a
  end

  # @param pinboard_item_scope [PinboardItem Scope]
  # @return [Array]
  #   create observation matrix row items for all scope items
  def self.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id)
    a = []
    pinboard_item_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.pinned_object)
    end
    a
  end

  def decrement_matrix_row_reference_count(mr)
    current = mr.reference_count - 1

    if current == 0
      mr.delete
    else
      mr.update_columns(reference_count: current)
      mr.update_columns(cached_observation_matrix_row_item_id: nil) if current == 1 && type =~ /Single/ # we've deleted the only single, so the last must be a Dynamic/Tagged
    end
  end

  # TODO: Should change behaviour of cached_
  # to only populate with id when reference count == 1
  # that way we could delete rows
  def increment_matrix_row_reference_count(mr)
    mr.update_columns(reference_count: (mr.reference_count || 0) +  1)
    mr.update_columns(cached_observation_matrix_row_item_id: id) if type =~ /Single/
  end
end

#taxon_name_idObject

Returns taxon name id if this row item is a ObservationMatrixRowItem::Dynamic::TaxonName.

Returns:

  • taxon name id if this row item is a ObservationMatrixRowItem::Dynamic::TaxonName



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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/observation_matrix_row_item.rb', line 24

class ObservationMatrixRowItem < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::Identifiers
  include Shared::Tags
  include Shared::Notes
  include Shared::IsData
  include SoftValidation
  include Shared::ObservationIndex

  acts_as_list scope: [:observation_matrix_id, :project_id]

  belongs_to :observation_matrix, inverse_of: :observation_matrix_row_items
  belongs_to :observation_object, polymorphic: true, inverse_of: :observation_matrix_row_items

  validates_presence_of :observation_matrix, :observation_object

  after_save :update_matrix_rows
  after_destroy :cleanup_matrix_rows

  # @return [Array]
  #   of all objects this row references
  # required/defined in subclasses
  def observation_objects
    []
  end

  def update_matrix_rows
    observation_objects.each do |o|
      update_single_matrix_row o
    end
  end

  def cleanup_matrix_rows
    return true if observation_objects.count == 0
    ObservationMatrixRow.where(observation_matrix:, observation_object: observation_objects).each do |mr|
      decrement_matrix_row_reference_count(mr)
    end
    true
  end

  def find_or_build_row(object)
    ObservationMatrixRow.find_or_initialize_by(observation_matrix:, observation_object: object)
  end

  def update_single_matrix_row(object)
    mr = find_or_build_row(object)
    mr.save! if !mr.persisted?
    increment_matrix_row_reference_count(mr)
  end

  # Not named "destroy_" because it doesn't always delete row
  def cleanup_single_matrix_row(object)
    mr = ObservationMatrixRow.where(observation_matrix:, observation_object: object).first
    decrement_matrix_row_reference_count(mr) if !mr.nil?
  end

  def self.human_name
    self.name.demodulize.humanize
  end

  # @return [Array]
  #   the required attributes for this subclass
  # override
  def self.subclass_attributes
    []
  end

  # @return [Object]
  #   the object used to define the set of matrix rows
  # override
  def matrix_row_item_object
    nil
  end

  # @return [matrix_row_item_object, nil]
  def object_is?(object_type)
    matrix_row_item_object.class.name == object_type ? matrix_row_item_object : nil
  end


  # @return [Array] of ObservationMatrixRowItems
  # @raise [TaxonWorks::Error]
  def self.batch_create(params)
    case params[:batch_type]
    when 'tags'
      batch_create_from_tags(params[:keyword_id], params[:klass], params[:observation_matrix_id])
    when 'pinboard'
      batch_create_from_pinboard(params[:observation_matrix_id], params[:project_id], params[:user_id], params[:klass])
    end
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_tags(keyword_id, klass, observation_matrix_id)
    created = []
    ObservationMatrixRowItem.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:tags).where(tags: {keyword_id:} ).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_tags(
            Tag.where(keyword_id:, tag_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
      raise TaxonWorks::Error, 'Internal error, missing required batch create data'
    end

    created = []
    ObservationMatrixRow.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:pinboard_items).where(pinboard_items: {user_id:, project_id:}).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_pinboard_items(
            PinboardItem.where(project_id:, user_id:, pinned_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  private

  # @return [Array]
  def self.create_for_tags(tag_scope, observation_matrix_id)
    a = []
    tag_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.tag_object)
    end
    a
  end

  # @param pinboard_item_scope [PinboardItem Scope]
  # @return [Array]
  #   create observation matrix row items for all scope items
  def self.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id)
    a = []
    pinboard_item_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.pinned_object)
    end
    a
  end

  def decrement_matrix_row_reference_count(mr)
    current = mr.reference_count - 1

    if current == 0
      mr.delete
    else
      mr.update_columns(reference_count: current)
      mr.update_columns(cached_observation_matrix_row_item_id: nil) if current == 1 && type =~ /Single/ # we've deleted the only single, so the last must be a Dynamic/Tagged
    end
  end

  # TODO: Should change behaviour of cached_
  # to only populate with id when reference count == 1
  # that way we could delete rows
  def increment_matrix_row_reference_count(mr)
    mr.update_columns(reference_count: (mr.reference_count || 0) +  1)
    mr.update_columns(cached_observation_matrix_row_item_id: id) if type =~ /Single/
  end
end

#typeObject

Returns rails STI type.

Returns:

  • rails STI type



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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/observation_matrix_row_item.rb', line 24

class ObservationMatrixRowItem < ApplicationRecord
  include Housekeeping
  include Shared::Citations
  include Shared::Identifiers
  include Shared::Tags
  include Shared::Notes
  include Shared::IsData
  include SoftValidation
  include Shared::ObservationIndex

  acts_as_list scope: [:observation_matrix_id, :project_id]

  belongs_to :observation_matrix, inverse_of: :observation_matrix_row_items
  belongs_to :observation_object, polymorphic: true, inverse_of: :observation_matrix_row_items

  validates_presence_of :observation_matrix, :observation_object

  after_save :update_matrix_rows
  after_destroy :cleanup_matrix_rows

  # @return [Array]
  #   of all objects this row references
  # required/defined in subclasses
  def observation_objects
    []
  end

  def update_matrix_rows
    observation_objects.each do |o|
      update_single_matrix_row o
    end
  end

  def cleanup_matrix_rows
    return true if observation_objects.count == 0
    ObservationMatrixRow.where(observation_matrix:, observation_object: observation_objects).each do |mr|
      decrement_matrix_row_reference_count(mr)
    end
    true
  end

  def find_or_build_row(object)
    ObservationMatrixRow.find_or_initialize_by(observation_matrix:, observation_object: object)
  end

  def update_single_matrix_row(object)
    mr = find_or_build_row(object)
    mr.save! if !mr.persisted?
    increment_matrix_row_reference_count(mr)
  end

  # Not named "destroy_" because it doesn't always delete row
  def cleanup_single_matrix_row(object)
    mr = ObservationMatrixRow.where(observation_matrix:, observation_object: object).first
    decrement_matrix_row_reference_count(mr) if !mr.nil?
  end

  def self.human_name
    self.name.demodulize.humanize
  end

  # @return [Array]
  #   the required attributes for this subclass
  # override
  def self.subclass_attributes
    []
  end

  # @return [Object]
  #   the object used to define the set of matrix rows
  # override
  def matrix_row_item_object
    nil
  end

  # @return [matrix_row_item_object, nil]
  def object_is?(object_type)
    matrix_row_item_object.class.name == object_type ? matrix_row_item_object : nil
  end


  # @return [Array] of ObservationMatrixRowItems
  # @raise [TaxonWorks::Error]
  def self.batch_create(params)
    case params[:batch_type]
    when 'tags'
      batch_create_from_tags(params[:keyword_id], params[:klass], params[:observation_matrix_id])
    when 'pinboard'
      batch_create_from_pinboard(params[:observation_matrix_id], params[:project_id], params[:user_id], params[:klass])
    end
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_tags(keyword_id, klass, observation_matrix_id)
    created = []
    ObservationMatrixRowItem.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:tags).where(tags: {keyword_id:} ).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_tags(
            Tag.where(keyword_id:, tag_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array]
  # @raise [TaxonWorks::Error]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
      raise TaxonWorks::Error, 'Internal error, missing required batch create data'
    end

    created = []
    ObservationMatrixRow.transaction do
      begin
        if klass
          klass.safe_constantize.joins(:pinboard_items).where(pinboard_items: {user_id:, project_id:}).each do |o|
            created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
          end
        else
          created += create_for_pinboard_items(
            PinboardItem.where(project_id:, user_id:, pinned_object_type: OBSERVABLE_TYPES).all,
            observation_matrix_id
          )
        end
      rescue ActiveRecord::RecordInvalid => e
        raise TaxonWorks::Error, e.to_s, e.backtrace
      end
    end
    return created
  end

  private

  # @return [Array]
  def self.create_for_tags(tag_scope, observation_matrix_id)
    a = []
    tag_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.tag_object)
    end
    a
  end

  # @param pinboard_item_scope [PinboardItem Scope]
  # @return [Array]
  #   create observation matrix row items for all scope items
  def self.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id)
    a = []
    pinboard_item_scope.each do |o|
      a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.pinned_object)
    end
    a
  end

  def decrement_matrix_row_reference_count(mr)
    current = mr.reference_count - 1

    if current == 0
      mr.delete
    else
      mr.update_columns(reference_count: current)
      mr.update_columns(cached_observation_matrix_row_item_id: nil) if current == 1 && type =~ /Single/ # we've deleted the only single, so the last must be a Dynamic/Tagged
    end
  end

  # TODO: Should change behaviour of cached_
  # to only populate with id when reference count == 1
  # that way we could delete rows
  def increment_matrix_row_reference_count(mr)
    mr.update_columns(reference_count: (mr.reference_count || 0) +  1)
    mr.update_columns(cached_observation_matrix_row_item_id: id) if type =~ /Single/
  end
end

Class Method Details

.batch_create(params) ⇒ Array

Returns of ObservationMatrixRowItems.

Returns:

  • (Array)

    of ObservationMatrixRowItems

Raises:

  • (TaxonWorks::Error)


107
108
109
110
111
112
113
114
# File 'app/models/observation_matrix_row_item.rb', line 107

def self.batch_create(params)
  case params[:batch_type]
  when 'tags'
    batch_create_from_tags(params[:keyword_id], params[:klass], params[:observation_matrix_id])
  when 'pinboard'
    batch_create_from_pinboard(params[:observation_matrix_id], params[:project_id], params[:user_id], params[:klass])
  end
end

.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass) ⇒ Array

Returns:

  • (Array)

Raises:

  • (TaxonWorks::Error)


143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'app/models/observation_matrix_row_item.rb', line 143

def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
  if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
    raise TaxonWorks::Error, 'Internal error, missing required batch create data'
  end

  created = []
  ObservationMatrixRow.transaction do
    begin
      if klass
        klass.safe_constantize.joins(:pinboard_items).where(pinboard_items: {user_id:, project_id:}).each do |o|
          created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
        end
      else
        created += create_for_pinboard_items(
          PinboardItem.where(project_id:, user_id:, pinned_object_type: OBSERVABLE_TYPES).all,
          observation_matrix_id
        )
      end
    rescue ActiveRecord::RecordInvalid => e
      raise TaxonWorks::Error, e.to_s, e.backtrace
    end
  end
  return created
end

.batch_create_from_tags(keyword_id, klass, observation_matrix_id) ⇒ Array

Returns:

  • (Array)

Raises:

  • (TaxonWorks::Error)


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/models/observation_matrix_row_item.rb', line 119

def self.batch_create_from_tags(keyword_id, klass, observation_matrix_id)
  created = []
  ObservationMatrixRowItem.transaction do
    begin
      if klass
        klass.safe_constantize.joins(:tags).where(tags: {keyword_id:} ).each do |o|
          created.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o)
        end
      else
        created += create_for_tags(
          Tag.where(keyword_id:, tag_object_type: OBSERVABLE_TYPES).all,
          observation_matrix_id
        )
      end
    rescue ActiveRecord::RecordInvalid => e
      raise TaxonWorks::Error, e.to_s, e.backtrace
    end
  end
  return created
end

.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id) ⇒ Array (private)

Returns create observation matrix row items for all scope items.

Parameters:

Returns:

  • (Array)

    create observation matrix row items for all scope items



182
183
184
185
186
187
188
# File 'app/models/observation_matrix_row_item.rb', line 182

def self.create_for_pinboard_items(pinboard_item_scope, observation_matrix_id)
  a = []
  pinboard_item_scope.each do |o|
    a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.pinned_object)
  end
  a
end

.create_for_tags(tag_scope, observation_matrix_id) ⇒ Array (private)

Returns:

  • (Array)


171
172
173
174
175
176
177
# File 'app/models/observation_matrix_row_item.rb', line 171

def self.create_for_tags(tag_scope, observation_matrix_id)
  a = []
  tag_scope.each do |o|
    a.push ObservationMatrixRowItem::Single.create!(observation_matrix_id:, observation_object: o.tag_object)
  end
  a
end

.human_nameObject



81
82
83
# File 'app/models/observation_matrix_row_item.rb', line 81

def self.human_name
  self.name.demodulize.humanize
end

.subclass_attributesArray

override

Returns:

  • (Array)

    the required attributes for this subclass



88
89
90
# File 'app/models/observation_matrix_row_item.rb', line 88

def self.subclass_attributes
  []
end

Instance Method Details

#cleanup_matrix_rowsObject



57
58
59
60
61
62
63
# File 'app/models/observation_matrix_row_item.rb', line 57

def cleanup_matrix_rows
  return true if observation_objects.count == 0
  ObservationMatrixRow.where(observation_matrix:, observation_object: observation_objects).each do |mr|
    decrement_matrix_row_reference_count(mr)
  end
  true
end

#cleanup_single_matrix_row(object) ⇒ Object

Not named “destroy_” because it doesn’t always delete row



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

def cleanup_single_matrix_row(object)
  mr = ObservationMatrixRow.where(observation_matrix:, observation_object: object).first
  decrement_matrix_row_reference_count(mr) if !mr.nil?
end

#decrement_matrix_row_reference_count(mr) ⇒ Object (private)



190
191
192
193
194
195
196
197
198
199
# File 'app/models/observation_matrix_row_item.rb', line 190

def decrement_matrix_row_reference_count(mr)
  current = mr.reference_count - 1

  if current == 0
    mr.delete
  else
    mr.update_columns(reference_count: current)
    mr.update_columns(cached_observation_matrix_row_item_id: nil) if current == 1 && type =~ /Single/ # we've deleted the only single, so the last must be a Dynamic/Tagged
  end
end

#find_or_build_row(object) ⇒ Object



65
66
67
# File 'app/models/observation_matrix_row_item.rb', line 65

def find_or_build_row(object)
  ObservationMatrixRow.find_or_initialize_by(observation_matrix:, observation_object: object)
end

#increment_matrix_row_reference_count(mr) ⇒ Object (private)

TODO: Should change behaviour of cached_ to only populate with id when reference count == 1 that way we could delete rows



204
205
206
207
# File 'app/models/observation_matrix_row_item.rb', line 204

def increment_matrix_row_reference_count(mr)
  mr.update_columns(reference_count: (mr.reference_count || 0) +  1)
  mr.update_columns(cached_observation_matrix_row_item_id: id) if type =~ /Single/
end

#matrix_row_item_objectObject

override

Returns:

  • (Object)

    the object used to define the set of matrix rows



95
96
97
# File 'app/models/observation_matrix_row_item.rb', line 95

def matrix_row_item_object
  nil
end

#object_is?(object_type) ⇒ matrix_row_item_object?

Returns:



100
101
102
# File 'app/models/observation_matrix_row_item.rb', line 100

def object_is?(object_type)
  matrix_row_item_object.class.name == object_type ? matrix_row_item_object : nil
end

#observation_objectsArray

required/defined in subclasses

Returns:

  • (Array)

    of all objects this row references



47
48
49
# File 'app/models/observation_matrix_row_item.rb', line 47

def observation_objects
  []
end

#update_matrix_rowsObject



51
52
53
54
55
# File 'app/models/observation_matrix_row_item.rb', line 51

def update_matrix_rows
  observation_objects.each do |o|
    update_single_matrix_row o
  end
end

#update_single_matrix_row(object) ⇒ Object



69
70
71
72
73
# File 'app/models/observation_matrix_row_item.rb', line 69

def update_single_matrix_row(object)
  mr = find_or_build_row(object)
  mr.save! if !mr.persisted?
  increment_matrix_row_reference_count(mr)
end