Class: SqedDepiction
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- SqedDepiction
- Includes:
- Housekeeping, Shared::IsData, Shared::Notes, Shared::Tags
- Defined in:
- app/models/sqed_depiction.rb
Overview
A SqedDepiction identifies a depiction as sqed (https://github.com/SpeciesFileGroup/sqed) parsable, and records the metadata required for parsing a stage image.
Instance Attribute Summary collapse
-
#boundary_color ⇒ Symbol
Color of the boundaries in the image, default/recommendation is green.
-
#boundary_finder ⇒ String
Name of the sqed BoundaryFinder class to use, e.g.
-
#has_border ⇒ Boolean
True if the stage image has a border than needs to be detected.
-
#layout ⇒ Symbol
The Sqed layout, like :cross, :equal_cross, :vertical_offset_cross, :internal_box etc.
-
#metadata_map ⇒ Hash
The sqed metadata map, e.g.
-
#rebuild ⇒ Object
Returns the value of attribute rebuild.
-
#result_boundaries ⇒ Hash
A cache for the result.
-
#result_ocr ⇒ Hash
A cache for the ocr result.
-
#specimen_coordinates ⇒ Hash
Not presently used, the specific coordinates bounding the specimen(s) only.
Class Method Summary collapse
- .annotates? ⇒ Boolean
- .clear_stale_progress(sqed_depiction = nil) ⇒ Object
- .is_containable? ⇒ Boolean
- .last_without_data(project_id) ⇒ Object
-
.preprocess_empty(total = 10) ⇒ Integer
Caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes.
- .with_collection_object_data ⇒ Object
- .without_collection_object_data ⇒ Object
Instance Method Summary collapse
-
#collecting_event_sections ⇒ Array of symbols
The (named) sections in this depiction that may have collecting event label metadata.
- #depiction_is_of_collection_object ⇒ Object protected
- #enqueue_preprocess ⇒ Object
- #extraction_metadata ⇒ Object
- #is_in_progress? ⇒ Boolean
- #nearby_sqed_depictions(before = 5, after = 5, progress = false) ⇒ Object
-
#next_collection_object ⇒ CollectionObject?
The next collection object, by :id, created from the addition of a SqedDepiction.
- #next_sqed_depiction ⇒ Object
-
#next_without_data(progress = false) ⇒ SqedDepiction
The next record in which the collection object has no buffered data.
- #preprocess(force = true) ⇒ Object
- #recalculate ⇒ Object
- #set_invisible_boundary ⇒ Object
- #sqed_metadata_map ⇒ Object protected
Methods included from Shared::IsData
#errors_excepting, #full_error_messages_excepting, #identical, #is_community?, #is_in_use?, #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 Housekeeping
#has_polymorphic_relationship?
Methods inherited from ApplicationRecord
Instance Attribute Details
#boundary_color ⇒ Symbol
Color of the boundaries in the image, default/recommendation is green.
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'app/models/sqed_depiction.rb', line 36 class SqedDepiction < ApplicationRecord include Housekeeping include Shared::Tags include Shared::Notes # True?! include Shared::IsData attr_accessor :rebuild belongs_to :depiction has_one :image, through: :depiction has_one :collection_object, through: :depiction, source_type: 'CollectionObject', source: :depiction_object, inverse_of: :sqed_depictions validates_presence_of :depiction validates_presence_of :metadata_map, :boundary_color validates_inclusion_of :layout, in: SqedConfig::LAYOUTS.keys.map(&:to_s) validates_inclusion_of :boundary_finder, in: %w{Sqed::BoundaryFinder::ColorLineFinder Sqed::BoundaryFinder::Cross} validates_inclusion_of :has_border, in: [true, false] validate :depiction_is_of_collection_object accepts_nested_attributes_for :depiction before_validation :set_invisible_boundary, if: -> { layout.presence == 'stage' } def set_invisible_boundary self.boundary_color = 'invisible' end after_save :recalculate, if: -> { rebuild } after_create_commit :enqueue_preprocess def self.is_containable? false end def rebuild=(value) @rebuild = value end def recalculate preprocess(true) end def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end delegate :depiction_object, to: :depiction def self.annotates? false end def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end # @return [SqedDepiction] # the next record in which the collection object has no buffered data def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end def is_in_progress? in_progress && in_progress < 5.minutes.ago end def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end # @return [CollectionObject, nil] # the next collection object, by :id, created from the addition of a SqedDepiction def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end # @return [Array of symbols] # the (named) sections in this depiction that may have collecting event label metadata def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end # @return [Integer] # caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end protected def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end end |
#boundary_finder ⇒ String
Name of the sqed BoundaryFinder class to use, e.g. 'Sqed::BoundaryFinder::ColorLineFinder'
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'app/models/sqed_depiction.rb', line 36 class SqedDepiction < ApplicationRecord include Housekeeping include Shared::Tags include Shared::Notes # True?! include Shared::IsData attr_accessor :rebuild belongs_to :depiction has_one :image, through: :depiction has_one :collection_object, through: :depiction, source_type: 'CollectionObject', source: :depiction_object, inverse_of: :sqed_depictions validates_presence_of :depiction validates_presence_of :metadata_map, :boundary_color validates_inclusion_of :layout, in: SqedConfig::LAYOUTS.keys.map(&:to_s) validates_inclusion_of :boundary_finder, in: %w{Sqed::BoundaryFinder::ColorLineFinder Sqed::BoundaryFinder::Cross} validates_inclusion_of :has_border, in: [true, false] validate :depiction_is_of_collection_object accepts_nested_attributes_for :depiction before_validation :set_invisible_boundary, if: -> { layout.presence == 'stage' } def set_invisible_boundary self.boundary_color = 'invisible' end after_save :recalculate, if: -> { rebuild } after_create_commit :enqueue_preprocess def self.is_containable? false end def rebuild=(value) @rebuild = value end def recalculate preprocess(true) end def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end delegate :depiction_object, to: :depiction def self.annotates? false end def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end # @return [SqedDepiction] # the next record in which the collection object has no buffered data def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end def is_in_progress? in_progress && in_progress < 5.minutes.ago end def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end # @return [CollectionObject, nil] # the next collection object, by :id, created from the addition of a SqedDepiction def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end # @return [Array of symbols] # the (named) sections in this depiction that may have collecting event label metadata def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end # @return [Integer] # caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end protected def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end end |
#has_border ⇒ Boolean
Returns True if the stage image has a border than needs to be detected.
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'app/models/sqed_depiction.rb', line 36 class SqedDepiction < ApplicationRecord include Housekeeping include Shared::Tags include Shared::Notes # True?! include Shared::IsData attr_accessor :rebuild belongs_to :depiction has_one :image, through: :depiction has_one :collection_object, through: :depiction, source_type: 'CollectionObject', source: :depiction_object, inverse_of: :sqed_depictions validates_presence_of :depiction validates_presence_of :metadata_map, :boundary_color validates_inclusion_of :layout, in: SqedConfig::LAYOUTS.keys.map(&:to_s) validates_inclusion_of :boundary_finder, in: %w{Sqed::BoundaryFinder::ColorLineFinder Sqed::BoundaryFinder::Cross} validates_inclusion_of :has_border, in: [true, false] validate :depiction_is_of_collection_object accepts_nested_attributes_for :depiction before_validation :set_invisible_boundary, if: -> { layout.presence == 'stage' } def set_invisible_boundary self.boundary_color = 'invisible' end after_save :recalculate, if: -> { rebuild } after_create_commit :enqueue_preprocess def self.is_containable? false end def rebuild=(value) @rebuild = value end def recalculate preprocess(true) end def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end delegate :depiction_object, to: :depiction def self.annotates? false end def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end # @return [SqedDepiction] # the next record in which the collection object has no buffered data def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end def is_in_progress? in_progress && in_progress < 5.minutes.ago end def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end # @return [CollectionObject, nil] # the next collection object, by :id, created from the addition of a SqedDepiction def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end # @return [Array of symbols] # the (named) sections in this depiction that may have collecting event label metadata def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end # @return [Integer] # caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end protected def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end end |
#layout ⇒ Symbol
The Sqed layout, like :cross, :equal_cross, :vertical_offset_cross, :internal_box etc.
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'app/models/sqed_depiction.rb', line 36 class SqedDepiction < ApplicationRecord include Housekeeping include Shared::Tags include Shared::Notes # True?! include Shared::IsData attr_accessor :rebuild belongs_to :depiction has_one :image, through: :depiction has_one :collection_object, through: :depiction, source_type: 'CollectionObject', source: :depiction_object, inverse_of: :sqed_depictions validates_presence_of :depiction validates_presence_of :metadata_map, :boundary_color validates_inclusion_of :layout, in: SqedConfig::LAYOUTS.keys.map(&:to_s) validates_inclusion_of :boundary_finder, in: %w{Sqed::BoundaryFinder::ColorLineFinder Sqed::BoundaryFinder::Cross} validates_inclusion_of :has_border, in: [true, false] validate :depiction_is_of_collection_object accepts_nested_attributes_for :depiction before_validation :set_invisible_boundary, if: -> { layout.presence == 'stage' } def set_invisible_boundary self.boundary_color = 'invisible' end after_save :recalculate, if: -> { rebuild } after_create_commit :enqueue_preprocess def self.is_containable? false end def rebuild=(value) @rebuild = value end def recalculate preprocess(true) end def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end delegate :depiction_object, to: :depiction def self.annotates? false end def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end # @return [SqedDepiction] # the next record in which the collection object has no buffered data def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end def is_in_progress? in_progress && in_progress < 5.minutes.ago end def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end # @return [CollectionObject, nil] # the next collection object, by :id, created from the addition of a SqedDepiction def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end # @return [Array of symbols] # the (named) sections in this depiction that may have collecting event label metadata def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end # @return [Integer] # caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end protected def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end end |
#metadata_map ⇒ Hash
The sqed metadata map, e.g. {0 => :curator_metadata, 1 => :identifier, 2 => :image_registration, 3 => :annotated_specimen }.
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'app/models/sqed_depiction.rb', line 36 class SqedDepiction < ApplicationRecord include Housekeeping include Shared::Tags include Shared::Notes # True?! include Shared::IsData attr_accessor :rebuild belongs_to :depiction has_one :image, through: :depiction has_one :collection_object, through: :depiction, source_type: 'CollectionObject', source: :depiction_object, inverse_of: :sqed_depictions validates_presence_of :depiction validates_presence_of :metadata_map, :boundary_color validates_inclusion_of :layout, in: SqedConfig::LAYOUTS.keys.map(&:to_s) validates_inclusion_of :boundary_finder, in: %w{Sqed::BoundaryFinder::ColorLineFinder Sqed::BoundaryFinder::Cross} validates_inclusion_of :has_border, in: [true, false] validate :depiction_is_of_collection_object accepts_nested_attributes_for :depiction before_validation :set_invisible_boundary, if: -> { layout.presence == 'stage' } def set_invisible_boundary self.boundary_color = 'invisible' end after_save :recalculate, if: -> { rebuild } after_create_commit :enqueue_preprocess def self.is_containable? false end def rebuild=(value) @rebuild = value end def recalculate preprocess(true) end def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end delegate :depiction_object, to: :depiction def self.annotates? false end def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end # @return [SqedDepiction] # the next record in which the collection object has no buffered data def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end def is_in_progress? in_progress && in_progress < 5.minutes.ago end def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end # @return [CollectionObject, nil] # the next collection object, by :id, created from the addition of a SqedDepiction def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end # @return [Array of symbols] # the (named) sections in this depiction that may have collecting event label metadata def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end # @return [Integer] # caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end protected def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end end |
#rebuild ⇒ Object
Returns the value of attribute rebuild.
44 45 46 |
# File 'app/models/sqed_depiction.rb', line 44 def rebuild @rebuild end |
#result_boundaries ⇒ Hash
Returns A cache for the result.
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'app/models/sqed_depiction.rb', line 36 class SqedDepiction < ApplicationRecord include Housekeeping include Shared::Tags include Shared::Notes # True?! include Shared::IsData attr_accessor :rebuild belongs_to :depiction has_one :image, through: :depiction has_one :collection_object, through: :depiction, source_type: 'CollectionObject', source: :depiction_object, inverse_of: :sqed_depictions validates_presence_of :depiction validates_presence_of :metadata_map, :boundary_color validates_inclusion_of :layout, in: SqedConfig::LAYOUTS.keys.map(&:to_s) validates_inclusion_of :boundary_finder, in: %w{Sqed::BoundaryFinder::ColorLineFinder Sqed::BoundaryFinder::Cross} validates_inclusion_of :has_border, in: [true, false] validate :depiction_is_of_collection_object accepts_nested_attributes_for :depiction before_validation :set_invisible_boundary, if: -> { layout.presence == 'stage' } def set_invisible_boundary self.boundary_color = 'invisible' end after_save :recalculate, if: -> { rebuild } after_create_commit :enqueue_preprocess def self.is_containable? false end def rebuild=(value) @rebuild = value end def recalculate preprocess(true) end def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end delegate :depiction_object, to: :depiction def self.annotates? false end def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end # @return [SqedDepiction] # the next record in which the collection object has no buffered data def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end def is_in_progress? in_progress && in_progress < 5.minutes.ago end def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end # @return [CollectionObject, nil] # the next collection object, by :id, created from the addition of a SqedDepiction def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end # @return [Array of symbols] # the (named) sections in this depiction that may have collecting event label metadata def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end # @return [Integer] # caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end protected def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end end |
#result_ocr ⇒ Hash
Returns A cache for the ocr result.
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'app/models/sqed_depiction.rb', line 36 class SqedDepiction < ApplicationRecord include Housekeeping include Shared::Tags include Shared::Notes # True?! include Shared::IsData attr_accessor :rebuild belongs_to :depiction has_one :image, through: :depiction has_one :collection_object, through: :depiction, source_type: 'CollectionObject', source: :depiction_object, inverse_of: :sqed_depictions validates_presence_of :depiction validates_presence_of :metadata_map, :boundary_color validates_inclusion_of :layout, in: SqedConfig::LAYOUTS.keys.map(&:to_s) validates_inclusion_of :boundary_finder, in: %w{Sqed::BoundaryFinder::ColorLineFinder Sqed::BoundaryFinder::Cross} validates_inclusion_of :has_border, in: [true, false] validate :depiction_is_of_collection_object accepts_nested_attributes_for :depiction before_validation :set_invisible_boundary, if: -> { layout.presence == 'stage' } def set_invisible_boundary self.boundary_color = 'invisible' end after_save :recalculate, if: -> { rebuild } after_create_commit :enqueue_preprocess def self.is_containable? false end def rebuild=(value) @rebuild = value end def recalculate preprocess(true) end def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end delegate :depiction_object, to: :depiction def self.annotates? false end def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end # @return [SqedDepiction] # the next record in which the collection object has no buffered data def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end def is_in_progress? in_progress && in_progress < 5.minutes.ago end def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end # @return [CollectionObject, nil] # the next collection object, by :id, created from the addition of a SqedDepiction def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end # @return [Array of symbols] # the (named) sections in this depiction that may have collecting event label metadata def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end # @return [Integer] # caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end protected def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end end |
#specimen_coordinates ⇒ Hash
Returns Not presently used, the specific coordinates bounding the specimen(s) only.
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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'app/models/sqed_depiction.rb', line 36 class SqedDepiction < ApplicationRecord include Housekeeping include Shared::Tags include Shared::Notes # True?! include Shared::IsData attr_accessor :rebuild belongs_to :depiction has_one :image, through: :depiction has_one :collection_object, through: :depiction, source_type: 'CollectionObject', source: :depiction_object, inverse_of: :sqed_depictions validates_presence_of :depiction validates_presence_of :metadata_map, :boundary_color validates_inclusion_of :layout, in: SqedConfig::LAYOUTS.keys.map(&:to_s) validates_inclusion_of :boundary_finder, in: %w{Sqed::BoundaryFinder::ColorLineFinder Sqed::BoundaryFinder::Cross} validates_inclusion_of :has_border, in: [true, false] validate :depiction_is_of_collection_object accepts_nested_attributes_for :depiction before_validation :set_invisible_boundary, if: -> { layout.presence == 'stage' } def set_invisible_boundary self.boundary_color = 'invisible' end after_save :recalculate, if: -> { rebuild } after_create_commit :enqueue_preprocess def self.is_containable? false end def rebuild=(value) @rebuild = value end def recalculate preprocess(true) end def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end delegate :depiction_object, to: :depiction def self.annotates? false end def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end # @return [SqedDepiction] # the next record in which the collection object has no buffered data def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end def is_in_progress? in_progress && in_progress < 5.minutes.ago end def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end # @return [CollectionObject, nil] # the next collection object, by :id, created from the addition of a SqedDepiction def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end # @return [Array of symbols] # the (named) sections in this depiction that may have collecting event label metadata def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end # @return [Integer] # caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end protected def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end end |
Class Method Details
.annotates? ⇒ Boolean
97 98 99 |
# File 'app/models/sqed_depiction.rb', line 97 def self.annotates? false end |
.clear_stale_progress(sqed_depiction = nil) ⇒ Object
136 137 138 139 140 141 142 143 144 145 |
# File 'app/models/sqed_depiction.rb', line 136 def self.clear_stale_progress(sqed_depiction = nil) SqedDepiction.where('(in_progress < ?)', 5.minutes.ago) .update_all(in_progress: nil) if sqed_depiction SqedDepiction .where(updated_by_id: sqed_depiction.updated_by_id) .update_all(in_progress: nil) end true end |
.is_containable? ⇒ Boolean
69 70 71 |
# File 'app/models/sqed_depiction.rb', line 69 def self.is_containable? false end |
.last_without_data(project_id) ⇒ Object
147 148 149 150 |
# File 'app/models/sqed_depiction.rb', line 147 def self.last_without_data(project_id) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).order(:id).first object.nil? ? SqedDepiction.where(project_id:).order(id: :asc).first : object end |
.preprocess_empty(total = 10) ⇒ Integer
Returns caches section coordinates and ocr text for the first images that don't have such caches !! does not take into account project or user, just finds and processes.
202 203 204 205 206 207 208 209 210 211 212 |
# File 'app/models/sqed_depiction.rb', line 202 def self.preprocess_empty(total = 10) t = SqedDepiction.arel_table i = 0 while i < total r = SqedDepiction.where(t[:result_ocr].eq(nil).or(t[:result_boundary_coordinates].eq(nil)).to_sql).limit(1).first return i if r.nil? r.preprocess i = i + 1 end i end |
.with_collection_object_data ⇒ Object
101 102 103 104 105 106 107 108 |
# File 'app/models/sqed_depiction.rb', line 101 def self.with_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].not_eq(nil). or(t[:buffered_determinations].not_eq(nil)). or(t[:buffered_other_labels].not_eq(nil)) joins(:collection_object).where(q.to_sql) end |
.without_collection_object_data ⇒ Object
110 111 112 113 114 115 116 117 |
# File 'app/models/sqed_depiction.rb', line 110 def self.without_collection_object_data t = CollectionObject.arel_table q = t[:buffered_collecting_event].eq(nil). and(t[:buffered_determinations].eq(nil)). and(t[:buffered_other_labels].eq(nil)) joins(:collection_object).where(q.to_sql) end |
Instance Method Details
#collecting_event_sections ⇒ Array of symbols
Returns the (named) sections in this depiction that may have collecting event label metadata.
162 163 164 165 |
# File 'app/models/sqed_depiction.rb', line 162 def collecting_event_sections # !! master merge [:collecting_event_labels, :annotated_specimen] & [:metadata_map].values end |
#depiction_is_of_collection_object ⇒ Object (protected)
216 217 218 219 220 |
# File 'app/models/sqed_depiction.rb', line 216 def depiction_is_of_collection_object if depiction errors.add(:depiction, 'must be of a collection object') if !(depiction.depiction_object_type =~ /CollectionObject/) end end |
#enqueue_preprocess ⇒ Object
81 82 83 |
# File 'app/models/sqed_depiction.rb', line 81 def enqueue_preprocess SqedDepictionPreprocessJob.perform_later(sqed_depiction_id: id) end |
#extraction_metadata ⇒ Object
85 86 87 88 89 90 91 92 93 |
# File 'app/models/sqed_depiction.rb', line 85 def { boundary_color: boundary_color.to_sym, boundary_finder: boundary_finder&.constantize, has_border:, layout: layout.to_sym, metadata_map: } end |
#is_in_progress? ⇒ Boolean
132 133 134 |
# File 'app/models/sqed_depiction.rb', line 132 def is_in_progress? in_progress && in_progress < 5.minutes.ago end |
#nearby_sqed_depictions(before = 5, after = 5, progress = false) ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'app/models/sqed_depiction.rb', line 167 def nearby_sqed_depictions(before = 5, after = 5, progress = false) q = SqedDepiction.where(project_id:) if progress == true SqedDepiction.clear_stale_progress(self) q = q.where(in_progress: nil) end a = q.where('id > ?', id).order(:id).limit(after) b = q.where('id < ?', id).order('id DESC').limit(before) return { before: b, after: a} end |
#next_collection_object ⇒ CollectionObject?
Returns the next collection object, by :id, created from the addition of a SqedDepiction.
154 155 156 157 158 |
# File 'app/models/sqed_depiction.rb', line 154 def next_collection_object object = CollectionObject.joins(:sqed_depictions).where(project_id:).where('sqed_depictions.id > ?', id).where('collection_objects.id <> ?', depiction_object.id).order(:id).first object = CollectionObject.joins(:sqed_depictions).order(:id).first if object.nil? object end |
#next_sqed_depiction ⇒ Object
181 182 183 184 |
# File 'app/models/sqed_depiction.rb', line 181 def next_sqed_depiction sd = SqedDepiction.where(project_id:).where('id > ?', id).order(:id).limit(1) sd.any? ? sd.first : SqedDepiction.where(project_id:).first end |
#next_without_data(progress = false) ⇒ SqedDepiction
Returns the next record in which the collection object has no buffered data.
121 122 123 124 125 126 127 128 129 130 |
# File 'app/models/sqed_depiction.rb', line 121 def next_without_data(progress = false) if progress SqedDepiction.clear_stale_progress(self) object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(in_progress: nil, project_id:).order(:id).first else object = SqedDepiction.without_collection_object_data.with_project_id(project_id).where('collection_objects.id <> ?', depiction_object.id).where('sqed_depictions.id > ?', id).order(:id).first object || SqedDepiction.where(project_id:).order(:id).first end end |
#preprocess(force = true) ⇒ Object
186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'app/models/sqed_depiction.rb', line 186 def preprocess(force = true) return true if !File.exist?(depiction.image.image_file.path(:original)) # don't rebuild if not forced and one or both cache is empty if !force if result_ocr.present? || result_boundary_coordinates.present? return true end end # otherwise rebuild result = Vendor::SqedToTaxonworks::Result.new(depiction_id: depiction.id) result.cache_all end |
#recalculate ⇒ Object
77 78 79 |
# File 'app/models/sqed_depiction.rb', line 77 def recalculate preprocess(true) end |
#set_invisible_boundary ⇒ Object
62 63 64 |
# File 'app/models/sqed_depiction.rb', line 62 def set_invisible_boundary self.boundary_color = 'invisible' end |
#sqed_metadata_map ⇒ Object (protected)
222 223 224 |
# File 'app/models/sqed_depiction.rb', line 222 def .inject({}){|hsh, i| hsh.merge(i[0].to_i => i[1].to_sym)} end |