Skip to content

Commit

Permalink
Add Forms funnel and Ineligible forms funnel
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
fumimowdan committed Oct 2, 2023
1 parent 648072b commit a76257c
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 20 deletions.
15 changes: 14 additions & 1 deletion app/controllers/system_admin/dashboard_controller.rb
Original file line number Diff line number Diff line change
@@ -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
50 changes: 48 additions & 2 deletions app/models/kpis.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
25 changes: 25 additions & 0 deletions app/views/system_admin/dashboard/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,30 @@
</table>
</div>

<div class="kpi-widget form-funnel">
<h3 class="title">Forms funnel</h3>
<h6 class="subtitle"><%= @kpis.funnel_date_range_title %></h6>
<table class="kpi-table">
<% @kpis.forms_funnel.each do |path,v|%>
<tr>
<td><%= path %></td>
<td><%= v[:eligible] %></td>
</tr>
<% end %>
</table>
</div>

<div class="kpi-widget ineligibility-funnel">
<h3 class="title">Ineligible forms funnel</h3>
<h6 class="subtitle"><%= @kpis.funnel_date_range_title %></h6>
<table class="kpi-table">
<% @kpis.ineligible_forms_funnel.each do |path,v|%>
<tr>
<td><%= path %></td>
<td><%= v[:ineligible] %></td>
</tr>
<% end %>
</table>
</div>

</div>
76 changes: 59 additions & 17 deletions spec/models/kpis_spec.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit a76257c

Please sign in to comment.