From 10ef59ccbda69187bcaffc5cf944217199e43a47 Mon Sep 17 00:00:00 2001 From: Arthur Campbell Date: Mon, 13 May 2024 14:06:54 +0100 Subject: [PATCH] create presenter class to hold all the db calls and data for showing the home page plus tests, some changes to factoreis and helpers necessary this should avoid an overly bloated controller method or calling the DB from a view helper --- app/presenters/homepage_presenter.rb | 110 ++++++++ spec/factories/lettings_log.rb | 12 +- spec/factories/sales_log.rb | 9 + spec/factories/user.rb | 4 +- spec/presenters/homepage_presenter_spec.rb | 309 +++++++++++++++++++++ spec/request_helper.rb | 3 + 6 files changed, 443 insertions(+), 4 deletions(-) create mode 100644 app/presenters/homepage_presenter.rb create mode 100644 spec/presenters/homepage_presenter_spec.rb diff --git a/app/presenters/homepage_presenter.rb b/app/presenters/homepage_presenter.rb new file mode 100644 index 000000000..e05045e23 --- /dev/null +++ b/app/presenters/homepage_presenter.rb @@ -0,0 +1,110 @@ +class HomepagePresenter + include Rails.application.routes.url_helpers + + attr_reader :current_year_in_progress_lettings_data, :current_year_completed_lettings_data, :current_year_in_progress_sales_data, :current_year_completed_sales_data, :last_year_in_progress_lettings_data, :last_year_completed_lettings_data, :last_year_in_progress_sales_data, :last_year_completed_sales_data, :incomplete_schemes_data + + def initialize(user) + @user = user + @organisation_logs_sales = organisation_has_ever_logged_sales? + @in_crossover_period = FormHandler.instance.in_crossover_period? + @current_year = FormHandler.instance.current_lettings_form.start_date.year + @current_year_in_progress_lettings_data = data_box_data(:lettings, @current_year, :in_progress) + @current_year_completed_lettings_data = data_box_data(:lettings, @current_year, :completed) + @current_year_in_progress_sales_data = data_box_data(:sales, @current_year, :in_progress) if organisation_logs_sales? + @current_year_completed_sales_data = data_box_data(:sales, @current_year, :completed) if organisation_logs_sales? + @last_year = @current_year - 1 + @last_year_in_progress_lettings_data = data_box_data(:lettings, @last_year, :in_progress) if in_crossover_period? + @last_year_completed_lettings_data = data_box_data(:lettings, @last_year, :completed) + @last_year_in_progress_sales_data = data_box_data(:sales, @last_year, :in_progress) if in_crossover_period? && organisation_logs_sales? + @last_year_completed_sales_data = data_box_data(:sales, @last_year, :completed) if organisation_logs_sales? + if display_schemes? + @incomplete_schemes_data = { + count: @user.schemes.incomplete.count, + text: data_box_text(type: :schemes, status: :incomplete), + path: schemes_path(status: [:incomplete], owning_organisation_select: "all"), + } + end + end + + def title_text_for_user + if @user.support? + "Manage all data" + elsif @user.data_coordinator? + "Manage your organisation's logs" + else + "Manage logs assigned to you" + end + end + + def organisation_logs_sales? + @organisation_logs_sales + end + + def in_crossover_period? + @in_crossover_period + end + + def subheading_for_current_year + subheading_from_year @current_year + end + + def subheading_for_last_year + subheading = subheading_from_year @last_year + in_crossover_period? ? subheading : "#{subheading} (Closed collection year)" + end + + def display_schemes? + !@user.data_provider? + end + +private + + def subheading_from_year(year) + return "AAAAH" if year.nil? + + "#{year} to #{year + 1} Logs" + end + + def data_box_data(type, year, status) + { + count: logs_count(type:, year:, status:), + text: data_box_text(type:, status:), + path: logs_link(type:, year:, status:), + } + end + + def data_box_text(type:, status:) + text = [status, type] + text.reverse! if status == :in_progress + text.join(" ").humanize + end + + def logs_link(type:, year:, status:) + params = { + status: [status], + years: [year], + assigned_to: @user.data_provider? ? "you" : "all", + owning_organisation_select: "all", + managing_organisation_select: "all", + } + case type + when :lettings then lettings_logs_path(params) + when :sales then sales_logs_path(params) + end + end + + def logs_count(type:, year:, status:) + query = case type + when :lettings then @user.lettings_logs + when :sales then @user.sales_logs + end + query = query.where(created_by: @user) if @user.data_provider? + query.filter_by_year(year) + .where(status:) + .count + end + + def organisation_has_ever_logged_sales? + @user.organisation.sales_logs.exists? + end +end diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index ebf94230e..9136dc0dc 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -87,7 +87,7 @@ FactoryBot.define do rsnvac { 6 } unittype_gn { 7 } beds { 3 } - voiddate { 2.days.ago } + voiddate { startdate - 2.days } offered { 2 } wchair { 1 } earnings { 268 } @@ -154,7 +154,7 @@ FactoryBot.define do hbrentshortfall { 1 } tshortfall { 12 } property_relet { 0 } - mrcdate { 1.day.ago } + mrcdate { startdate - 1.day } incref { 0 } armedforces { 1 } builtype { 1 } @@ -171,6 +171,14 @@ FactoryBot.define do ppcodenk { 1 } tshortfall_known { 1 } end + trait :completed2024 do + completed + address_line1_input { address_line1 } + postcode_full_input { postcode_full } + nationality_all_group { 826 } + uprn { 1 } + uprn_selection { 1 } + end trait :export do tenancycode { "987654" } ppostcode_full { "LE5 1QP" } diff --git a/spec/factories/sales_log.rb b/spec/factories/sales_log.rb index f0cea588f..94347c6f1 100644 --- a/spec/factories/sales_log.rb +++ b/spec/factories/sales_log.rb @@ -154,6 +154,15 @@ FactoryBot.define do buy2living { 3 } proplen_asked { 1 } end + trait :completed2024 do + completed + address_line1_input { address_line1 } + postcode_full_input { postcode_full } + nationality_all_group { 826 } + nationality_all_buyer2_group { 826 } + uprn { 1 } + uprn_selection { 1 } + end trait :with_uprn do uprn { rand(999_999_999_999).to_s } uprn_known { 1 } diff --git a/spec/factories/user.rb b/spec/factories/user.rb index 935bd30f6..f898c5504 100644 --- a/spec/factories/user.rb +++ b/spec/factories/user.rb @@ -1,9 +1,9 @@ FactoryBot.define do factory :user do - sequence(:email) { |i| "test#{i}@example.com" } + sequence(:email) { "test#{SecureRandom.hex}@example.com" } name { "Danny Rojas" } password { "pAssword1" } - organisation + organisation { association :organisation } role { "data_provider" } phone { "1234512345123" } trait :data_coordinator do diff --git a/spec/presenters/homepage_presenter_spec.rb b/spec/presenters/homepage_presenter_spec.rb new file mode 100644 index 000000000..7d1be7e0c --- /dev/null +++ b/spec/presenters/homepage_presenter_spec.rb @@ -0,0 +1,309 @@ +require "rails_helper" + +RSpec.describe HomepagePresenter do + let(:organisation) { create(:organisation) } + let(:user) { create(:user, organisation:) } + let(:in_crossover_period) { true } + let(:presenter) { described_class.new(user) } + let(:date_this_year) { Time.zone.today } + let(:date_last_year) { Time.zone.today - 1.year } + let(:expected_count) { rand 1..10 } + + before do + allow(FormHandler.instance).to receive(:in_crossover_period?).and_return(in_crossover_period) + end + + context "when the user is support" do + let(:user) { create(:user, :support) } + + it "sets the correct title" do + expect(presenter.title_text_for_user).to eq "Manage all data" + end + + it "returns that schemes should be displayed" do + expect(presenter.display_schemes?).to be true + end + end + + context "when the user is a data coordinator" do + let(:user) { create(:user, :data_coordinator) } + + it "sets the correct title" do + expect(presenter.title_text_for_user).to eq "Manage your organisation's logs" + end + + it "returns that schemes should be displayed" do + expect(presenter.display_schemes?).to be true + end + end + + context "when the user is a data provider" do + let(:user) { create(:user, :data_provider) } + + it "sets the correct title" do + expect(presenter.title_text_for_user).to eq "Manage logs assigned to you" + end + + it "returns that schemes should not be displayed" do + expect(presenter.display_schemes?).to be false + end + end + + context "when the user's organisation has never submitted sales logs" do + it "shows the user's organisation does not log sales" do + expect(presenter.organisation_logs_sales?).to be false + end + + context "when in the crossover period" do + let(:in_crossover_period) { true } + + it "leaves all sales related data as nil" do + sales_data = [ + presenter.current_year_in_progress_sales_data, + presenter.current_year_completed_sales_data, + presenter.last_year_in_progress_sales_data, + presenter.last_year_completed_sales_data, + ] + expect(sales_data).to all be nil + end + end + + context "when not in the crossover period" do + let(:in_crossover_period) { false } + + it "leaves all sales related data as nil" do + sales_data = [ + presenter.current_year_in_progress_sales_data, + presenter.current_year_completed_sales_data, + presenter.last_year_in_progress_sales_data, + presenter.last_year_completed_sales_data, + ] + expect(sales_data).to all be nil + end + end + end + + context "when the user's organisation has submitted sales logs" do + before do + create(:sales_log, created_by: user) + end + + it "shows the user's organisation logs sales" do + expect(presenter.organisation_logs_sales?).to be true + end + + context "when in the crossover period" do + let(:in_crossover_period) { true } + + it "populates all sales related data" do + sales_data = [ + presenter.current_year_in_progress_sales_data, + presenter.current_year_completed_sales_data, + presenter.last_year_in_progress_sales_data, + presenter.last_year_completed_sales_data, + ] + expect(sales_data).to all be_an_instance_of(Hash) + end + end + + context "when not in the crossover period" do + let(:in_crossover_period) { false } + + it "populates all relevant sales related data" do + sales_data = [ + presenter.current_year_in_progress_sales_data, + presenter.current_year_completed_sales_data, + presenter.last_year_completed_sales_data, + ] + expect(sales_data).to all be_an_instance_of(Hash) + end + + it "does not populate data for last year's in progress logs" do + last_year_in_progress_data = [ + presenter.last_year_in_progress_lettings_data, + presenter.last_year_in_progress_sales_data, + ] + expect(last_year_in_progress_data).to all be nil + end + end + end + + context "when in the crossover period" do + let(:in_crossover_period) { true } + + it "returns that we are in the crossover period" do + expect(presenter.in_crossover_period?).to be true + end + end + + context "when not in the crossover period" do + let(:in_crossover_period) { false } + + it "returns that we are in the crossover period" do + expect(presenter.in_crossover_period?).to be false + end + end + + context "when testing the data collected and exposed by the presenter" do + context "with lettings logs" do + let(:type) { :lettings_log } + + context "with in progress status" do + let(:status) { :in_progress } + + context "and the current year" do + let(:startdate) { date_this_year } + + it "exposes the correct data for the data box" do + create_list(type, expected_count, status, created_by: user, startdate:) + data = presenter.current_year_in_progress_lettings_data + + expect(data[:count]).to be expected_count + expect(data[:text]).to eq "Lettings in progress" + uri = URI.parse(data[:path]) + expect(uri.path).to eq "/#{type.to_s.dasherize}s" + query_params = CGI.parse(uri.query) + expect(query_params["status[]"]).to eq [status.to_s] + expect(query_params["years[]"]).to eq [date_this_year.year.to_s] + end + end + + context "and the last year" do + let(:startdate) { date_last_year } + + it "exposes the correct data for the data box" do + create_list(type, expected_count, status, created_by: user, startdate:) + data = presenter.last_year_in_progress_lettings_data + + expect(data[:count]).to be expected_count + expect(data[:text]).to eq "Lettings in progress" + uri = URI.parse(data[:path]) + expect(uri.path).to eq "/#{type.to_s.dasherize}s" + query_params = CGI.parse(uri.query) + expect(query_params["status[]"]).to eq [status.to_s] + expect(query_params["years[]"]).to eq [date_last_year.year.to_s] + end + end + end + + context "with completed status" do + let(:status) { :completed } + + context "and the current year" do + let(:startdate) { date_this_year } + + it "exposes the correct data for the data box" do + create_list(type, expected_count, :completed2024, created_by: user, startdate:) + data = presenter.current_year_completed_lettings_data + + expect(data[:count]).to be expected_count + expect(data[:text]).to eq "Completed lettings" + uri = URI.parse(data[:path]) + expect(uri.path).to eq "/#{type.to_s.dasherize}s" + query_params = CGI.parse(uri.query) + expect(query_params["status[]"]).to eq [status.to_s] + expect(query_params["years[]"]).to eq [date_this_year.year.to_s] + end + end + + context "and the last year" do + let(:startdate) { date_last_year } + + it "exposes the correct data for the data box" do + create_list(type, expected_count, status, created_by: user, startdate:) + data = presenter.last_year_completed_lettings_data + + expect(data[:count]).to be expected_count + expect(data[:text]).to eq "Completed lettings" + uri = URI.parse(data[:path]) + expect(uri.path).to eq "/#{type.to_s.dasherize}s" + query_params = CGI.parse(uri.query) + expect(query_params["status[]"]).to eq [status.to_s] + expect(query_params["years[]"]).to eq [date_last_year.year.to_s] + end + end + end + end + + context "with sales logs" do + let(:type) { :sales_log } + + context "with in progress status" do + let(:status) { :in_progress } + + context "and the current year" do + let(:saledate) { date_this_year } + + it "exposes the correct data for the data box" do + create_list(type, expected_count, status, created_by: user, saledate:) + data = presenter.current_year_in_progress_sales_data + + expect(data[:count]).to be expected_count + expect(data[:text]).to eq "Sales in progress" + uri = URI.parse(data[:path]) + expect(uri.path).to eq "/#{type.to_s.dasherize}s" + query_params = CGI.parse(uri.query) + expect(query_params["status[]"]).to eq [status.to_s] + expect(query_params["years[]"]).to eq [date_this_year.year.to_s] + end + end + + context "and the last year" do + let(:saledate) { date_last_year } + + it "exposes the correct data for the data box" do + create_list(type, expected_count, status, created_by: user, saledate:) + data = presenter.last_year_in_progress_sales_data + + expect(data[:count]).to be expected_count + expect(data[:text]).to eq "Sales in progress" + uri = URI.parse(data[:path]) + expect(uri.path).to eq "/#{type.to_s.dasherize}s" + query_params = CGI.parse(uri.query) + expect(query_params["status[]"]).to eq [status.to_s] + expect(query_params["years[]"]).to eq [date_last_year.year.to_s] + end + end + end + + context "with completed status" do + let(:status) { :completed } + + context "and the current year" do + let(:saledate) { date_this_year } + + it "exposes the correct data for the data box" do + create_list(type, expected_count, :completed2024, created_by: user, saledate:) + data = presenter.current_year_completed_sales_data + + binding.pry + expect(data[:count]).to be expected_count + expect(data[:text]).to eq "Completed sales" + uri = URI.parse(data[:path]) + expect(uri.path).to eq "/#{type.to_s.dasherize}s" + query_params = CGI.parse(uri.query) + expect(query_params["status[]"]).to eq [status.to_s] + expect(query_params["years[]"]).to eq [date_this_year.year.to_s] + end + end + + context "and the last year" do + let(:saledate) { date_last_year } + + it "exposes the correct data for the data box" do + create_list(type, expected_count, status, created_by: user, saledate:) + data = presenter.last_year_completed_sales_data + + expect(data[:count]).to be expected_count + expect(data[:text]).to eq "Completed sales" + uri = URI.parse(data[:path]) + expect(uri.path).to eq "/#{type.to_s.dasherize}s" + query_params = CGI.parse(uri.query) + expect(query_params["status[]"]).to eq [status.to_s] + expect(query_params["years[]"]).to eq [date_last_year.year.to_s] + end + end + end + end + end +end diff --git a/spec/request_helper.rb b/spec/request_helper.rb index 8c5759bec..346bd5f7c 100644 --- a/spec/request_helper.rb +++ b/spec/request_helper.rb @@ -83,6 +83,9 @@ module RequestHelper WebMock.stub_request(:get, "https://api.os.uk/search/places/v1/uprn?key=OS_DATA_KEY&uprn=1234567890123") .to_return(status: 404, body: "", headers: {}) + + WebMock.stub_request(:get, /api\.os\.uk/) + .to_return(status: 200, body: { results: [{ DPA: { MATCH: 0.9, BUILDING_NAME: "result address line 1", POST_TOWN: "result town or city", POSTCODE: "AA1 1AA", UPRN: "1" } }] }.to_json, headers: {}) end def self.real_http_requests