mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-03-26 02:33:03 -04:00
* Add collaboration model * Permissions and garden show * List by owner, or where I am a collaborator * Add index * Add permissions * Typo * Typo * Add route * Update schema * Update schema * Add CRUD * Add CRUD * Add CRUD * Factory * Add validations * Rubocop * Rubocop * Rubocop * Unique index * Fix * Make CI more fine grained for faster feedback * Swap order * Fix path, fail-fast * Fix spec * Remove 'significant drop in coverage' as not everything runs in one giant run * Fix tests?
169 lines
4.8 KiB
Ruby
169 lines
4.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Harvest < ApplicationRecord
|
|
include ActionView::Helpers::NumberHelper
|
|
extend FriendlyId
|
|
include PhotoCapable
|
|
include Ownable
|
|
include SearchHarvests
|
|
include Likeable
|
|
|
|
friendly_id :harvest_slug, use: %i(slugged finders)
|
|
|
|
# Constants
|
|
UNITS_VALUES = {
|
|
"individual" => "individual",
|
|
"bunches" => "bunch",
|
|
"sprigs" => "sprig",
|
|
"handfuls" => "handful",
|
|
"litres" => "litre",
|
|
"pints" => "pint",
|
|
"quarts" => "quart",
|
|
"buckets" => "bucket",
|
|
"baskets" => "basket",
|
|
"bushels" => "bushel"
|
|
}.freeze
|
|
|
|
WEIGHT_UNITS_VALUES = {
|
|
"kg" => "kg",
|
|
"lb" => "lb",
|
|
"oz" => "oz"
|
|
}.freeze
|
|
|
|
##
|
|
## Triggers
|
|
after_validation :cleanup_quantities
|
|
before_save :set_si_weight
|
|
|
|
##
|
|
## Relationships
|
|
belongs_to :crop, counter_cache: true
|
|
belongs_to :plant_part, counter_cache: true
|
|
belongs_to :planting, optional: true, counter_cache: true
|
|
|
|
##
|
|
## Scopes
|
|
scope :interesting, -> { has_photos.one_per_owner }
|
|
scope :recent, -> { order(created_at: :desc) }
|
|
scope :one_per_owner, lambda {
|
|
joins("JOIN members m ON (m.id=harvests.owner_id)
|
|
LEFT OUTER JOIN harvests h2
|
|
ON (m.id=h2.owner_id AND harvests.id < h2.id)").where("h2 IS NULL")
|
|
}
|
|
|
|
delegate :name, :slug, to: :crop, prefix: true
|
|
delegate :login_name, :slug, to: :owner, prefix: true
|
|
delegate :name, to: :plant_part, prefix: true
|
|
|
|
##
|
|
## Validations
|
|
validates :crop, approved: true
|
|
validates :crop, presence: { message: "must be present and exist in our database" }
|
|
validates :plant_part, presence: { message: "must be present and exist in our database" }
|
|
validates :harvested_at, presence: true
|
|
validates :quantity, allow_nil: true, numericality: {
|
|
only_integer: false, greater_than_or_equal_to: 0
|
|
}
|
|
validates :unit, allow_blank: true, inclusion: {
|
|
in: UNITS_VALUES.values, message: "%<value>s is not a valid unit"
|
|
}
|
|
validates :weight_quantity, allow_nil: true, numericality: { only_integer: false }
|
|
validates :weight_unit, allow_blank: true, inclusion: {
|
|
in: WEIGHT_UNITS_VALUES.values, message: "%<value>s is not a valid unit"
|
|
}
|
|
validate :crop_must_match_planting
|
|
validate :owner_must_match_planting
|
|
validate :harvest_must_be_after_planting
|
|
|
|
def time_from_planting_to_harvest
|
|
return if planting.blank?
|
|
|
|
harvested_at - planting.planted_at
|
|
end
|
|
|
|
def default_photo
|
|
most_liked_photo || planting&.default_photo
|
|
end
|
|
|
|
# we're storing the harvest weight in kilograms in the db too
|
|
# to make data manipulation easier
|
|
def set_si_weight
|
|
return if weight_unit.nil?
|
|
|
|
weight_string = "#{weight_quantity} #{weight_unit}"
|
|
self.si_weight = Unit.new(weight_string).convert_to("kg").to_s("%0.3f").delete(" kg").to_f
|
|
end
|
|
|
|
def cleanup_quantities
|
|
self.quantity = nil if quantity&.zero?
|
|
self.unit = nil if quantity.blank?
|
|
self.weight_quantity = nil if weight_quantity&.zero?
|
|
self.weight_unit = nil if weight_quantity.blank?
|
|
end
|
|
|
|
def harvest_slug
|
|
"#{owner.login_name}-#{crop}".downcase.tr(' ', '-')
|
|
end
|
|
|
|
# stringify as "beet in Skud's backyard" or similar
|
|
def to_s
|
|
# 50 individual apples, weighing 3lb
|
|
# 2 buckets of apricots, weighing 10kg
|
|
"#{quantity_to_human} #{unit_to_human} #{crop_name_to_human} #{weight_to_human}".strip
|
|
end
|
|
|
|
def quantity_to_human
|
|
return number_to_human(quantity.to_s, strip_insignificant_zeros: true) if quantity
|
|
|
|
""
|
|
end
|
|
|
|
def unit_to_human
|
|
return "" unless quantity && unit
|
|
return 'individual' if unit == 'individual'
|
|
return "#{unit} of" if quantity == 1
|
|
|
|
"#{unit.pluralize} of"
|
|
end
|
|
|
|
def weight_to_human
|
|
return "" unless weight_quantity
|
|
|
|
"weighing #{number_to_human(weight_quantity, strip_insignificant_zeros: true)} #{weight_unit}"
|
|
end
|
|
|
|
def crop_name_to_human
|
|
if unit != 'individual' # buckets of apricot*s*
|
|
crop.name.pluralize
|
|
elsif quantity == 1
|
|
crop.name
|
|
else
|
|
crop.name.pluralize
|
|
end.to_s
|
|
end
|
|
|
|
private
|
|
|
|
def crop_must_match_planting
|
|
return if planting.blank? # only check if we are linked to a planting
|
|
|
|
errors.add(:planting, "must be the same crop") unless crop == planting.crop
|
|
end
|
|
|
|
def owner_must_match_planting
|
|
return if planting.blank? # only check if we are linked to a planting
|
|
|
|
return if owner == planting.owner || planting.garden.garden_collaborators.where(member_id: owner).any?
|
|
|
|
errors.add(:owner,
|
|
"of harvest must be the same as planting, or a collaborator on that garden")
|
|
end
|
|
|
|
def harvest_must_be_after_planting
|
|
# only check if we are linked to a planting
|
|
return unless harvested_at.present? && planting.present? && planting.planted_at.present?
|
|
|
|
errors.add(:planting, "cannot be harvested before planting") unless harvested_at > planting.planted_at
|
|
end
|
|
end
|