Browse Source

CLDC-2257 Refactor filters (#1675)

* feat: split lettings and sales filters

* feat: remove logs filter

* refactor: type -> filter_type

* refactor: slight cleanup

* feat: update csv download

* feat: set org filters to mirror primary sales and lettings

* refactor: linting

* feat: fix filter types in org controller - can be changed to org specific labels in future if needed rather than copying filters from primary sales/lettings tabs

* refactor: initalize filter_type

* refactor: lint

* refactor: combine filter_manager into filter_service

* refactor: rename to filter manager

* refactor: DRY session name filter logic

* feat: share session filter name logic

* feat: further refactor

* refactor: simplify

* feat: revert change to filter manager
pull/1693/head v0.3.28
natdeanlewissoftwire 2 years ago committed by GitHub
parent
commit
b0271ea0ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      app/controllers/lettings_logs_controller.rb
  2. 1
      app/controllers/logs_controller.rb
  3. 23
      app/controllers/modules/logs_filter.rb
  4. 4
      app/controllers/modules/search_filter.rb
  5. 35
      app/controllers/organisations_controller.rb
  6. 20
      app/controllers/sales_logs_controller.rb
  7. 18
      app/helpers/filters_helper.rb
  8. 4
      app/jobs/email_csv_job.rb
  9. 78
      app/services/filter_manager.rb
  10. 48
      app/services/filter_service.rb
  11. 2
      app/views/filters/_checkbox_filter.html.erb
  12. 2
      app/views/filters/_radio_filter.html.erb
  13. 2
      app/views/filters/_select_filter.html.erb
  14. 2
      spec/controllers/lettings_logs_controller_spec.rb
  15. 4
      spec/controllers/sales_logs_controller_spec.rb
  16. 38
      spec/helpers/filters_helper_spec.rb
  17. 2
      spec/services/filter_manager_spec.rb

20
app/controllers/lettings_logs_controller.rb

@ -4,7 +4,7 @@ class LettingsLogsController < LogsController
before_action :find_resource, only: %i[update show]
before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :set_session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action -> { filter_manager.serialize_filters_to_session }, if: :current_user, only: %i[index email_csv download_csv]
before_action :authenticate_scope!, only: %i[download_csv email_csv]
before_action :extract_bulk_upload_from_session_filters, only: [:index]
@ -14,13 +14,14 @@ class LettingsLogsController < LogsController
respond_to do |format|
format.html do
all_logs = current_user.lettings_logs.visible
unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters)
unpaginated_filtered_logs = filter_manager.filtered_logs(all_logs, search_term, session_filters)
@search_term = search_term
@pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
@total_count = all_logs.size
@unresolved_count = all_logs.unresolved.created_by(current_user).count
@filter_type = "lettings_logs"
render "logs/index"
end
end
@ -86,14 +87,14 @@ class LettingsLogsController < LogsController
end
def download_csv
unpaginated_filtered_logs = filtered_logs(current_user.lettings_logs, search_term, @session_filters)
unpaginated_filtered_logs = filter_manager.filtered_logs(current_user.lettings_logs, search_term, session_filters)
render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_lettings_logs_path, codes_only: codes_only_export? }
end
def email_csv
all_orgs = params["organisation_select"] == "all"
EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs, nil, codes_only_export?)
EmailCsvJob.perform_later(current_user, search_term, session_filters, all_orgs, nil, codes_only_export?)
redirect_to csv_confirmation_lettings_logs_path
end
@ -113,6 +114,14 @@ class LettingsLogsController < LogsController
private
def session_filters
filter_manager.session_filters
end
def filter_manager
FilterManager.new(current_user:, session:, params:, filter_type: "lettings_logs")
end
def org_params
super.merge(
{ "managing_organisation_id" => current_user.organisation.id },
@ -130,8 +139,7 @@ private
end
def extract_bulk_upload_from_session_filters
filter_service = FilterService.new(current_user:, session:)
@bulk_upload = filter_service.bulk_upload
@bulk_upload = filter_manager.bulk_upload
end
def permitted_log_params

1
app/controllers/logs_controller.rb

@ -1,6 +1,5 @@
class LogsController < ApplicationController
include Pagy::Backend
include Modules::LogsFilter
include Modules::SearchFilter
skip_before_action :verify_authenticity_token, if: :json_api_request?

23
app/controllers/modules/logs_filter.rb

@ -1,23 +0,0 @@
module Modules::LogsFilter
def filtered_logs(logs, search_term, filters)
all_orgs = params["organisation_select"] == "all"
FilterService.filter_logs(logs, search_term, filters, all_orgs, current_user)
end
def load_session_filters(specific_org: false)
current_filters = session[:logs_filters]
new_filters = current_filters.present? ? JSON.parse(current_filters) : {}
current_user.logs_filters(specific_org:).each do |filter|
new_filters[filter] = params[filter] if params[filter].present?
end
params["organisation_select"] == "all" ? new_filters.except("organisation") : new_filters
end
def session_filters(specific_org: false)
@session_filters ||= load_session_filters(specific_org:)
end
def set_session_filters
session[:logs_filters] = @session_filters.to_json
end
end

4
app/controllers/modules/search_filter.rb

@ -1,9 +1,9 @@
module Modules::SearchFilter
def filtered_collection(base_collection, search_term = nil)
FilterService.filter_by_search(base_collection, search_term)
FilterManager.filter_by_search(base_collection, search_term)
end
def filtered_users(base_collection, search_term = nil)
FilterService.filter_by_search(base_collection, search_term).includes(:organisation)
FilterManager.filter_by_search(base_collection, search_term).includes(:organisation)
end
end

35
app/controllers/organisations_controller.rb

@ -1,13 +1,12 @@
class OrganisationsController < ApplicationController
include Pagy::Backend
include Modules::LogsFilter
include Modules::SearchFilter
before_action :authenticate_user!
before_action :find_resource, except: %i[index new create]
before_action :authenticate_scope!, except: [:index]
before_action -> { session_filters(specific_org: true) }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
before_action :set_session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
before_action :session_filters, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
before_action -> { filter_manager.serialize_filters_to_session }, if: -> { current_user.support? || current_user.organisation.has_managing_agents? }, only: %i[lettings_logs sales_logs email_lettings_csv download_lettings_csv email_sales_csv download_sales_csv]
def index
redirect_to organisation_path(current_user.organisation) unless current_user.support?
@ -91,7 +90,7 @@ class OrganisationsController < ApplicationController
def lettings_logs
organisation_logs = LettingsLog.visible.where(owning_organisation_id: @organisation.id)
unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters)
respond_to do |format|
format.html do
@ -100,6 +99,7 @@ class OrganisationsController < ApplicationController
@searched = search_term.presence
@total_count = organisation_logs.size
@log_type = :lettings
@filter_type = "lettings_logs"
render "logs", layout: "application"
end
end
@ -107,20 +107,20 @@ class OrganisationsController < ApplicationController
def download_lettings_csv
organisation_logs = LettingsLog.visible.where(owning_organisation_id: @organisation.id)
unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters)
codes_only = params.require(:codes_only) == "true"
render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: lettings_logs_email_csv_organisation_path, codes_only: }
end
def email_lettings_csv
EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation, codes_only_export?)
EmailCsvJob.perform_later(current_user, search_term, session_filters, false, @organisation, codes_only_export?)
redirect_to lettings_logs_csv_confirmation_organisation_path
end
def sales_logs
organisation_logs = SalesLog.where(owning_organisation_id: @organisation.id)
unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters)
respond_to do |format|
format.html do
@ -129,6 +129,7 @@ class OrganisationsController < ApplicationController
@searched = search_term.presence
@total_count = organisation_logs.size
@log_type = :sales
@filter_type = "sales_logs"
render "logs", layout: "application"
end
@ -140,14 +141,14 @@ class OrganisationsController < ApplicationController
def download_sales_csv
organisation_logs = SalesLog.visible.where(owning_organisation_id: @organisation.id)
unpaginated_filtered_logs = filtered_logs(organisation_logs, search_term, @session_filters)
unpaginated_filtered_logs = filter_manager.filtered_logs(organisation_logs, search_term, session_filters)
codes_only = params.require(:codes_only) == "true"
render "logs/download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: sales_logs_email_csv_organisation_path, codes_only: }
end
def email_sales_csv
EmailCsvJob.perform_later(current_user, search_term, @session_filters, false, @organisation, codes_only_export?, "sales")
EmailCsvJob.perform_later(current_user, search_term, session_filters, false, @organisation, codes_only_export?, "sales")
redirect_to sales_logs_csv_confirmation_organisation_path
end
@ -189,6 +190,22 @@ class OrganisationsController < ApplicationController
private
def filter_type
if params[:action].include?("lettings")
"lettings_logs"
elsif params[:action].include?("sales")
"sales_logs"
end
end
def session_filters
filter_manager.session_filters
end
def filter_manager
FilterManager.new(current_user:, session:, params:, filter_type:)
end
def org_params
params.require(:organisation).permit(:name, :address_line1, :address_line2, :postcode, :phone, :holds_own_stock, :provider_type, :housing_registration_no)
end

