Browse Source

feat: add schemes and locations download links and pages

pull/2083/head
natdeanlewissoftwire 2 years ago
parent
commit
4359ecc42c
  1. 6
      app/components/search_result_caption_component.html.erb
  2. 12
      app/controllers/organisations_controller.rb
  3. 22
      app/controllers/schemes_controller.rb
  4. 14
      app/helpers/schemes_helper.rb
  5. 33
      app/jobs/email_scheme_csv_job.rb
  6. 8
      app/models/user.rb
  7. 6
      app/views/organisations/schemes.html.erb
  8. 7
      app/views/schemes/_scheme_list.html.erb
  9. 16
      app/views/schemes/download_csv.html.erb
  10. 4
      app/views/schemes/index.html.erb
  11. 9
      config/routes.rb
  12. 110
      spec/requests/organisations_controller_spec.rb

6
app/components/search_result_caption_component.html.erb

@ -1,10 +1,10 @@
<span class="govuk-!-margin-right-4">
<% if searched.present? && filters_count&.positive? %>
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching search and filters<br>
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching search and filters
<% elsif searched.present? %>
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching search<br>
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching search
<% elsif filters_count&.positive? %>
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching filters<br>
<strong><%= count %></strong> <%= item_label.pluralize(count) %> matching filters
<% else %>
<strong><%= count %></strong> matching <%= item %>
<% end %>

12
app/controllers/organisations_controller.rb

@ -29,6 +29,18 @@ class OrganisationsController < ApplicationController
@filter_type = "schemes"
end
def download_schemes_csv
organisation_schemes = Scheme.where(owning_organisation_id: @organisation.id)
unpaginated_filtered_schemes = filter_manager.filtered_schemes(organisation_schemes, search_term, session_filters)
render "schemes/download_csv", locals: { search_term:, post_path: email_csv_schemes_path, download_type: params[:download_type], schemes: unpaginated_filtered_schemes }
end
def email_schemes_csv
SchemesEmailCsvJob.perform_later(current_user, search_term, session_filters, false, @organisation)
redirect_to schemes_csv_confirmation_organisation_path
end
def show
redirect_to details_organisation_path(@organisation)
end

22
app/controllers/schemes_controller.rb

@ -3,11 +3,11 @@ class SchemesController < ApplicationController
include Modules::SearchFilter
before_action :authenticate_user!
before_action :find_resource, except: %i[index create new changes]
before_action :find_resource, except: %i[index create new changes email_csv download_csv csv_confirmation]
before_action :redirect_if_scheme_confirmed, only: %i[primary_client_group confirm_secondary_client_group secondary_client_group support details]
before_action :authorize_user
before_action :session_filters, if: :current_user, only: %i[index]
before_action -> { filter_manager.serialize_filters_to_session }, if: :current_user, only: %i[index]
before_action :authorize_user, except: %i[email_csv download_csv csv_confirmation]
before_action :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]
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
@ -205,6 +205,20 @@ class SchemesController < ApplicationController
render "schemes/changes"
end
def download_csv
unpaginated_filtered_schemes = filter_manager.filtered_schemes(current_user.schemes, search_term, session_filters)
render "download_csv", locals: { search_term:, post_path: email_csv_schemes_path, download_type: params[:download_type], schemes: unpaginated_filtered_schemes }
end
def email_csv
all_orgs = params["organisation_select"] == "all"
SchemesEmailCsvJob.perform_later(current_user, search_term, session_filters, all_orgs, nil)
redirect_to csv_confirmation_schemes_path
end
def csv_confirmation; end
private
def authorize_user

14
app/helpers/schemes_helper.rb

@ -54,6 +54,20 @@ module SchemesHelper
end
end
def selected_schemes_and_locations_text(download_type, schemes)
scheme_count = schemes.count
case download_type
when "schemes"
"You've selected #{pluralize(scheme_count, 'scheme')}."
when "locations"
location_count = schemes.map(&:locations).flatten.count
"You've selected #{pluralize(location_count, 'location')} from #{pluralize(scheme_count, 'scheme')}."
when "combined"
location_count = schemes.map(&:locations).flatten.count
"You've selected #{pluralize(scheme_count, 'scheme')} with #{pluralize(location_count, 'location')}. The CSV will have one location per row with scheme details listed for each location."
end
end
private
ActivePeriod = Struct.new(:from, :to)

33
app/jobs/email_scheme_csv_job.rb

