From a76257c4a0033f2611b0473312f04fe52cf6a905 Mon Sep 17 00:00:00 2001 From: fumimowdan Date: Mon, 2 Oct 2023 13:47:13 +0100 Subject: [PATCH] Add Forms funnel and Ineligible forms funnel These widget have the ability to change the date_range of the formsfunnelquery but defaults to the last 24 hours. The date range filter feature is soft launched at the moment and is only usable via the url `/system-admin/dashboard?date_range[unit]=hours&date_range[range_start]=48&date_range[range_end]=24` --- .../system_admin/dashboard_controller.rb | 15 +++- app/models/kpis.rb | 50 +++++++++++- .../system_admin/dashboard/show.html.erb | 25 ++++++ spec/models/kpis_spec.rb | 76 ++++++++++++++----- 4 files changed, 146 insertions(+), 20 deletions(-) diff --git a/app/controllers/system_admin/dashboard_controller.rb b/app/controllers/system_admin/dashboard_controller.rb index 7714bf3b..97aefbb1 100644 --- a/app/controllers/system_admin/dashboard_controller.rb +++ b/app/controllers/system_admin/dashboard_controller.rb @@ -1,7 +1,20 @@ module SystemAdmin class DashboardController < AdminController def show - @kpis = Kpis.new + @kpis = Kpis.new(**kpi_params) + rescue ArgumentError => e + flash[:alert] = e.message + redirect_to(dashboard_path) + end + + private + + def kpi_params + params + .fetch(:date_range, {}) + .permit(:unit, :range_start, :range_end) + .to_hash + .symbolize_keys end end end diff --git a/app/models/kpis.rb b/app/models/kpis.rb index 227a536b..46903e41 100644 --- a/app/models/kpis.rb +++ b/app/models/kpis.rb @@ -1,8 +1,11 @@ class Kpis - def initialize - @applications = Applicant.all + def initialize(unit: "hours", range_start: "24", range_end: "0") + @date_range_params = parse_date_range(unit:, range_start:, range_end:) + @date_range = to_date_range(**@date_range_params) end + attr_reader :date_range, :date_range_params + def total_applications Application.count end @@ -66,4 +69,47 @@ def time_to_payment_confirmation def status_breakdown StatusBreakdownQuery.call end + + def forms_funnel + forms_funnel_query + end + + def ineligible_forms_funnel + normal_steps = %w[personal-details employment-details submission] + + forms_funnel_query.except(*normal_steps) + end + + def funnel_date_range_title + if date_range_params.fetch(:range_end).zero? + ["last", date_range_params.fetch(:range_start), date_range_params.fetch(:unit)].join(" ") + else + ["between", date_range_params.fetch(:range_start), "and", date_range_params.fetch(:range_end), date_range_params.fetch(:unit), "ago"].join(" ") + end + end + +private + + def parse_date_range(unit:, range_start:, range_end:) + raise(ArgumentError, "invalid unit value, must be hours or days") unless %w[hours days].include?(unit) + + range_end = range_end.to_i + range_start = range_start.to_i + + raise(ArgumentError, "range_end and range_start must be positive numbers") if range_start.negative? || range_end.negative? + raise(ArgumentError, "range_end must be lower than range_start") if range_end >= range_start + + { unit:, range_start:, range_end: } + end + + def to_date_range(unit:, range_start:, range_end:) + Range.new( + range_start.public_send(unit).ago, + range_end.public_send(unit).ago, + ) + end + + def forms_funnel_query + @forms_funnel_query ||= FormsFunnelQuery.call(date_range:) + end end diff --git a/app/views/system_admin/dashboard/show.html.erb b/app/views/system_admin/dashboard/show.html.erb index f2f37d87..06035df5 100644 --- a/app/views/system_admin/dashboard/show.html.erb +++ b/app/views/system_admin/dashboard/show.html.erb @@ -153,5 +153,30 @@ +
+

Forms funnel

+
<%= @kpis.funnel_date_range_title %>
+ + <% @kpis.forms_funnel.each do |path,v|%> + + + + + <% end %> +
<%= path %><%= v[:eligible] %>
+
+ +
+

Ineligible forms funnel

+
<%= @kpis.funnel_date_range_title %>
+ + <% @kpis.ineligible_forms_funnel.each do |path,v|%> + + + + + <% end %> +
<%= path %><%= v[:ineligible] %>
+
diff --git a/spec/models/kpis_spec.rb b/spec/models/kpis_spec.rb index c1ed016a..58abc3ed 100644 --- a/spec/models/kpis_spec.rb +++ b/spec/models/kpis_spec.rb @@ -1,35 +1,77 @@ require "rails_helper" RSpec.describe Kpis do - describe "#total_applications" do - it "returns the total number of applications" do - create_list(:application, 5) + subject(:kpis) { described_class.new(unit:, range_start:, range_end:) } + + let(:unit) { "hours" } + let(:range_start) { "12" } + let(:range_end) { "0" } - kpis = described_class.new + let(:application) { create(:application) } - expect(kpis.total_applications).to eq(5) + describe "argument error on initialize" do + context "when unknown unit" do + let(:unit) { "bad" } + + it { expect { kpis }.to raise_error(ArgumentError) } end - end - describe "#total_rejections" do - it "returns the total number of applications rejected" do - application = create(:application) - create_list(:application_progress, 5, :rejection_completed, application:) + context "when bad range_start and range_end" do + let(:range_start) { "bad" } + let(:range_end) { "bad" } + + it { expect { kpis }.to raise_error(ArgumentError) } + end + + context "when range_start negative" do + let(:range_start) { "-15" } + + it { expect { kpis }.to raise_error(ArgumentError) } + end + + context "when range_end negative" do + let(:range_end) { "-15" } + + it { expect { kpis }.to raise_error(ArgumentError) } + end - stats = described_class.new + context "when range_end value greater than range_start" do + let(:range_start) { "15" } + let(:range_end) { "24" } - expect(stats.total_rejections).to eq(5) + it { expect { kpis }.to raise_error(ArgumentError) } end end + describe "#total_applications" do + before { create_list(:application, 5) } + + it { expect(kpis.total_applications).to eq(5) } + end + + describe "#total_rejections" do + before { create_list(:application_progress, 5, :rejection_completed, application:) } + + it { expect(kpis.total_rejections).to eq(5) } + end + describe "#total_paid" do - it "returns the total number of applications paid" do - application = create(:application) - create_list(:application_progress, 5, :banking_approval_completed, application:) + before { create_list(:application_progress, 5, :banking_approval_completed, application:) } + + it { expect(kpis.total_paid).to eq(5) } + end + + describe "funnel_date_range_title" do + context "when range_end is set to 0" do + let(:range_end) { 0 } + + it { expect(kpis.funnel_date_range_title).to eq("last 12 hours") } + end - stats = described_class.new + context "when range_end is not 0" do + let(:range_end) { 6 } - expect(stats.total_paid).to eq(5) + it { expect(kpis.funnel_date_range_title).to eq("between 12 and 6 hours ago") } end end end