20
app/controllers/sales_logs_controller.rb

@ -2,7 +2,7 @@ class SalesLogsController < LogsController
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
before_action :session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action :set_session_filters, if: :current_user, only: %i[index email_csv download_csv]
before_action -> { filter_manager.serialize_filters_to_session }, if: :current_user, only: %i[index email_csv download_csv]
before_action :authenticate_scope!, only: %i[download_csv email_csv]
before_action :extract_bulk_upload_from_session_filters, only: [:index]
@ -16,12 +16,13 @@ class SalesLogsController < LogsController
respond_to do |format|
format.html do
all_logs = current_user.sales_logs.visible
unpaginated_filtered_logs = filtered_logs(all_logs, search_term, @session_filters)
unpaginated_filtered_logs = filter_manager.filtered_logs(all_logs, search_term, session_filters)
@search_term = search_term
@pagy, @logs = pagy(unpaginated_filtered_logs)
@searched = search_term.presence
@total_count = all_logs.size
@filter_type = "sales_logs"
render "logs/index"
end
end
@ -57,14 +58,14 @@ class SalesLogsController < LogsController
end
def download_csv
unpaginated_filtered_logs = filtered_logs(current_user.sales_logs, search_term, @session_filters)
unpaginated_filtered_logs = filter_manager.filtered_logs(current_user.sales_logs, search_term, session_filters)
render "download_csv", locals: { search_term:, count: unpaginated_filtered_logs.size, post_path: email_csv_sales_logs_path, codes_only: codes_only_export? }
end
def email_csv
all_orgs = params["organisation_select"] == "all"
EmailCsvJob.perform_later(current_user, search_term, @session_filters, all_orgs, nil, codes_only_export?, "sales")
EmailCsvJob.perform_later(current_user, search_term, session_filters, all_orgs, nil, codes_only_export?, "sales")
redirect_to csv_confirmation_sales_logs_path
end
@ -80,9 +81,16 @@ class SalesLogsController < LogsController
private
def session_filters
filter_manager.session_filters
end
def filter_manager
FilterManager.new(current_user:, session:, params:, filter_type: "sales_logs")
end
def extract_bulk_upload_from_session_filters
filter_service = FilterService.new(current_user:, session:)
@bulk_upload = filter_service.bulk_upload
@bulk_upload = filter_manager.bulk_upload
end
def redirect_if_bulk_upload_resolved

