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 is set of Otus or Collection Objects (1 or more)

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

#observation_matrix_idInteger

Returns id of the matrix.

Returns:

  • (Integer)

    id of the matrix



15
16
17
18
19
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'app/models/observation_matrix_row_item.rb', line 15

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

  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
  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, false]
  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
        return false
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array, false]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    return false if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
    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
      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



15
16
17
18
19
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'app/models/observation_matrix_row_item.rb', line 15

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

  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
  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, false]
  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
        return false
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array, false]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    return false if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
    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
      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



15
16
17
18
19
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'app/models/observation_matrix_row_item.rb', line 15

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

  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
  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, false]
  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
        return false
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array, false]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    return false if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
    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
      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.

Returns:

  • (Integer)

    a sort order



15
16
17
18
19
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'app/models/observation_matrix_row_item.rb', line 15

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

  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
  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, false]
  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
        return false
      end
    end
    return created
  end

  # @params klass [String] the class name like `Otu` or `CollectionObject`
  # @return [Array, false]
  def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
    return false if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
    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
      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



97
98
99
100
101
102
103
104
# File 'app/models/observation_matrix_row_item.rb', line 97

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, false

Returns:

  • (Array, false)


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/models/observation_matrix_row_item.rb', line 131

def self.batch_create_from_pinboard(observation_matrix_id, project_id, user_id, klass)
  return false if observation_matrix_id.blank? || project_id.blank? || user_id.blank?
  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
    end
  end
  return created
end

.batch_create_from_tags(keyword_id, klass, observation_matrix_id) ⇒ Array, false

Returns:

  • (Array, false)


108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'app/models/observation_matrix_row_item.rb', line 108

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
      return false
    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



167
168
169
170
171
172
173
# File 'app/models/observation_matrix_row_item.rb', line 167

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)


156
157
158
159
160
161
162
# File 'app/models/observation_matrix_row_item.rb', line 156

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



72
73
74
# File 'app/models/observation_matrix_row_item.rb', line 72

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

.subclass_attributesArray

override

Returns:

  • (Array)

    the required attributes for this subclass



79
80
81
# File 'app/models/observation_matrix_row_item.rb', line 79

def self.subclass_attributes
  []
end

Instance Method Details

#cleanup_matrix_rowsObject



48
49
50
51
52
53
54
# File 'app/models/observation_matrix_row_item.rb', line 48

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



67
68
69
70
# File 'app/models/observation_matrix_row_item.rb', line 67

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)



175
176
177
178
179
180
181
182
183
184
# File 'app/models/observation_matrix_row_item.rb', line 175

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



56
57
58
# File 'app/models/observation_matrix_row_item.rb', line 56

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



189
190
191
192
# File 'app/models/observation_matrix_row_item.rb', line 189

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



86
87
88
# File 'app/models/observation_matrix_row_item.rb', line 86

def matrix_row_item_object
  nil
end

#object_is?(object_type) ⇒ matrix_row_item_object?

Returns:



91
92
93
# File 'app/models/observation_matrix_row_item.rb', line 91

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



38
39
40
# File 'app/models/observation_matrix_row_item.rb', line 38

def observation_objects
  []
end

#update_matrix_rowsObject



42
43
44
45
46
# File 'app/models/observation_matrix_row_item.rb', line 42

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

#update_single_matrix_row(object) ⇒ Object



60
61
62
63
64
# File 'app/models/observation_matrix_row_item.rb', line 60

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