@ -0,0 +1,33 @@
class EmailSchemeCsvJob < ApplicationJob
queue_as :default
BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8
EXPIRATION_TIME = 24.hours.to_i
def perform(user, search_term = nil, filters = {}, all_orgs = false, organisation = nil, download_type = "combined") # rubocop:disable Style/OptionalBooleanParameter - sidekiq can't serialise named params
unfiltered_schemes = organisation.present? && user.support? ? Scheme.where(owning_organisation_id: organisation.id) : user.schemes.visible
filtered_schemes = FilterManager.filter_schemes(unfiltered_schemes, search_term, filters, all_orgs, user)
case download_type
when "schemes"
csv_string = Csv::SchemeCsvService.new(user:).prepare_csv(filtered_schemes)
filename = "#{['schemes', organisation&.name, Time.zone.now].compact.join('-')}.csv"
when "locations"
filtered_locations = filtered_schemes.map(&:locations).flatten
csv_string = Csv::LocationCsvService.new(user:).prepare_csv(filtered_locations)
filename = "#{['locations', organisation&.name, Time.zone.now].compact.join('-')}.csv"
when "combined"
filtered_locations = filtered_schemes.map(&:locations).flatten
csv_string = Csv::SchemeAndLocationCsvService.new(user:).prepare_csv(filtered_locations)
filename = "#{['schemes-and-locations', organisation&.name, Time.zone.now].compact.join('-')}.csv"
end
storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"])
storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string)
url = storage_service.get_presigned_url(filename, EXPIRATION_TIME)
CsvDownloadMailer.new.send_csv_download_mail(user, url, EXPIRATION_TIME)
end
end

8
app/models/user.rb

@ -99,6 +99,14 @@ class User < ApplicationRecord
LettingsLog.filter_by_managing_organisation(organisation.absorbed_organisations + [organisation])
end
def schemes
if support?
Scheme.all
else
Scheme.filter_by_owning_organisation(organisation.absorbed_organisations + [organisation])
end
end
def is_key_contact?
is_key_contact
end

6
app/views/organisations/schemes.html.erb

@ -27,7 +27,11 @@
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
<% if current_user.support? %>
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count, schemes_csv_download_url: schemes_csv_download_organisation_path(@organisation, search: @searched, download_type: "schemes"), locations_csv_download_url: schemes_csv_download_organisation_path(@organisation, search: @searched, download_type: "locations"), combined_csv_download_url: schemes_csv_download_organisation_path(@organisation, search: @searched, download_type: "combined") } %>
<% else %>
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count, schemes_csv_download_url: csv_download_schemes_path(search: @searched, download_type: "schemes"), locations_csv_download_url: csv_download_schemes_path(search: @searched, download_type: "locations"), combined_csv_download_url: csv_download_schemes_path(search: @searched, download_type: "combined") } %>
<% end %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "schemes" } %>
</div>

7
app/views/schemes/_scheme_list.html.erb

@ -1,8 +1,13 @@
<section class="app-table-group" tabindex="0" aria-labelledby="<%= title.dasherize %>">
<%= govuk_table do |table| %>
<%= table.caption(classes: %w[govuk-!-font-size-19 govuk-!-font-weight-regular]) do |caption| %>
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "schemes", filters_count: applied_filters_count(@filter_type))) %>
<%= render(SearchResultCaptionComponent.new(searched:, count: pagy.count, item_label:, total_count:, item: "schemes", filters_count: applied_filters_count(@filter_type))) %>
<% if @schemes&.any? %>
<%= govuk_link_to "Download schemes (CSV)", schemes_csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4", style: "white-space: nowrap" %>
<%= govuk_link_to "Download locations (CSV)", locations_csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4", style: "white-space: nowrap" %>
<%= govuk_link_to "Download schemes and locations (CSV)", combined_csv_download_url, type: "text/csv", class: "govuk-!-margin-right-4", style: "white-space: nowrap" %>
<% end %>
<% end %>
<%= table.head do |head| %>
<%= head.row do |row| %>
<% row.cell(header: true, text: "Scheme", html_attributes: { scope: "col", class: "govuk-!-width-one-quarter" }) %>

16
app/views/schemes/download_csv.html.erb

@ -0,0 +1,16 @@
<% content_for :title, "Download CSV" %>
<% content_for :before_content do %>
<%= govuk_back_link(href: :back) %>
<% end %>
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-l">Download CSV</h1>
<p class="govuk-body">We'll send a secure download link to your email address <strong><%= @current_user.email %></strong>.</p>
<p class="govuk-body"><%= selected_schemes_and_locations_text(download_type, schemes) %></p>
<%= govuk_button_to "Send email", post_path, method: :post, params: { search: search_term } %>
</div>
</div>

4
app/views/schemes/index.html.erb

@ -15,8 +15,8 @@
<hr class="govuk-section-break govuk-section-break--visible govuk-section-break--m">
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count } %>
<%= render partial: "schemes/scheme_list", locals: { schemes: @schemes, title:, pagy: @pagy, searched: @searched, item_label:, total_count: @total_count, schemes_csv_download_url: csv_download_schemes_path(search: @searched, download_type: "schemes"), locations_csv_download_url: csv_download_schemes_path(search: @searched, download_type: "locations"), combined_csv_download_url: csv_download_schemes_path(search: @searched, download_type: "combined") } %>
<%== render partial: "pagy/nav", locals: { pagy: @pagy, item_name: "schemes" } %>
</div>
</div>