18
app/helpers/filters_helper.rb

@ -1,8 +1,8 @@
module FiltersHelper
def filter_selected?(filter, value)
return false unless session[:logs_filters]
def filter_selected?(filter, value, filter_type)
return false unless session[session_name_for(filter_type)]
selected_filters = JSON.parse(session[:logs_filters])
selected_filters = JSON.parse(session[session_name_for(filter_type)])
return true if selected_filters.blank? && filter == "user" && value == :all
return true if !selected_filters.key?("organisation") && filter == "organisation_select" && value == :all
return true if selected_filters["organisation"].present? && filter == "organisation_select" && value == :specific_org
@ -19,10 +19,10 @@ module FiltersHelper
}.freeze
end
def selected_option(filter)
return false unless session[:logs_filters]
def selected_option(filter, filter_type)
return false unless session[session_name_for(filter_type)]
JSON.parse(session[:logs_filters])[filter] || ""
JSON.parse(session[session_name_for(filter_type)])[filter] || ""
end
def organisations_filter_options(user)
@ -33,4 +33,10 @@ module FiltersHelper
def collection_year_options
{ "2023": "2023/24", "2022": "2022/23", "2021": "2021/22" }
end
private
def session_name_for(filter_type)
"#{filter_type}_filters"
end
end

4
app/jobs/email_csv_job.rb

