From 75b0e7cd18dbce923e1cd0c65f0f6b4f75cf7f90 Mon Sep 17 00:00:00 2001 From: Kat Date: Mon, 21 Nov 2022 11:19:31 +0000 Subject: [PATCH] Remove nested deactivation periods --- app/helpers/locations_helper.rb | 12 ++- spec/helpers/locations_helper_spec.rb | 113 ++++++++++++++------------ 2 files changed, 71 insertions(+), 54 deletions(-) diff --git a/app/helpers/locations_helper.rb b/app/helpers/locations_helper.rb index 530058482..0843792a2 100644 --- a/app/helpers/locations_helper.rb +++ b/app/helpers/locations_helper.rb @@ -46,7 +46,7 @@ module LocationsHelper def active_periods(location) periods = [ActivePeriod.new(location.available_from, nil)] - sorted_deactivation_periods = location.location_deactivation_periods.sort_by(&:deactivation_date) + sorted_deactivation_periods = remove_nested_periods(location.location_deactivation_periods.sort_by(&:deactivation_date)) sorted_deactivation_periods.each do |deactivation| periods.find { |period| period.to.nil? }.to = deactivation.deactivation_date periods << ActivePeriod.new(deactivation.reactivation_date, nil) @@ -71,4 +71,14 @@ private def remove_overlapping_and_empty_periods(periods) periods.select { |period| ((period.to.nil? && period.from.present?) || (period.from.present? && period.from < period.to)) } end + + def remove_nested_periods(periods) + periods.select { |inner_period| periods.none? { |outer_period| is_nested?(inner_period, outer_period) } } + end + + def is_nested?(inner, outer) + return false if inner == outer || [inner.deactivation_date, inner.reactivation_date, outer.deactivation_date, outer.reactivation_date].any?(&:blank?) + + [inner.deactivation_date, inner.reactivation_date].all? { |date| date.between?(outer.deactivation_date, outer.reactivation_date) } + end end diff --git a/spec/helpers/locations_helper_spec.rb b/spec/helpers/locations_helper_spec.rb index d2540481e..32306dad0 100644 --- a/spec/helpers/locations_helper_spec.rb +++ b/spec/helpers/locations_helper_spec.rb @@ -58,69 +58,76 @@ RSpec.describe LocationsHelper do Timecop.unfreeze end - context "when there have not been any previous deactivations" do - it "returns one active period without to date" do - expect(active_periods(location).count).to eq(1) - expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: nil) - end + it "returns one active period without to date" do + expect(active_periods(location).count).to eq(1) + expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: nil) + end - it "ignores reactivations that were deactivated on the same day" do - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 4)) - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4)) - location.save! + it "ignores reactivations that were deactivated on the same day" do + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 4)) + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 6, 4)) + location.save! - expect(active_periods(location).count).to eq(1) - expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) - end + expect(active_periods(location).count).to eq(1) + expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) + end - it "returns sequential non reactivated active periods" do - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 4)) - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 7, 6)) - location.save! + it "returns sequential non reactivated active periods" do + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 4)) + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 7, 6)) + location.save! - expect(active_periods(location).count).to eq(2) - expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) - expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 6, 4), to: Time.zone.local(2022, 7, 6)) - end + expect(active_periods(location).count).to eq(2) + expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) + expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 6, 4), to: Time.zone.local(2022, 7, 6)) + end - it "returns sequential reactivated active periods" do - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 4)) - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 7, 6), reactivation_date: Time.zone.local(2022, 8, 5)) - location.save! - expect(active_periods(location).count).to eq(3) - expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) - expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 6, 4), to: Time.zone.local(2022, 7, 6)) - expect(active_periods(location).third).to have_attributes(from: Time.zone.local(2022, 8, 5), to: nil) - end + it "returns sequential reactivated active periods" do + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 4)) + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 7, 6), reactivation_date: Time.zone.local(2022, 8, 5)) + location.save! + expect(active_periods(location).count).to eq(3) + expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) + expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 6, 4), to: Time.zone.local(2022, 7, 6)) + expect(active_periods(location).third).to have_attributes(from: Time.zone.local(2022, 8, 5), to: nil) + end - it "returns non sequential non reactivated active periods" do - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 7, 6), reactivation_date: Time.zone.local(2022, 8, 5)) - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: nil) - location.save! + it "returns non sequential non reactivated active periods" do + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 7, 6), reactivation_date: Time.zone.local(2022, 8, 5)) + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: nil) + location.save! - expect(active_periods(location).count).to eq(2) - expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) - expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 8, 5), to: nil) - end + expect(active_periods(location).count).to eq(2) + expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) + expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 8, 5), to: nil) + end - it "returns non sequential reactivated active periods" do - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 7, 6), reactivation_date: Time.zone.local(2022, 8, 5)) - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 4)) - location.save! - expect(active_periods(location).count).to eq(3) - expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) - expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 6, 4), to: Time.zone.local(2022, 7, 6)) - expect(active_periods(location).third).to have_attributes(from: Time.zone.local(2022, 8, 5), to: nil) - end + it "returns non sequential reactivated active periods" do + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 7, 6), reactivation_date: Time.zone.local(2022, 8, 5)) + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 4)) + location.save! + expect(active_periods(location).count).to eq(3) + expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 5, 5)) + expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 6, 4), to: Time.zone.local(2022, 7, 6)) + expect(active_periods(location).third).to have_attributes(from: Time.zone.local(2022, 8, 5), to: nil) + end - it "returns correct active periods when reactivation happends during a deactivated period" do - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 11, 11)) - location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 4, 6), reactivation_date: Time.zone.local(2022, 7, 7)) + it "returns correct active periods when reactivation happends during a deactivated period" do + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 11, 11)) + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 4, 6), reactivation_date: Time.zone.local(2022, 7, 7)) - expect(active_periods(location).count).to eq(2) - expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 4, 6)) - expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 11, 11), to: nil) - end + expect(active_periods(location).count).to eq(2) + expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 4, 6)) + expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 11, 11), to: nil) + end + + it "returns correct active periods when a full deactivation period happens during another deactivation period" do + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 5, 5), reactivation_date: Time.zone.local(2022, 6, 11)) + location.location_deactivation_periods << FactoryBot.create(:location_deactivation_period, deactivation_date: Time.zone.local(2022, 4, 6), reactivation_date: Time.zone.local(2022, 7, 7)) + + expect(active_periods(location).count).to eq(2) + expect(active_periods(location).first).to have_attributes(from: Time.zone.local(2022, 4, 1), to: Time.zone.local(2022, 4, 6)) + expect(active_periods(location).second).to have_attributes(from: Time.zone.local(2022, 7, 7), to: nil) end end