Skip to content

Commit

Permalink
feat: plant seeds #85 (#87)
Browse files Browse the repository at this point in the history
Modified the `request_distributions` schema to replace the `area` with `positions_on_bench` and `dimensions`.
Simplified routes by adjusting the `request_distributions` index action.
  • Loading branch information
MisterOryon authored Dec 17, 2024
1 parent 62be386 commit 4201ad3
Show file tree
Hide file tree
Showing 21 changed files with 519 additions and 208 deletions.
2 changes: 1 addition & 1 deletion app/blueprinter/bench_blueprint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

class BenchBlueprint < Base
# Fields
fields :name, :dimensions, :positions
fields :name, :dimensions, :positions, :greenhouse_id
end
2 changes: 1 addition & 1 deletion app/blueprinter/request_distribution_blueprint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

class RequestDistributionBlueprint < Base
# Fields
fields :bench_id, :plant_stage_id, :pot_id, :pot_quantity, :area
fields :bench_id, :plant_stage_id, :pot_id, :pot_quantity, :positions_on_bench, :dimensions, :request_id

field :greenhouse_id do |request_distribution|
request_distribution.bench&.greenhouse_id
Expand Down
19 changes: 3 additions & 16 deletions app/controllers/api/v1/request_distributions_controller.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# frozen_string_literal: true

class Api::V1::RequestDistributionsController < ApiController
before_action :set_request, only: %i[index create]
before_action :set_request, only: %i[create]
before_action :set_distribution, only: %i[show update destroy]

def index
request_distributions = policy_scope(@request.request_distributions)
request_distributions = policy_scope(RequestDistribution.all)
authorize request_distributions

render json: apply_fetcheable(request_distributions).to_blueprint
Expand All @@ -19,7 +19,6 @@ def create
authorize RequestDistribution

distribution = @request.request_distributions.new(distribution_params)
distribution.area = compute_area(distribution)

if distribution.save
render json: distribution.to_blueprint, status: :created
Expand All @@ -30,7 +29,6 @@ def create

def update
@distribution.assign_attributes(distribution_params)
@distribution.area = compute_area(@distribution)

if @distribution.save
render json: @distribution.to_blueprint
Expand Down Expand Up @@ -59,17 +57,6 @@ def set_distribution
end

def distribution_params
params.permit(:bench_id, :plant_stage_id, :pot_id, :pot_quantity)
end

def compute_area(distribution)
return params[:area] if params[:area].present?

pot_quantity = distribution.pot_quantity
pot_area = distribution.pot&.area

return if pot_quantity.nil? || pot_area.nil?

pot_quantity * pot_area
params.permit(:bench_id, :plant_stage_id, :pot_id, :pot_quantity, positions_on_bench: [], dimensions: [])
end
end
31 changes: 19 additions & 12 deletions app/models/bench.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,19 @@

class Bench < ApplicationRecord
# Validations
validates :dimensions,
presence: true,
length: { is: 2, message: I18n.t('activerecord.errors.models.bench.attributes.dimensions.incorrect_size') }
validate :dimensions_must_be_strictly_positive
include ValidateDimensionsConcern

validates :positions,
presence: true,
length: { is: 2, message: I18n.t('activerecord.errors.models.bench.attributes.positions.incorrect_size') }
validate :positions_must_be_positive

validates_associated :request_distributions,
message: I18n.t(
'activerecord.errors.models.bench.attributes.request_distributions.invalid_distribution'
)
validates_associated :request_distributions, if: :dimensions_and_positions_valid?

validate :overlapping_bench_exists, on: %i[create update]

validate :distributions_areas_lower_than_bench_area, on: %i[update]

# Associations
belongs_to :greenhouse,
class_name: 'Greenhouse',
Expand All @@ -29,12 +25,19 @@ class Bench < ApplicationRecord
inverse_of: :bench,
dependent: :restrict_with_error
# Checks
def dimensions_must_be_strictly_positive
return unless dimensions
def distributions_areas_lower_than_bench_area
return if errors[:dimensions].any?

width1, height1 = dimensions