@ -9,11 +9,11 @@ class EmailCsvJob < ApplicationJob
case log_type
when "lettings"
unfiltered_logs = organisation.present? && user.support? ? LettingsLog.visible.where(owning_organisation_id: organisation.id) : user.lettings_logs.visible
filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user)
filtered_logs = FilterManager.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user)
csv_string = filtered_logs.to_csv(user, codes_only_export:)
when "sales"
unfiltered_logs = organisation.present? && user.support? ? SalesLog.visible.where(owning_organisation_id: organisation.id) : user.sales_logs.visible
filtered_logs = FilterService.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user)
filtered_logs = FilterManager.filter_logs(unfiltered_logs, search_term, filters, all_orgs, user)
export_type = codes_only_export ? "codes" : "labels"
csv_string = Csv::SalesLogCsvService.new(export_type:).prepare_csv(filtered_logs)
end

78
app/services/filter_manager.rb

@ -0,0 +1,78 @@
class FilterManager
attr_reader :current_user, :session, :params, :filter_type
def initialize(current_user:, session:, params:, filter_type:)
@current_user = current_user
@session = session
@params = params
@filter_type = filter_type
end
def self.filter_by_search(base_collection, search_term = nil)
if search_term.present?
base_collection.search_by(search_term)
else
base_collection
end
end
def self.filter_logs(logs, search_term, filters, all_orgs, user)
logs = filter_by_search(logs, search_term)
filters.each do |category, values|
next if Array(values).reject(&:empty?).blank?
next if category == "organisation" && all_orgs
logs = logs.public_send("filter_by_#{category}", values, user)
end
logs = logs.order(created_at: :desc)
if user.support?
if logs.first&.lettings?
logs.all.includes(:owning_organisation, :managing_organisation)
else
logs.all.includes(:owning_organisation)
end
else
logs
end
end
def serialize_filters_to_session(specific_org: false)
session[session_name_for(filter_type)] = session_filters(specific_org:).to_json
end
def session_filters(specific_org: false)
@session_filters ||= deserialize_filters_from_session(specific_org)
end
def deserialize_filters_from_session(specific_org)
current_filters = session[session_name_for(filter_type)]
new_filters = current_filters.present? ? JSON.parse(current_filters) : {}
if @filter_type.include?("logs")
current_user.logs_filters(specific_org:).each do |filter|
new_filters[filter] = params[filter] if params[filter].present?
end
end
params["organisation_select"] == "all" ? new_filters.except("organisation") : new_filters
end
def filtered_logs(logs, search_term, filters)
all_orgs = params["organisation_select"] == "all"
FilterManager.filter_logs(logs, search_term, filters, all_orgs, current_user)
end
def bulk_upload
id = (logs_filters["bulk_upload_id"] || []).reject(&:blank?)[0]
@bulk_upload ||= current_user.bulk_uploads.find_by(id:)
end
private
def logs_filters
JSON.parse(session[session_name_for(filter_type)] || "{}") || {}
end
def session_name_for(filter_type)
"#{filter_type}_filters"
end
end