9
config/routes.rb

@ -79,6 +79,12 @@ Rails.application.routes.draw do
patch "deactivate", to: "schemes#deactivate"
patch "reactivate", to: "schemes#reactivate"
collection do
get "csv-download", to: "schemes#download_csv"
post "email-csv", to: "schemes#email_csv"
get "csv-confirmation", to: "schemes#csv_confirmation"
end
resources :locations do
post "locations", to: "locations#create"
get "new-deactivation", to: "locations#new_deactivation"
@ -148,6 +154,9 @@ Rails.application.routes.draw do
post "sales-logs/email-csv", to: "organisations#email_sales_csv"
get "sales-logs/csv-confirmation", to: "sales_logs#csv_confirmation"
get "schemes", to: "organisations#schemes"
get "schemes/csv-download", to: "organisations#download_schemes_csv"
post "schemes/email-csv", to: "organisations#email_schemes_csv"
get "schemes/csv-confirmation", to: "schemes#csv_confirmation"
get "stock-owners", to: "organisation_relationships#stock_owners"
get "stock-owners/add", to: "organisation_relationships#add_stock_owner"
get "stock-owners/remove", to: "organisation_relationships#remove_stock_owner"

110
spec/requests/organisations_controller_spec.rb

@ -59,6 +59,62 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_field("search", type: "search")
end
describe "scheme and location csv downloads" do
let!(:specific_organisation) { create(:organisation) }
let!(:specific_org_schemes) { create_list(:scheme, 5, owning_organisation: specific_organisation) }
let!(:specific_org_scheme) { create(:scheme, owning_organisation: specific_organisation) }
let!(:specific_org_locations) { create_list(:location, 3, scheme: specific_org_scheme) }
it "shows scheme and location download links" do
expect(page).to have_link("Download schemes (CSV)", href: csv_download_schemes_path(download_type: "schemes"))
expect(page).to have_link("Download locations (CSV)", href: csv_download_schemes_path(download_type: "locations"))
expect(page).to have_link("Download schemes and locations (CSV)", href: csv_download_schemes_path(download_type: "combined"))
end
context "when there are no schemes for this organisation" do
before do
specific_organisation.owned_schemes.destroy_all
get "/organisations/#{specific_organisation.id}/schemes", headers:, params: {}
end
it "does not display CSV download links" do
expect(page).not_to have_link("Download schemes (CSV)")
expect(page).not_to have_link("Download locations (CSV)")
expect(page).not_to have_link("Download schemes and locations (CSV)")
end
end
context "when downloading scheme data" do
before do
get csv_download_schemes_path(download_type: "schemes")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 6 schemes.")
end
end
context "when downloading location data" do
before do
get csv_download_schemes_path(download_type: "locations")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 3 locations from 6 schemes.")
end
end
context "when downloading scheme and location data" do
before do
get csv_download_schemes_path(download_type: "combined")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 6 schemes with 3 locations.")
end
end
end
it "has hidden accessibility field with description" do
expected_field = "<h2 class=\"govuk-visually-hidden\">Supported housing schemes</h2>"
expect(CGI.unescape_html(response.body)).to include(expected_field)
@ -116,6 +172,60 @@ RSpec.describe OrganisationsController, type: :request do
expect(page).to have_field("search", type: "search")
end
describe "scheme and location csv downloads" do
let!(:same_org_schemes) { create_list(:scheme, 5, owning_organisation: user.organisation) }
let!(:same_org_locations) { create_list(:location, 3, scheme: same_org_scheme) }
it "shows scheme and location download links" do
expect(page).to have_link("Download schemes (CSV)", href: csv_download_schemes_path(download_type: "schemes"))
expect(page).to have_link("Download locations (CSV)", href: csv_download_schemes_path(download_type: "locations"))
expect(page).to have_link("Download schemes and locations (CSV)", href: csv_download_schemes_path(download_type: "combined"))
end
context "when there are no schemes for this organisation" do
before do
user.organisation.owned_schemes.destroy_all
get "/organisations/#{organisation.id}/schemes", headers:, params: {}
end
it "does not display CSV download links" do
expect(page).not_to have_link("Download schemes (CSV)")
expect(page).not_to have_link("Download locations (CSV)")
expect(page).not_to have_link("Download schemes and locations (CSV)")
end
end
context "when downloading scheme data" do
before do
get csv_download_schemes_path(download_type: "schemes")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 6 schemes.")
end
end
context "when downloading location data" do
before do
get csv_download_schemes_path(download_type: "locations")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 3 locations from 6 schemes.")
end
end
context "when downloading scheme and location data" do
before do
get csv_download_schemes_path(download_type: "combined")
end
it "redirects to the correct download page" do
expect(page).to have_content("You've selected 6 schemes with 3 locations.")
end
end
end
it "shows only schemes belonging to the same organisation" do
expect(page).to have_content(same_org_scheme.id_to_display)
schemes.each do |scheme|

Loading…
Cancel
Save