return unless dimensions.any? { |d| d <= 0 }
if request_distributions.any? do |request_distribution|
x2, y2 = request_distribution.positions_on_bench
width2, height2 = request_distribution.dimensions

errors.add(:dimensions, 'each dimension must be greater than 0')
width1 < x2 + width2 || height1 < y2 + height2
end
errors.add(:dimensions, 'sum of distributions areas can\'t be greater than bench area')
end
end

def positions_must_be_positive
Expand Down Expand Up @@ -66,6 +69,10 @@ def positions_overlap?(other_bench)

x1 < x2 + width2 && x1 + width1 > x2 && y1 < y2 + height2 && y1 + height1 > y2
end

def dimensions_and_positions_valid?
errors[:dimensions].empty? && errors[:positions].empty?
end
end

# == Schema Information
Expand Down
25 changes: 25 additions & 0 deletions app/models/concerns/validate_dimensions_concern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# frozen_string_literal: true

require 'active_support/concern'

module ValidateDimensionsConcern
extend ActiveSupport::Concern

included do
validates :dimensions,
presence: true,
length: { is: 2,
message: I18n.t('activerecord.errors.concern.attributes.dimensions.incorrect_size') }
validate :dimensions_must_be_strictly_positive

private

def dimensions_must_be_strictly_positive
return unless dimensions

return unless dimensions.any? { |d| d <= 0 }

errors.add(:dimensions, I18n.t('activerecord.errors.concern.attributes.dimensions.not_positive'))
end
end
end
99 changes: 76 additions & 23 deletions app/models/request_distribution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@

class RequestDistribution < ApplicationRecord
# Validations
validates :area, numericality: { greater_than: 0 }
include ValidateDimensionsConcern

validates :pot_quantity, presence: true, if: -> { pot.present? }
validates :pot_quantity, presence: true, numericality: { greater_than: 0 }

validate :plant_stage_from_request,
:distributions_areas_lower_than_bench_area
validate :plant_stage_from_request

validates :positions_on_bench,
presence: true,
length: { is: 2, message: I18n.t('activerecord.errors.models.bench.attributes.positions.incorrect_size') }
validate :positions_must_be_positive

validate :validate_seeds_left_to_plant, on: %i[create update]

validate :overlapping_distribution_exists, on: %i[create update]

validate :distribution_within_bench_bounds, on: %i[create update]

# Associations
belongs_to :request,
Expand All @@ -24,11 +34,22 @@ class RequestDistribution < ApplicationRecord

belongs_to :pot,
class_name: 'Pot',
inverse_of: :request_distributions,
optional: true
inverse_of: :request_distributions

# Private instance methods

def validate_seeds_left_to_plant
return if errors[:pot_quantity].any?

quantity_change = pot_quantity - (pot_quantity_was || 0)

total_pot_quantity = request.request_distributions.where.not(id:).sum(:pot_quantity)

return if request.quantity >= quantity_change + total_pot_quantity

errors.add(:pot_quantity, 'not enough seeds left to plant for this request')
end

private

def plant_stage_from_request
Expand All @@ -38,32 +59,64 @@ def plant_stage_from_request
errors.add(:plant_stage, 'must be from requested plant')
end

def distributions_areas_lower_than_bench_area
return if area.nil?
return if bench&.dimensions.nil?
def positions_must_be_positive
return unless positions_on_bench

return unless positions_on_bench.any?(&:negative?)

errors.add(:positions_on_bench, 'each position must be positive')
end

def overlapping_distribution_exists
return if errors[:dimensions].any? || errors[:positions_on_bench].any?
return unless bench

if bench.request_distributions.any? do |other_distribution|
next if other_distribution == self

positions_overlap?(other_distribution)
end
errors.add(:positions_on_bench, 'distribution overlaps with an existing distribution')
end
end

def positions_overlap?(other_distribution)
x1, y1 = positions_on_bench
width1, height1 = dimensions
x2, y2 = other_distribution.positions_on_bench
width2, height2 = other_distribution.dimensions

x1 < x2 + width2 && x1 + width1 > x2 && y1 < y2 + height2 && y1 + height1 > y2
end

