Skip to content

Commit

Permalink
Merge branch 'main' into feature/roles
Browse files Browse the repository at this point in the history
  • Loading branch information
raul-gracia authored Oct 9, 2023
2 parents b367e01 + 87025bc commit 1702ec0
Show file tree
Hide file tree
Showing 47 changed files with 1,324 additions and 98 deletions.
6 changes: 6 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,9 @@ RSpec/DescribeClass:
RSpec/AnyInstance:
Exclude:
- 'spec/requests/submission_spec.rb'

RSpec/MultipleMemoizedHelpers:
Exclude:
- 'spec/models/event_spec.rb'


8 changes: 8 additions & 0 deletions app/controllers/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,12 @@ class PagesController < ApplicationController
def index; end
def closed; end
def sitemap; end

def ineligible
session.delete("form_id")
end

def ineligible_salaried_course
session.delete("form_id")
end
end
2 changes: 1 addition & 1 deletion app/controllers/system_admin/applicants_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def applicant_params
:payment_confirmation_completed_at,
:rejection_completed_at,
:rejection_reason,
:rejection_details,
:comments,
)
end

Expand Down
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
2 changes: 1 addition & 1 deletion app/models/application_progress.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
#
# id :bigint not null, primary key
# banking_approval_completed_at :date
# comments :text
# home_office_checks_completed_at :date
# initial_checks_completed_at :date
# payment_confirmation_completed_at :date
# rejection_completed_at :date
# rejection_details :text
# rejection_reason :integer
# school_checks_completed_at :date
# school_investigation_required :boolean default(FALSE), not null
Expand Down
61 changes: 61 additions & 0 deletions app/models/event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# == Schema Information
#
# Table name: events
#
# id :bigint not null, primary key
# action :string
# data :jsonb
# entity_class :string
# created_at :datetime not null
# updated_at :datetime not null
# entity_id :integer
#
class Event < ApplicationRecord
ACTIONS = [
CREATED = "created".freeze,
UPDATED = "updated".freeze,
DELETED = "deleted".freeze,
].freeze

validates(:action, presence: true)
validates(:action, inclusion: { in: ACTIONS })
validates(:entity_class, presence: true)
validates(:entity_id, presence: true)
validates(:data, presence: true, unless: :deleted?)

class << self
def publish(action, model, model_changes = {})
create!(
action: action&.to_s,
entity_class: model&.class,
entity_id: model&.id,
data: filtered(model, model_changes),
)
end

private

def filtered(model, model_changes)
model_changes.each_with_object({}) do |(attribute, diff), hsh|
hsh[attribute] = diff
hsh[attribute] = obfuscate(diff) if filtered_attribute?(model, attribute)

hsh
end
end

def filtered_attribute?(model, attribute)
Rails.configuration.x.events.filtered_attributes
.fetch(model.class.name, [])
.include?(attribute)
end

def obfuscate(diff)
Array(diff).map { |e| e.blank? ? e : "[FILTERED]" }
end
end

def deleted?
action == DELETED
end
end
57 changes: 55 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,54 @@ def time_to_payment_confirmation
def status_breakdown
StatusBreakdownQuery.call
end

def forms_funnel
forms_funnel_query
end

def ineligible_forms_funnel
forms_funnel_query.select { |_, hsh| hsh.fetch(:ineligible, nil) }
end

def funnel_date_range_title
return funnel_title_last_n if date_range_params.fetch(:range_end).zero?

[
"between",
date_range_params.fetch(:range_start),
"and",
date_range_params.fetch(:range_end),
date_range_params.fetch(:unit),
"ago",
].join(" ")
end

private

def funnel_title_last_n
["last", date_range_params.fetch(:range_start), date_range_params.fetch(:unit)].join(" ")
end

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
10 changes: 6 additions & 4 deletions app/models/reports/applications.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def csv

def header
%i[
ip_address
urn
given_name
middle_name
family_name
Expand All @@ -32,15 +32,16 @@ def header
date_of_entry
start_date
subject
urn
visa_type
rejection_reason
ip_address
].map { _1.to_s.titleize }
end

def columns(application)
applicant = application.applicant
[
applicant.ip_address,
application.urn,
applicant.given_name,
applicant.middle_name,
applicant.family_name,
Expand All @@ -61,8 +62,9 @@ def columns(application)
application.date_of_entry,
application.start_date,
application.subject,
application.urn,
application.visa_type,
application.application_progress.rejection_reason,
applicant.ip_address,
]
end

Expand Down
2 changes: 1 addition & 1 deletion app/models/reports/qa_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def rejection_rows(app)

[
app.application_progress.rejection_reason&.humanize,
app.application_progress.rejection_details,
app.application_progress.comments,
]
end

Expand Down
63 changes: 33 additions & 30 deletions app/models/urn.rb
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
# frozen_string_literal: true

# Urn represents a Uniform Resource Name (URN) generator.
# It generates a URN with a fixed prefix and a random alphanumeric suffix.
#
# == Schema Information
#
# Example:
# Table name: urns
#
# Urn.generate('teacher') # => "IRPTE12345"
# Urn.generate('teacher') # => "IRPTE12345"
# Urn.generate('salaried_trainee') # => "IRPST12345"
# id :bigint not null, primary key
# code :string
# prefix :string
# suffix :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Urn
attr_reader :value
attr_writer :suffix

def self.generate(applicant_type)
code = applicant_type_code(applicant_type)
PREFIX + code + Array.new(LENGTH) { CHARSET.sample }.join
end
class Urn < ApplicationRecord
class NoUrnAvailableError < StandardError; end

CHARSET = %w[0 1 2 3 4 5 6 7 8 9].freeze
PREFIX = "IRP"
LENGTH = 5
private_constant :CHARSET, :PREFIX, :LENGTH
PREFIX = "IRP".freeze
MAX_SUFFIX = 99_999
PADDING_SIZE = MAX_SUFFIX.to_s.size
VALID_CODES = {
"teacher" => "TE",
"salaried_trainee" => "ST",
}.freeze

def self.applicant_type_code(applicant_type)
case applicant_type
when "teacher"
"TE"
when "salaried_trainee"
"ST"
else
raise(ArgumentError, "Invalid applicant type: #{applicant_type}")
def self.next(route)
code = VALID_CODES.fetch(route)
Urn.transaction do
urn = find_by!(code:)
urn.destroy!
urn.to_s
end
rescue KeyError => e
Sentry.capture_exception(e)
raise(ArgumentError, "Unknown route #{route}")
rescue ActiveRecord::RecordNotFound => e
Sentry.capture_exception(e)
raise(NoUrnAvailableError, "There no more unique URN available for #{route}")
end

def to_s
[prefix, code, sprintf("%0#{PADDING_SIZE}d", suffix)].join
end
private_methods :applicant_type_code
end
Loading

0 comments on commit 1702ec0

Please sign in to comment.