48
app/services/filter_service.rb

@ -1,48 +0,0 @@
class FilterService
def self.filter_by_search(base_collection, search_term = nil)
if search_term.present?
base_collection.search_by(search_term)
else
base_collection
end
end
def self.filter_logs(logs, search_term, filters, all_orgs, user)
logs = filter_by_search(logs, search_term)
filters.each do |category, values|
next if Array(values).reject(&:empty?).blank?
next if category == "organisation" && all_orgs
logs = logs.public_send("filter_by_#{category}", values, user)
end
logs = logs.order(created_at: :desc)
if user.support?
if logs.first&.lettings?
logs.all.includes(:owning_organisation, :managing_organisation)
else
logs.all.includes(:owning_organisation)
end
else
logs
end
end
attr_reader :current_user, :session
def initialize(current_user:, session:)
@current_user = current_user
@session = session
end
def bulk_upload
id = (logs_filters["bulk_upload_id"] || []).reject(&:blank?)[0]
@bulk_upload ||= current_user.bulk_uploads.find_by(id:)
end
private
def logs_filters
JSON.parse(session[:logs_filters] || "{}") || {}
end
end

2
app/views/filters/_checkbox_filter.html.erb

@ -2,7 +2,7 @@
<% options.map do |key, option| %>
<%= f.govuk_check_box category, key.to_s,
label: { text: option },
checked: filter_selected?(category, key),
checked: filter_selected?(category, key, @filter_type),
size: "s" %>
<% end %>
<% end %>

2
app/views/filters/_radio_filter.html.erb

