diff --git a/app/assets/stylesheets/custom_bootstrap/variables.sass b/app/assets/stylesheets/custom_bootstrap/variables.sass index f94f04106..f2fc82dda 100644 --- a/app/assets/stylesheets/custom_bootstrap/variables.sass +++ b/app/assets/stylesheets/custom_bootstrap/variables.sass @@ -1,7 +1,7 @@ // Use this file to override Twitter Bootstrap variables or define own variables. // Import original variables so they can be used in overrides -//@import 'bootstrap/variables.scss' +@import 'bootstrap/variables.scss' // Base colours @@ -10,7 +10,7 @@ $brown: #413f3b $green: #5f8e43 $blue: #2f4365 -$red: #8e4d43 +$red: #ff4d43 $orange: #ffa500 $yellow: #b2935c $white: #ffffff diff --git a/app/assets/stylesheets/overrides.sass b/app/assets/stylesheets/overrides.sass index c7cee0cc4..b255536ea 100644 --- a/app/assets/stylesheets/overrides.sass +++ b/app/assets/stylesheets/overrides.sass @@ -112,7 +112,32 @@ p.stats padding-left: 1em width: 15em +.progress + border-radius: 0 + +.badge-super-late + background-color: $red +.badge-harvest + background-color: $blue + +.planting-super-late +.planting-late + background-color: $beige + .planting + .planting-badges + position: absolute + + .planting-thumbnail + padding: 0 + border: 1px solid darken($beige, 10%) + border-radius: 4px + .planting-actions + top: -8em + .planting-name + position: relative + top: -1em + dl.planting-attributes dt text-align: left @@ -175,6 +200,7 @@ p.stats text-align: center margin-bottom: 1.5em max-width: 160px + max-height: 200px .member-thumbnail text-align: left diff --git a/app/controllers/harvests_controller.rb b/app/controllers/harvests_controller.rb index 2e7fbef3e..73aaafd86 100644 --- a/app/controllers/harvests_controller.rb +++ b/app/controllers/harvests_controller.rb @@ -92,7 +92,7 @@ class HarvestsController < ApplicationController # if this harvest is not linked to a planting, then do nothing return if @harvest.planting.nil? - @harvest.planting.update_harvest_days + @harvest.planting.update_harvest_days! @harvest.crop.update_harvest_medians end end diff --git a/app/controllers/plantings_controller.rb b/app/controllers/plantings_controller.rb index f6da9d076..5ee31ea2c 100644 --- a/app/controllers/plantings_controller.rb +++ b/app/controllers/plantings_controller.rb @@ -77,7 +77,7 @@ class PlantingsController < ApplicationController end def update_planting_medians - @planting.update_harvest_days + @planting.update_harvest_days! end def planting_params diff --git a/app/helpers/plantings_helper.rb b/app/helpers/plantings_helper.rb index f71dc8158..4ea42f43e 100644 --- a/app/helpers/plantings_helper.rb +++ b/app/helpers/plantings_helper.rb @@ -32,4 +32,24 @@ module PlantingsHelper def plantings_active_tickbox_path(owner, show_all) show_inactive_tickbox_path('plantings', owner, show_all) end + + def days_from_now_to_finished(planting) + return unless planting.finish_is_predicatable? + (planting.finish_predicted_at - Time.zone.today).to_i + end + + def days_from_now_to_first_harvest(planting) + return unless planting.planted_at.present? && planting.first_harvest_predicted_at.present? + (planting.first_harvest_predicted_at - Time.zone.today).to_i + end + + def planting_classes(planting) + classes = [] + classes << 'planting-growing' if planting.growing? + classes << 'planting-finished' if planting.finished? + classes << 'planting-harvest-time' if planting.harvest_time? + classes << 'planting-late' if planting.late? + classes << 'planting-super-late' if planting.super_late? + classes.join(' ') + end end diff --git a/app/models/concerns/predict_harvest.rb b/app/models/concerns/predict_harvest.rb new file mode 100644 index 000000000..d5422ee35 --- /dev/null +++ b/app/models/concerns/predict_harvest.rb @@ -0,0 +1,61 @@ +module PredictHarvest + extend ActiveSupport::Concern + + included do # rubocop:disable Metrics/BlockLength + # dates + def first_harvest_date + harvests_with_dates.minimum(:harvested_at) + end + + def last_harvest_date + harvests_with_dates.maximum(:harvested_at) + end + + def first_harvest_predicted_at + return unless crop.median_days_to_first_harvest.present? && planted_at.present? + planted_at + crop.median_days_to_first_harvest.days + end + + def last_harvest_predicted_at + return unless crop.median_days_to_last_harvest.present? && planted_at.present? + planted_at + crop.median_days_to_last_harvest.days + end + + # actions + def update_harvest_days! + days_to_first_harvest = nil + days_to_last_harvest = nil + if planted_at.present? && harvests_with_dates.size.positive? + days_to_first_harvest = (first_harvest_date - planted_at).to_i + days_to_last_harvest = (last_harvest_date - planted_at).to_i if finished? + end + update(days_to_first_harvest: days_to_first_harvest, days_to_last_harvest: days_to_last_harvest) + end + + # status + def harvest_time? + return false if crop.perennial || finished + + # We have harvests but haven't finished + harvests.size.positive? || + + # or, we don't have harvests, but we predict we should by now + (first_harvest_predicted_at.present? && + harvests.empty? && + first_harvest_predicted_at < Time.zone.today) + end + + def before_harvest_time? + first_harvest_predicted_at.present? && + harvests.empty? && + first_harvest_predicted_at.present? && + first_harvest_predicted_at > Time.zone.today + end + + private + + def harvests_with_dates + harvests.where.not(harvested_at: nil) + end + end +end diff --git a/app/models/concerns/predict_planting.rb b/app/models/concerns/predict_planting.rb new file mode 100644 index 000000000..ff66168d8 --- /dev/null +++ b/app/models/concerns/predict_planting.rb @@ -0,0 +1,80 @@ +module PredictPlanting + extend ActiveSupport::Concern + + included do # rubocop:disable Metrics/BlockLength + ## Triggers + before_save :calculate_lifespan + + def calculate_lifespan + self.lifespan = (planted_at.present? && finished_at.present? ? finished_at - planted_at : nil) + end + + # dates + def finish_predicted_at + if planted_at.blank? + nil + elsif crop.median_lifespan.present? + planted_at + crop.median_lifespan.days + elsif crop.parent.present? && crop.parent.median_lifespan.present? + planted_at + crop.parent.median_lifespan.days + end + end + + # days + def expected_lifespan + if actual_lifespan.present? + actual_lifespan + elsif crop.median_lifespan.present? + crop.median_lifespan + elsif crop.parent.present? && crop.parent.median_lifespan.present? + crop.parent.median_lifespan + end + end + + def actual_lifespan + return unless planted_at.present? && finished_at.present? + (finished_at - planted_at).to_i + end + + def days_since_planted + (Time.zone.today - planted_at).to_i if planted_at.present? + end + + # progress + def percentage_grown + if finished? + 100 + elsif !finish_is_predicatable? + nil + elsif growing? + calculate_percentage_grown + elsif planted? + 0 + end + end + + # states + def finish_is_predicatable? + crop.annual? && planted_at.present? && finish_predicted_at.present? + end + + # Planting has live more then 90 days past predicted finish + def super_late? + late? && (finish_predicted_at + 90.days) < Time.zone.today + end + + def late? + crop.annual? && !finished && + planted_at.present? && + finish_predicted_at.present? && + finish_predicted_at <= Time.zone.today + end + + private + + def calculate_percentage_grown + percent = (days_since_planted / expected_lifespan.to_f) * 100 + (percent > 100 ? 100 : percent) + end + end +end diff --git a/app/models/crop.rb b/app/models/crop.rb index bead8e89b..b1d492353 100644 --- a/app/models/crop.rb +++ b/app/models/crop.rb @@ -185,7 +185,7 @@ class Crop < ActiveRecord::Base end def update_medians - plantings.each(&:update_harvest_days) + plantings.each(&:update_harvest_days!) update_lifespan_medians update_harvest_medians end diff --git a/app/models/planting.rb b/app/models/planting.rb index 3dff4c006..bea6b9a8b 100644 --- a/app/models/planting.rb +++ b/app/models/planting.rb @@ -3,6 +3,8 @@ class Planting < ActiveRecord::Base include PhotoCapable include Finishable include Ownable + include PredictPlanting + include PredictHarvest friendly_id :planting_slug, use: %i(slugged finders) # Constants @@ -13,10 +15,6 @@ class Planting < ActiveRecord::Base 'graft', 'layering' ].freeze - ## - ## Triggers - before_save :calculate_lifespan - belongs_to :garden belongs_to :crop, counter_cache: true has_many :harvests, dependent: :destroy @@ -59,6 +57,10 @@ class Planting < ActiveRecord::Base in: PLANTED_FROM_VALUES, message: "%s is not a valid planting method" } + def age_in_days + (Time.zone.today - planted_at).to_i if planted_at.present? + end + def planting_slug [ owner.login_name, @@ -81,61 +83,20 @@ class Planting < ActiveRecord::Base photos.order(created_at: :desc).first end + def finished? + finished || (finished_at.present? && finished_at <= Time.zone.today) + end + def planted? - planted_at.present? && planted_at <= Date.current + planted_at.present? && planted_at <= Time.zone.today end - def finish_predicted_at - planted_at + crop.median_lifespan.days if crop.median_lifespan.present? && planted_at.present? - end - - def calculate_lifespan - self.lifespan = (planted_at.present? && finished_at.present? ? finished_at - planted_at : nil) - end - - def expected_lifespan - if planted_at.present? && finished_at.present? - return (finished_at - planted_at).to_i - end - crop.median_lifespan - end - - def days_since_planted - (Time.zone.today - planted_at).to_i if planted_at.present? - end - - def percentage_grown - return 100 if finished - return if planted_at.blank? || expected_lifespan.blank? - p = (days_since_planted / expected_lifespan.to_f) * 100 - return p if p <= 100 - 100 - end - - def update_harvest_days - days_to_first_harvest = nil - days_to_last_harvest = nil - if planted_at.present? && harvests_with_dates.size.positive? - days_to_first_harvest = (first_harvest_date - planted_at).to_i - days_to_last_harvest = (last_harvest_date - planted_at).to_i if finished? - end - update(days_to_first_harvest: days_to_first_harvest, days_to_last_harvest: days_to_last_harvest) - end - - def first_harvest_date - harvests_with_dates.minimum(:harvested_at) - end - - def last_harvest_date - harvests_with_dates.maximum(:harvested_at) + def growing? + planted? && !finished? end private - def harvests_with_dates - harvests.where.not(harvested_at: nil) - end - # check that any finished_at date occurs after planted_at def finished_must_be_after_planted return unless planted_at && finished_at # only check if we have both diff --git a/app/views/gardens/_overview.html.haml b/app/views/gardens/_overview.html.haml index b8f9ce54a..ed12d0cf5 100644 --- a/app/views/gardens/_overview.html.haml +++ b/app/views/gardens/_overview.html.haml @@ -13,12 +13,9 @@ .col-md-10 .row - if garden.plantings.current.size.positive? - - garden.plantings.current.includes(:crop).each do |planting| - .col-md-2.col-sm-6.col-xs-6 - .hover-wrapper - .text - = render 'plantings/actions', planting: planting - = render partial: "plantings/thumbnail", locals: { planting: planting } + - garden.plantings.current.order(created_at: :desc).includes(:crop, :photos).each do |planting| + .col-md-2.col-sm-4.col-xs-6 + = render "plantings/thumbnail", planting: planting - else .col-md-2.col-sm-6.col-xs-6 no plantings - if can?(:edit, garden) diff --git a/app/views/plantings/_actions.html.haml b/app/views/plantings/_actions.html.haml index 87eddc8cf..6f5b1b93d 100644 --- a/app/views/plantings/_actions.html.haml +++ b/app/views/plantings/_actions.html.haml @@ -8,5 +8,5 @@ = render 'shared/buttons/harvest_planting', planting: planting = render 'shared/buttons/save_seeds', planting: planting - - if can? :destroy, planting - = render 'shared/buttons/delete', path: planting + - if can? :destroy, planting + = render 'shared/buttons/delete', path: planting diff --git a/app/views/plantings/_badges.html.haml b/app/views/plantings/_badges.html.haml new file mode 100644 index 000000000..2d690b914 --- /dev/null +++ b/app/views/plantings/_badges.html.haml @@ -0,0 +1,20 @@ +// Finish times +- if planting.finish_is_predicatable? + - if planting.super_late? + %span.badge.badge-super-late= t('.super_late') + = render 'shared/buttons/finish_planting', planting: planting + - elsif planting.late? + %span.badge.badge-late= t('.late_finishing') + - else + %span.badge + = days_from_now_to_finished(planting) + = t('.days_until_finished') + +// Harvest times +- unless planting.super_late? + - if planting.harvest_time? + %span.badge.badge-harvest= t('.harvesting_now') + - elsif planting.before_harvest_time? + %span.badge + = days_from_now_to_first_harvest(planting) + = t('.days_until_harvest') diff --git a/app/views/plantings/_progress.html.haml b/app/views/plantings/_progress.html.haml index a594de75d..5d94de1c3 100644 --- a/app/views/plantings/_progress.html.haml +++ b/app/views/plantings/_progress.html.haml @@ -1,8 +1,8 @@ - if planting.crop.perennial - %p Perennial + = render "plantings/progress_bar", status: "perennial", progress: nil - elsif !planting.planted? - if show_explanation - %p Progress: 0% - not planted yet + %p= t('.progress_0_not_planted_yet') = render "plantings/progress_bar", status: "not planted", progress: 0 - elsif planting.finished? = render "plantings/progress_bar", status: 'finished', progress: 100 diff --git a/app/views/plantings/_thumbnail.html.haml b/app/views/plantings/_thumbnail.html.haml index e34c653da..a6b8349b8 100644 --- a/app/views/plantings/_thumbnail.html.haml +++ b/app/views/plantings/_thumbnail.html.haml @@ -1,10 +1,13 @@ -.thumbnail - .planting-thumbnail - - if planting - = link_to image_tag(planting_image_path(planting), - alt: planting.crop.name, class: 'img'), - planting - .plantinginfo +.planting + .planting-badges + = render 'plantings/badges', planting: planting + .hover-wrapper + .thumbnail + .planting-thumbnail{ class: planting_classes(planting) } + = link_to image_tag(planting_image_path(planting), + alt: planting.crop.name, class: 'img'), planting_path(planting) + = render 'plantings/progress', planting: planting, show_explanation: false .planting-name - = render 'plantings/progress', planting: planting, show_explanation: false = link_to planting.crop.name, planting + .text + .planting-actions= render 'plantings/actions', planting: planting diff --git a/app/views/shared/buttons/_finish_planting.html.haml b/app/views/shared/buttons/_finish_planting.html.haml index 9a3b6ce75..7b1ba8319 100644 --- a/app/views/shared/buttons/_finish_planting.html.haml +++ b/app/views/shared/buttons/_finish_planting.html.haml @@ -1,4 +1,4 @@ -- unless planting.finished +- if can?(:edit, planting) && !planting.finished = link_to planting_path(planting, planting: { finished: 1 }), method: :put, class: 'btn btn-default btn-xs append-date' do %span.glyphicon.glyphicon-ok{ title: "Finished" } diff --git a/config/locales/en.yml b/config/locales/en.yml index b78d869ea..c494975e7 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -52,6 +52,11 @@ en: location_helper: If you have a location set in your profile, it will be used when you create a new garden. location: "%{owner}'s %{garden}" updated: Garden was successfully updated. + overview: + gardensphoto: gardens/photo + plantingsthumbnail: plantings/thumbnail + no_plantings: no plantings + gardensactions: gardens/actions harvests: created: Harvest was successfully created. index: @@ -191,6 +196,15 @@ en: view_owners_profile: View %{owner}'s profile >> the_data_on_this_page_is_available_in_the_following_formats: 'The data on this page is available in the following formats:' string: "%{crop} planting in %{garden} by %{owner}" + badges: + late_finishing: late finishing + super_late: super late + sharedbuttonsfinish_planting: shared/buttons/finish_planting + days_until_finished: days until finished + harvesting_now: harvesting now + days_until_harvest: days until harvest + progress: + progress_0_not_planted_yet: 'Progress: 0% - not planted yet' posts: index: title: diff --git a/db/migrate/20171105011017_set_prediction_data.rb b/db/migrate/20171105011017_set_prediction_data.rb index 149001927..5f5590d37 100644 --- a/db/migrate/20171105011017_set_prediction_data.rb +++ b/db/migrate/20171105011017_set_prediction_data.rb @@ -1,7 +1,7 @@ class SetPredictionData < ActiveRecord::Migration def up say "Updating all plantings time to first harvest" - Planting.all.each(&:update_harvest_days) + Planting.all.each(&:update_harvest_days!) say "Updating crop median time to first harvest, and lifespan" Crop.all.each do |crop| crop.update_lifespan_medians diff --git a/spec/factories/garden.rb b/spec/factories/garden.rb index 1f3d7461d..f93107361 100644 --- a/spec/factories/garden.rb +++ b/spec/factories/garden.rb @@ -1,6 +1,6 @@ FactoryBot.define do factory :garden do - name 'Springfield Community Garden' + name { Faker::Vehicle.vin } description "This is a **totally** cool garden" owner active true diff --git a/spec/factories/planting.rb b/spec/factories/planting.rb index 5b7311025..27a2b179d 100644 --- a/spec/factories/planting.rb +++ b/spec/factories/planting.rb @@ -6,6 +6,8 @@ FactoryBot.define do planted_at { Time.zone.local(2014, 7, 30) } quantity 33 description "This is a *really* good plant." + finished false + finished_at nil factory :seed_planting do planted_from 'seed' diff --git a/spec/features/gardens/gardens_index_spec.rb b/spec/features/gardens/gardens_index_spec.rb index 39161d5c8..0a973e305 100644 --- a/spec/features/gardens/gardens_index_spec.rb +++ b/spec/features/gardens/gardens_index_spec.rb @@ -3,14 +3,13 @@ require 'custom_matchers' feature "Gardens#index", :js do context "Logged in as member" do - let(:member) { FactoryBot.create :member } - + let(:member) { FactoryBot.create :member, login_name: 'shadow' } background { login_as member } context "with 10 gardens" do before do FactoryBot.create_list :garden, 10, owner: member - visit gardens_path(member: member) + visit gardens_path(owner: member.login_name) end it "displays each of the gardens" do @@ -67,4 +66,70 @@ feature "Gardens#index", :js do end end end + + describe 'badges' do + let(:garden) { member.gardens.first } + let(:member) { FactoryBot.create :member, login_name: 'robbieburns' } + let(:crop) { FactoryBot.create :crop } + before(:each) do + # time to harvest = 50 day + # time to finished = 90 days + FactoryBot.create(:harvest, + harvested_at: 50.days.ago, + crop: crop, + planting: FactoryBot.create(:planting, + crop: crop, + planted_at: 100.days.ago, + finished_at: 10.days.ago)) + crop.plantings.each(&:update_harvest_days!) + crop.update_lifespan_medians + crop.update_harvest_medians + + garden.update! name: 'super awesome garden' + assert planting + visit gardens_path(owner: member.login_name) + end + + describe 'harvest still growing' do + let!(:planting) do + FactoryBot.create :planting, + crop: crop, + owner: member, + garden: garden, + planted_at: Time.zone.today + end + it { expect(page).to have_link href: planting_path(planting) } + it { expect(page).to have_link href: garden_path(planting.garden) } + it { expect(page).to have_text '50 days until harvest' } + it { expect(page).to have_text '90 days until finished' } + it { expect(page).not_to have_text 'harvesting now' } + end + + describe 'harvesting now' do + let!(:planting) do + FactoryBot.create :planting, + crop: crop, + owner: member, garden: garden, + planted_at: 51.days.ago + end + it { expect(crop.median_days_to_first_harvest).to eq 50 } + it { expect(crop.median_lifespan).to eq 90 } + + it { expect(page).to have_text 'harvesting now' } + it { expect(page).to have_text '39 days until finished' } + it { expect(page).not_to have_text 'days until harvest' } + end + + describe 'super late' do + let!(:planting) do + FactoryBot.create :planting, + crop: crop, owner: member, + garden: garden, planted_at: 260.days.ago + end + it { expect(page).to have_text 'super late' } + it { expect(page).not_to have_text 'harvesting now' } + it { expect(page).not_to have_text 'days until harvest' } + it { expect(page).not_to have_text 'days until finished' } + end + end end diff --git a/spec/features/harvests/harvesting_a_crop_spec.rb b/spec/features/harvests/harvesting_a_crop_spec.rb index e99c5e814..5f6d85fa2 100644 --- a/spec/features/harvests/harvesting_a_crop_spec.rb +++ b/spec/features/harvests/harvesting_a_crop_spec.rb @@ -130,7 +130,7 @@ feature "Harvesting a crop", :js, :elasticsearch do end scenario "linking to a planting" do - expect(page).to have_content planting.to_s + expect(page).to have_content existing_planting.to_s choose("harvest_planting_id_#{existing_planting.id}") click_button "save" expect(page).to have_link(href: planting_path(existing_planting)) diff --git a/spec/models/garden_spec.rb b/spec/models/garden_spec.rb index f8e5a3311..839ef3445 100644 --- a/spec/models/garden_spec.rb +++ b/spec/models/garden_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' describe Garden do let(:owner) { FactoryBot.create(:member) } - let(:garden) { FactoryBot.create(:garden, owner: owner) } + let(:garden) { FactoryBot.create(:garden, owner: owner, name: 'Springfield Community Garden') } it "should have a slug" do garden.slug.should match(/member\d+-springfield-community-garden/) diff --git a/spec/models/planting_spec.rb b/spec/models/planting_spec.rb index 6948caa75..6b49f1c0a 100644 --- a/spec/models/planting_spec.rb +++ b/spec/models/planting_spec.rb @@ -3,7 +3,7 @@ require 'rails_helper' describe Planting do let(:crop) { FactoryBot.create(:tomato) } let(:garden_owner) { FactoryBot.create(:member) } - let(:garden) { FactoryBot.create(:garden, owner: garden_owner) } + let(:garden) { FactoryBot.create(:garden, owner: garden_owner, name: 'Springfield Community Garden') } let(:planting) { FactoryBot.create(:planting, crop: crop, garden: garden, owner: garden.owner) } let(:finished_planting) do FactoryBot.create :planting, planted_at: 4.days.ago, finished_at: 2.days.ago, finished: true @@ -60,9 +60,26 @@ describe Planting do describe 'planting 30 days ago, not finished' do let(:planting) { FactoryBot.create :planting, planted_at: 30.days.ago } - # 30 / 50 + # 30 / 50 = 60% it { expect(planting.percentage_grown).to eq 60.0 } + # planted 30 days ago it { expect(planting.days_since_planted).to eq 30 } + # means 20 days to go + it { expect(planting.finish_predicted_at).to eq Time.zone.today + 20.days } + end + + describe 'child crop uses parent data' do + let(:child_crop) { FactoryBot.create :crop, parent: crop, name: 'child' } + let(:child_planting) { FactoryBot.create :planting, crop: child_crop, planted_at: 30.days.ago } + + # not data for this crop + it { expect(child_crop.median_lifespan).to eq nil } + # 30 / 50 = 60% + it { expect(child_planting.percentage_grown).to eq 60.0 } + # planted 30 days ago + it { expect(child_planting.days_since_planted).to eq 30 } + # means 20 days to go + it { expect(child_planting.finish_predicted_at).to eq Time.zone.today + 20.days } end describe 'planting not planted yet' do @@ -73,8 +90,7 @@ describe Planting do describe 'planting finished 10 days, but was never planted' do let(:planting) { FactoryBot.create :planting, planted_at: nil, finished_at: 10.days.ago } - - it { expect(planting.percentage_grown).to eq nil } + it { expect(planting.percentage_grown).to eq 100 } end describe 'planted 30 days ago, finished 10 days ago' do @@ -97,8 +113,10 @@ describe Planting do it { expect(planting.expected_lifespan).to eq(nil) } end context 'lots of data' do + let(:crop) { FactoryBot.create :crop } + # this is a method so it creates a new one each time def one_hundred_day_old_planting - FactoryBot.create(:planting, crop: planting.crop, planted_at: 100.days.ago) + FactoryBot.create(:planting, crop: crop, planted_at: 100.days.ago) end before do # 50 days to harvest @@ -110,19 +128,35 @@ describe Planting do # 10 days to harvest FactoryBot.create(:harvest, harvested_at: 90.days.ago, crop: planting.crop, planting: one_hundred_day_old_planting) - planting.crop.plantings.each(&:update_harvest_days) + + planting.crop.plantings.each(&:update_harvest_days!) planting.crop.update_lifespan_medians planting.crop.update_harvest_medians end - it { expect(planting.crop.median_days_to_first_harvest).to eq(20) } + it { expect(crop.median_days_to_first_harvest).to eq(20) } + describe 'sets median time to harvest' do + let(:planting) { FactoryBot.create :planting, crop: crop, planted_at: Time.zone.today } + it { expect(planting.first_harvest_predicted_at).to eq(Time.zone.today + 20.days) } + end + + describe 'harvest still growing' do + let(:planting) { FactoryBot.create :planting, crop: crop, planted_at: Time.zone.today } + it { expect(planting.before_harvest_time?).to eq true } + it { expect(planting.harvest_time?).to eq false } + end + describe 'harvesting ready now' do + let(:planting) { FactoryBot.create :planting, crop: crop, planted_at: 21.days.ago } + it { expect(planting.first_harvest_predicted_at).to eq(1.day.ago.to_date) } + it { expect(planting.before_harvest_time?).to eq false } + it { expect(planting.harvest_time?).to eq true } + end end describe 'planting has no harvests' do + let(:planting) { FactoryBot.create :planting } before do - planting.update_harvest_days + planting.update_harvest_days! planting.crop.update_harvest_medians end - let(:planting) { FactoryBot.create :planting } - it { expect(planting.days_to_first_harvest).to eq(nil) } it { expect(planting.days_to_last_harvest).to eq(nil) } end @@ -134,7 +168,7 @@ describe Planting do planting: planting, crop: planting.crop, harvested_at: 10.days.ago) - planting.update_harvest_days + planting.update_harvest_days! planting.crop.update_harvest_medians end it { expect(planting.days_to_first_harvest).to eq(90) } @@ -148,7 +182,7 @@ describe Planting do before do FactoryBot.create :harvest, planting: planting, crop: planting.crop, harvested_at: 90.days.ago FactoryBot.create :harvest, planting: planting, crop: planting.crop, harvested_at: 10.days.ago - planting.update_harvest_days + planting.update_harvest_days! planting.crop.update_harvest_medians end it { expect(planting.days_to_first_harvest).to eq(10) }