def distribution_within_bench_bounds
return if errors[:dimensions].any? || errors[:positions_on_bench].any?
return unless bench

bench_dimensions = bench.dimensions
x, y = positions_on_bench
width, height = dimensions

bench_area = bench.dimensions.inject(:*)
sum = bench.request_distributions.sum(&:area)
sum += area if new_record? # area isn't included in previous operation if record isn't persisted
return if sum <= bench_area
return unless x + width > bench_dimensions[0] || y + height > bench_dimensions[1]

errors.add(:bench, 'sum of distributions areas can\'t be greater than bench area')
errors.add(:positions_on_bench, 'distribution exceeds the bounds of the bench')
end
end

# == Schema Information
#
# Table name: request_distributions
#
# id :bigint not null, primary key
# request_id :bigint
# bench_id :bigint
# plant_stage_id :bigint
# pot_id :bigint
# pot_quantity :integer
# area :decimal(, )
# created_at :datetime not null
# updated_at :datetime not null
# id :bigint not null, primary key
# request_id :bigint
# bench_id :bigint
# plant_stage_id :bigint
# pot_id :bigint
# pot_quantity :integer
# created_at :datetime not null
# updated_at :datetime not null
# positions_on_bench :integer is an Array
# dimensions :integer is an Array
#
# Indexes
#
Expand Down
12 changes: 6 additions & 6 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ en:
at_least_one: should have at least 1 plant_stage
bench:
attributes:
dimensions:
incorrect_size: "should contain exactly two elements: length and width"
not_positive: "each dimension must be greater than 0"
positions:
incorrect_size: "should contain exactly two elements: x and y"
not_positive: "each position must be positive"
request_distributions:
invalid_distribution: "sum of distributions areas can't be greater than bench area"
positions_overlap: "bench overlaps with an existing bench"
positions_overlap: "bench overlaps with an existing bench"
concern:
attributes:
dimensions:
incorrect_size: "should contain exactly two elements: length and width"
not_positive: "each dimension must be greater than 0"
10 changes: 5 additions & 5 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ fr:
at_least_one: Au moins 1 request_distribution est requise
bench:
attributes:
dimensions:
incorrect_size: "doit contenir exactement deux éléments: longueur et largeur"
not_positive: "chaque dimension doit être supérieure à 0"
positions:
incorrect_size: "doit contenir exactement deux éléments : x et y"
not_positive: "chaque position doit être positive"
request_distributions:
invalid_distribution: "la somme des aires de distribution ne peut pas être supérieure à l'aire du banc"
positions_overlap: "le banc chevauche un banc existant"
concern:
attributes:
dimensions:
incorrect_size: "doit contenir exactement deux éléments: longueur et largeur"
not_positive: "chaque dimension doit être supérieure à 0"
date:
abbr_day_names:
- dim
Expand Down
5 changes: 4 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,11 @@
post :refuse, on: :member
post :cancel, on: :member
post :complete, on: :member
resources :request_distributions, only: %i[index show create update destroy]
resources :request_distributions, only: %i[show create update destroy]
end

resources :request_distributions, only: :index

resources :buildings, only: %i[index show create update destroy], shallow: true do
resources :greenhouses, only: %i[index show create update destroy], shallow: true do
resources :benches, only: %i[index show create update destroy]
Expand Down
11 changes: 11 additions & 0 deletions db/migrate/20241209193343_update_request_distributions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class UpdateRequestDistributions < ActiveRecord::Migration[7.2]
def change
change_table :request_distributions, bulk: true do |t|
t.remove :area, type: :decimal
t.integer :positions_on_bench, array: true
t.integer :dimensions, array: true
end
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,10 @@
t.bigint "plant_stage_id"
t.bigint "pot_id"
t.integer "pot_quantity"
t.decimal "area"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "positions_on_bench", array: true
t.integer "dimensions", array: true
t.index ["bench_id"], name: "index_request_distributions_on_bench_id"
t.index ["plant_stage_id"], name: "index_request_distributions_on_plant_stage_id"
t.index ["pot_id"], name: "index_request_distributions_on_pot_id"
Expand Down
Loading

0 comments on commit 4201ad3

Please sign in to comment.