@ -2,7 +2,7 @@
<% options.map do |key, option| %>
<%= f.govuk_radio_button category, key.to_s,
label: { text: option[:label] },
checked: filter_selected?(category, key),
checked: filter_selected?(category, key, @filter_type),
size: "s" do %>
<% if option[:conditional_filter] %>
<%= render partial: "filters/#{option[:conditional_filter][:type]}_filter", locals: {

2
app/views/filters/_select_filter.html.erb

@ -3,5 +3,5 @@
:id,
:name,
label: { hidden: secondary },
options: { disabled: [""], selected: selected_option(category) },
options: { disabled: [""], selected: selected_option(category, @filter_type) },
"data-controller": %w[accessible-autocomplete conditional-filter] %>

2
spec/controllers/lettings_logs_controller_spec.rb

@ -10,7 +10,7 @@ RSpec.describe LettingsLogsController do
let(:bulk_upload) { create(:bulk_upload, :sales) }
it "does not redirect to resume path" do
session[:logs_filters] = { bulk_upload_id: [bulk_upload.id.to_s] }.to_json
session[:lettings_logs_filters] = { bulk_upload_id: [bulk_upload.id.to_s] }.to_json
get :index

4
spec/controllers/sales_logs_controller_spec.rb

@ -10,7 +10,7 @@ RSpec.describe SalesLogsController do
describe "#index" do
context "when a sales bulk upload has been resolved" do
it "redirects to resume_bulk_upload_sales_result_path" do
session[:logs_filters] = { bulk_upload_id: [bulk_upload.id.to_s] }.to_json
session[:sales_logs_filters] = { bulk_upload_id: [bulk_upload.id.to_s] }.to_json
get :index
@ -22,7 +22,7 @@ RSpec.describe SalesLogsController do
let(:bulk_upload) { create(:bulk_upload, :lettings) }
it "does not redirect to resume" do
session[:logs_filters] = { bulk_upload_id: [bulk_upload.id.to_s] }.to_json
session[:sales_logs_filters] = { bulk_upload_id: [bulk_upload.id.to_s] }.to_json
get :index

38
spec/helpers/filters_helper_spec.rb

@ -4,80 +4,80 @@ RSpec.describe FiltersHelper do
describe "#filter_selected?" do
context "when no filters are selected" do
it "returns false for all filters" do
expect(filter_selected?("status", "completed")).to be_falsey
expect(filter_selected?("status", "in_progress")).to be_falsey
expect(filter_selected?("status", "completed", "lettings_logs")).to be_falsey
expect(filter_selected?("status", "in_progress", "lettings_logs")).to be_falsey
end
end
context "when the filter is the user filter but session filters is empty" do
before do
session[:logs_filters] = {}.to_json
session[:lettings_logs_filters] = {}.to_json
end
context "when looking at the all value" do
it "returns true if no filters have been set yet" do
expect(filter_selected?("user", :all)).to be true
expect(filter_selected?("user", :yours)).to be false
expect(filter_selected?("user", :all, "lettings_logs")).to be true
expect(filter_selected?("user", :yours, "lettings_logs")).to be false
end
end
end
context "when one filter is selected" do
before do
session[:logs_filters] = { "status": "in_progress" }.to_json
session[:lettings_logs_filters] = { "status": "in_progress" }.to_json
end
it "returns false for non selected filters" do
expect(filter_selected?("status", "completed")).to be false
expect(filter_selected?("status", "completed", "lettings_logs")).to be false
end
it "returns true for selected filter" do
expect(filter_selected?("status", "in_progress")).to be true
expect(filter_selected?("status", "in_progress", "lettings_logs")).to be true
end
end
context "when support user is using the organisation filter" do
before do
session[:logs_filters] = { "organisation": "1" }.to_json
session[:lettings_logs_filters] = { "organisation": "1" }.to_json
end
it "returns true for the parent organisation_select filter" do
expect(filter_selected?("organisation_select", :specific_org)).to be true
expect(filter_selected?("organisation_select", :all)).to be false
expect(filter_selected?("organisation_select", :specific_org, "lettings_logs")).to be true
expect(filter_selected?("organisation_select", :all, "lettings_logs")).to be false
end
end
context "when support user has not set the organisation_select filter" do
before do
session[:logs_filters] = {}.to_json
session[:lettings_logs_filters] = {}.to_json
end
it "defaults to all organisations" do
expect(filter_selected?("organisation_select", :all)).to be true
expect(filter_selected?("organisation_select", :specific_org)).to be false
expect(filter_selected?("organisation_select", :all, "lettings_logs")).to be true
expect(filter_selected?("organisation_select", :specific_org, "lettings_logs")).to be false
end
end
context "when the specific organisation filter is not set" do
before do
session[:logs_filters] = { "status" => [""], "years" => [""], "user" => "all" }.to_json
session[:lettings_logs_filters] = { "status" => [""], "years" => [""], "user" => "all" }.to_json
end
it "marks the all options as checked" do
expect(filter_selected?("organisation_select", :all)).to be true
expect(filter_selected?("organisation_select", :specific_org)).to be false
expect(filter_selected?("organisation_select", :all, "lettings_logs")).to be true
expect(filter_selected?("organisation_select", :specific_org, "lettings_logs")).to be false
end
end
end
describe "#selected_option" do
before do
session[:logs_filters] = {}.to_json
session[:lettings_logs_filters] = {}.to_json
end
context "when nothing has been selected" do
it "returns an empty string" do
expect(selected_option("organisation")).to eq("")
expect(selected_option("organisation", "lettings_logs")).to eq("")
end
end
end

2
spec/services/filter_service_spec.rb → spec/services/filter_manager_spec.rb

@ -1,6 +1,6 @@
require "rails_helper"
describe FilterService do
describe FilterManager do
describe "filter_by_search" do
context "when filtering organisations" do
before do
Loading…
Cancel
Save