diff --git a/app/jobs/create_illness_csv_job.rb b/app/jobs/create_illness_csv_job.rb new file mode 100644 index 000000000..be6693247 --- /dev/null +++ b/app/jobs/create_illness_csv_job.rb @@ -0,0 +1,17 @@ +class CreateIllnessCsvJob < ApplicationJob + queue_as :default + + BYTE_ORDER_MARK = "\uFEFF".freeze # Required to ensure Excel always reads CSV as UTF-8 + + def perform(organisation) + csv_service = Csv::MissingIllnessCsvService.new(organisation) + + csv_string = csv_service.create_illness_csv + filename = "#{['missing-illness', organisation.name, Time.zone.now].compact.join('-')}.csv" + + storage_service = Storage::S3Service.new(Configuration::EnvConfigurationService.new, ENV["CSV_DOWNLOAD_PAAS_INSTANCE"]) + storage_service.write_file(filename, BYTE_ORDER_MARK + csv_string) + + Rails.logger.info("Created illness file: #{filename}") + end +end diff --git a/app/models/lettings_log.rb b/app/models/lettings_log.rb index 79a6837d3..66d894e3b 100644 --- a/app/models/lettings_log.rb +++ b/app/models/lettings_log.rb @@ -99,6 +99,20 @@ class LettingsLog < Log scope.pluck("ARRAY_AGG(id)") } + scope :with_illness_without_type, lambda { + where(illness: 1, + illness_type_1: false, + illness_type_2: false, + illness_type_3: false, + illness_type_4: false, + illness_type_5: false, + illness_type_6: false, + illness_type_7: false, + illness_type_8: false, + illness_type_9: false, + illness_type_10: false) + } + AUTOGENERATED_FIELDS = %w[id status created_at updated_at discarded_at].freeze OPTIONAL_FIELDS = %w[tenancycode propcode chcharge].freeze RENT_TYPE_MAPPING_LABELS = { 1 => "Social Rent", 2 => "Affordable Rent", 3 => "Intermediate Rent" }.freeze diff --git a/app/services/csv/missing_illness_csv_service.rb b/app/services/csv/missing_illness_csv_service.rb new file mode 100644 index 000000000..106065653 --- /dev/null +++ b/app/services/csv/missing_illness_csv_service.rb @@ -0,0 +1,48 @@ +module Csv + class MissingIllnessCsvService + def initialize(organisation) + @organisation = organisation + end + + def create_illness_csv + logs = @organisation.managed_lettings_logs + .imported + .after_date(Time.zone.local(2022, 4, 1)) + .with_illness_without_type + + CSV.generate(headers: false) do |csv| + csv << ["Question", "Log ID", "Tenancy start date", "Tenant code", "Property reference", "Log owner", "Owning organisation", "Managing organisation", "Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?", "Does this person's condition affect their vision?", "Does this person's condition affect their hearing?", "Does this person's condition affect their mobility?", "Does this person's condition affect their dexterity?", "Does this person's condition affect their learning or understanding or concentrating?", "Does this person's condition affect their memory?", "Does this person's condition affect their mental health?", "Does this person's condition affect their stamina or breathing or fatigue?", "Does this person's condition affect them socially or behaviourally?", "Does this person's condition affect them in another way?"] + csv << ["Additional info", nil, nil, nil, nil, nil, nil, nil, nil, "For example, blindness or partial sight", "For example, deafness or partial hearing", nil, "For example, lifting and carrying objects, or using a keyboard", nil, nil, "For example, depression or anxiety", nil, "Anything associated with autism spectrum disorder (ASD), including Asperger's or attention deficit hyperactivity disorder (ADHD)", nil, nil] + csv << ["How to answer", "Do not change the answers for this field", "Do not change the answers for this field", "Do not change the answers for this field", "Do not change the answers for this field", "Do not change the answers for this field", "Do not change the answers for this field", "Do not change the answers for this field", "1 = Yes; 2 = No; 3 = Prefers not to say", "1 = Yes; blank = No", "1 = Yes; blank = No", "1 = Yes; blank = No", "1 = Yes; blank = No", "1 = Yes; blank = No", "1 = Yes; blank = No", "1 = Yes; blank = No", "1 = Yes; blank = No", "1 = Yes; blank = No", "1 = Yes; blank = No"] + + logs.each do |log| + csv << log_to_csv_row(log) + end + end + end + + private + + def log_to_csv_row(log) + [nil, + log.id, + log.startdate&.to_date, + log.tenancycode, + log.propcode, + log.created_by&.email, + log.owning_organisation&.name, + log.managing_organisation&.name, + 1, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil, + nil] + end + end +end diff --git a/lib/tasks/correct_illness_from_csv.rake b/lib/tasks/correct_illness_from_csv.rake new file mode 100644 index 000000000..08fab4e17 --- /dev/null +++ b/lib/tasks/correct_illness_from_csv.rake @@ -0,0 +1,15 @@ +namespace :correct_illness do + desc "Export data CSVs for import into Central Data System (CDS)" + task :create_illness_csv, %i[organisation_id] => :environment do |_task, args| + organisation_id = args[:organisation_id] + raise "Usage: rake correct_illness:create_illness_csv['organisation_id']" if organisation_id.blank? + + organisation = Organisation.find_by(id: organisation_id) + if organisation.present? + CreateIllnessCsvJob.perform_later(organisation) + Rails.logger.info("Creating illness CSV for #{organisation.name}") + else + Rails.logger.error("Organisation with ID #{organisation_id} not found") + end + end +end diff --git a/spec/factories/lettings_log.rb b/spec/factories/lettings_log.rb index c85b4b00a..e661645c7 100644 --- a/spec/factories/lettings_log.rb +++ b/spec/factories/lettings_log.rb @@ -193,6 +193,19 @@ FactoryBot.define do trait :imported do old_id { Random.hex } end + trait :with_illness_without_type do + illness { 1 } + illness_type_1 { false } + illness_type_2 { false } + illness_type_3 { false } + illness_type_4 { false } + illness_type_5 { false } + illness_type_6 { false } + illness_type_7 { false } + illness_type_8 { false } + illness_type_9 { false } + illness_type_10 { false } + end created_at { Time.zone.today } updated_at { Time.zone.today } end diff --git a/spec/fixtures/files/empty_illness.csv b/spec/fixtures/files/empty_illness.csv new file mode 100644 index 000000000..21d651351 --- /dev/null +++ b/spec/fixtures/files/empty_illness.csv @@ -0,0 +1,3 @@ +Question,Log ID,Tenancy start date,Tenant code,Property reference,Log owner,Owning organisation,Managing organisation,Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?,Does this person's condition affect their vision?,Does this person's condition affect their hearing?,Does this person's condition affect their mobility?,Does this person's condition affect their dexterity?,Does this person's condition affect their learning or understanding or concentrating?,Does this person's condition affect their memory?,Does this person's condition affect their mental health?,Does this person's condition affect their stamina or breathing or fatigue?,Does this person's condition affect them socially or behaviourally?,Does this person's condition affect them in another way? +Additional info,,,,,,,,,"For example, blindness or partial sight","For example, deafness or partial hearing",,"For example, lifting and carrying objects, or using a keyboard",,,"For example, depression or anxiety",,"Anything associated with autism spectrum disorder (ASD), including Asperger's or attention deficit hyperactivity disorder (ADHD)",, +How to answer,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,1 = Yes; 2 = No; 3 = Prefers not to say,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No diff --git a/spec/fixtures/files/illness.csv b/spec/fixtures/files/illness.csv new file mode 100644 index 000000000..9bc10befc --- /dev/null +++ b/spec/fixtures/files/illness.csv @@ -0,0 +1,4 @@ +Question,Log ID,Tenancy start date,Tenant code,Property reference,Log owner,Owning organisation,Managing organisation,Does anybody in the household have a physical or mental health condition (or other illness) expected to last 12 months or more?,Does this person's condition affect their vision?,Does this person's condition affect their hearing?,Does this person's condition affect their mobility?,Does this person's condition affect their dexterity?,Does this person's condition affect their learning or understanding or concentrating?,Does this person's condition affect their memory?,Does this person's condition affect their mental health?,Does this person's condition affect their stamina or breathing or fatigue?,Does this person's condition affect them socially or behaviourally?,Does this person's condition affect them in another way? +Additional info,,,,,,,,,"For example, blindness or partial sight","For example, deafness or partial hearing",,"For example, lifting and carrying objects, or using a keyboard",,,"For example, depression or anxiety",,"Anything associated with autism spectrum disorder (ASD), including Asperger's or attention deficit hyperactivity disorder (ADHD)",, +How to answer,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,Do not change the answers for this field,1 = Yes; 2 = No; 3 = Prefers not to say,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No,1 = Yes; blank = No +,{id},2023-04-05,tenancycode1,propcode1,testy@example.com,Illness org,Illness org,1,,,,,,,,,, diff --git a/spec/jobs/create_illness_csv_job_spec.rb b/spec/jobs/create_illness_csv_job_spec.rb new file mode 100644 index 000000000..eb5180467 --- /dev/null +++ b/spec/jobs/create_illness_csv_job_spec.rb @@ -0,0 +1,33 @@ +require "rails_helper" + +describe CreateIllnessCsvJob do + include Helpers + + let(:job) { described_class.new } + let(:storage_service) { instance_double(Storage::S3Service) } + let(:mailer) { instance_double(CsvDownloadMailer) } + let(:missing_illness_csv_service) { instance_double(Csv::MissingIllnessCsvService) } + let(:organisation) { build(:organisation) } + let(:users) { create_list(:user, 2) } + + before do + allow(Storage::S3Service).to receive(:new).and_return(storage_service) + allow(storage_service).to receive(:write_file) + + allow(Csv::MissingIllnessCsvService).to receive(:new).and_return(missing_illness_csv_service) + allow(missing_illness_csv_service).to receive(:create_illness_csv).and_return("") + end + + context "when creating illness logs csv" do + it "uses an appropriate filename in S3" do + expect(storage_service).to receive(:write_file).with(/missing-illness-#{organisation.name}-.*\.csv/, anything) + job.perform(organisation) + end + + it "creates a MissingIllnessCsvService with the correct organisation and calls create illness csv" do + expect(Csv::MissingIllnessCsvService).to receive(:new).with(organisation) + expect(missing_illness_csv_service).to receive(:create_illness_csv) + job.perform(organisation) + end + end +end diff --git a/spec/lib/tasks/correct_illness_from_csv_spec.rb b/spec/lib/tasks/correct_illness_from_csv_spec.rb new file mode 100644 index 000000000..4f4b1a95d --- /dev/null +++ b/spec/lib/tasks/correct_illness_from_csv_spec.rb @@ -0,0 +1,43 @@ +require "rails_helper" +require "rake" +RSpec.describe "correct_illness" do + describe ":create_illness_csv", type: :task do + subject(:task) { Rake::Task["correct_illness:create_illness_csv"] } + + before do + organisation.users.destroy_all + Rake.application.rake_require("tasks/correct_illness_from_csv") + Rake::Task.define_task(:environment) + task.reenable + end + + context "when the rake task is run" do + let(:organisation) { create(:organisation, name: "test organisation") } + + context "and organisation ID is provided" do + it "enqueues the job with correct organisation" do + expect { task.invoke(organisation.id) }.to enqueue_job(CreateIllnessCsvJob).with(organisation) + end + + it "prints out the jobs enqueued" do + expect(Rails.logger).to receive(:info).with(nil) + expect(Rails.logger).to receive(:info).with("Creating illness CSV for test organisation") + task.invoke(organisation.id) + end + end + + context "when organisation with given ID cannot be found" do + it "prints out error" do + expect(Rails.logger).to receive(:error).with("Organisation with ID fake not found") + task.invoke("fake") + end + end + + context "when organisation ID is not given" do + it "raises an error" do + expect { task.invoke }.to raise_error(RuntimeError, "Usage: rake correct_illness:create_illness_csv['organisation_id']") + end + end + end + end +end diff --git a/spec/services/csv/missing_illness_csv_service_spec.rb b/spec/services/csv/missing_illness_csv_service_spec.rb new file mode 100644 index 000000000..2132c245d --- /dev/null +++ b/spec/services/csv/missing_illness_csv_service_spec.rb @@ -0,0 +1,50 @@ +require "rails_helper" +RSpec.describe Csv::MissingIllnessCsvService do + let(:organisation) { create(:organisation, name: "Illness org") } + let(:user) { create(:user, organisation:, email: "testy@example.com") } + let(:service) { described_class.new(organisation) } + + def replace_entity_ids(lettings_log, export_template) + export_template.sub!(/\{id\}/, lettings_log.id.to_s) + end + + describe "#create_illness_csv" do + context "when the organisation has lettings logs" do + let!(:lettings_log) do + create(:lettings_log, + :setup_completed, + :with_illness_without_type, + tenancycode: "tenancycode1", + propcode: "propcode1", + startdate: Time.zone.local(2023, 4, 5), + created_by: user, + owning_organisation: organisation, + managing_organisation: organisation, + old_id: "old_id_1") + end + + before do + create(:lettings_log, :setup_completed, :with_illness_without_type, startdate: Time.zone.local(2023, 4, 5), owning_organisation: organisation, managing_organisation: organisation, created_by: user) + create(:lettings_log, :setup_completed, :with_illness_without_type, startdate: Time.zone.local(2023, 4, 5), old_id: "old_id_3") + create(:lettings_log, :setup_completed, illness: 0, startdate: Time.zone.local(2023, 4, 5), owning_organisation: organisation, managing_organisation: organisation, created_by: user, old_id: "old_id_4") + create(:lettings_log, :setup_completed, illness: 1, illness_type_1: true, startdate: Time.zone.local(2023, 4, 5), owning_organisation: organisation, managing_organisation: organisation, created_by: user, old_id: "old_id_5") + log = build(:lettings_log, :setup_completed, :with_illness_without_type, startdate: Time.zone.local(2021, 4, 5), owning_organisation: organisation, managing_organisation: organisation, created_by: user, old_id: "old_id_2") + log.save!(validate: false) + end + + it "returns a csv with relevant logs" do + illness_csv = replace_entity_ids(lettings_log, File.open("spec/fixtures/files/illness.csv").read) + csv = service.create_illness_csv + expect(csv).to eq(illness_csv) + end + end + + context "when the organisation does not have relevant lettings logs" do + it "returns only headers" do + illness_headers_only_csv = File.open("spec/fixtures/files/empty_illness.csv").read + csv = service.create_illness_csv + expect(csv).to eq(illness_headers_only_csv) + end + end + end +end