mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-10-31 11:21:06 -04:00
Keep every packet label when a side is photographed again, and use the newest photo of a side. Before, giving a second photo the same label cleared the older photo's, which let it back into the crop's photos and default picture. Carry the label through the HTML photo picker (its search form and the links that add a photo), so a packet photo added without JavaScript is labelled. Have the OCR script treat any network failure on an image (DNS, timeout) as that image failing, so the rest of the batch still runs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
60 lines
2.0 KiB
Ruby
60 lines
2.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class PhotoAssociation < ApplicationRecord
|
|
belongs_to :photo, touch: true, inverse_of: :photo_associations
|
|
belongs_to :crop, optional: true, touch: true # , counter_cache: true
|
|
belongs_to :photographable, polymorphic: true, touch: true
|
|
|
|
# What part of the item a photo is of, for the items that have parts: a job's
|
|
# photo from before or after it was done, the front or back of a seed packet.
|
|
# Most photos have no label.
|
|
LABELS = {
|
|
'Job' => %w(before after),
|
|
'Seed' => %w(front back)
|
|
}.freeze
|
|
|
|
normalizes :label, with: ->(label) { label.presence }
|
|
validates :label, inclusion: { in: ->(association) { LABELS.fetch(association.photographable_type, []) } }, allow_nil: true
|
|
|
|
validate :photo_and_item_have_same_owner
|
|
validate :crop_present
|
|
|
|
##
|
|
## Triggers
|
|
before_validation :set_crop
|
|
|
|
# Photos of a seed packet say what the packet says, not what the crop looks
|
|
# like, so a photo labelled as one is left out of a crop's own photos.
|
|
scope :of_packets, -> { where(photographable_type: 'Seed').where.not(label: nil) }
|
|
# Those of a seed that has no text from its packet yet, which is what still
|
|
# needs reading.
|
|
scope :awaiting_text, -> { of_packets.where(photographable_id: Seed.unscoped.where(packet_text: [nil, '']).select(:id)) }
|
|
|
|
def self.item(item_id, item_type)
|
|
find_by!(photographable_id: item_id, photographable_type: item_type).photographable
|
|
end
|
|
|
|
private
|
|
|
|
def set_crop
|
|
if %w(Planting Seed Harvest).include?(photographable_type)
|
|
self.crop_id = photographable.crop_id
|
|
elsif photographable_type == 'Crop'
|
|
self.crop_id = photographable_id
|
|
end
|
|
end
|
|
|
|
def photo_and_item_have_same_owner
|
|
return if photographable_type == 'Crop'
|
|
return if photographable.blank?
|
|
|
|
errors.add(:photo, :photo_owner_mismatch) unless photographable.owner_id == photo.owner_id
|
|
end
|
|
|
|
def crop_present
|
|
return unless %w(Planting Seed Harvest).include?(photographable_type)
|
|
|
|
errors.add(:crop_id, :calculate_crop_failed) if crop_id.blank?
|
|
end
|
|
end
|