From b81ae68946e1abc9f3aa19b5ae7d8822e0510bf3 Mon Sep 17 00:00:00 2001 From: Phil Lee Date: Wed, 14 Dec 2022 11:39:48 +0000 Subject: [PATCH] able to view lettings bulk upload errors --- .../bulk_upload/lettings_validator.rb | 230 ++++++++++++++++++ .../bulk_upload/lettings_validator_spec.rb | 38 +++ 2 files changed, 268 insertions(+) create mode 100644 app/services/bulk_upload/lettings_validator.rb create mode 100644 spec/services/bulk_upload/lettings_validator_spec.rb diff --git a/app/services/bulk_upload/lettings_validator.rb b/app/services/bulk_upload/lettings_validator.rb new file mode 100644 index 000000000..7b1e64d66 --- /dev/null +++ b/app/services/bulk_upload/lettings_validator.rb @@ -0,0 +1,230 @@ +require "csv" + +class BulkUpload::LettingsValidator + include ActiveModel::Validations + + QUESTIONS = { + field_1: "What is the letting type?", + field_2: "This question has been removed", + field_3: "This question has been removed", + field_4: "Management group code", + field_5: "Scheme code", + field_6: "This question has been removed", + field_7: "What is the tenant code?", + field_8: "Is this a starter tenancy?", + field_9: "What is the tenancy type?", + field_10: "If 'Other', what is the tenancy type?", + field_11: "What is the length of the fixed-term tenancy to the nearest year?", + field_12: "Age of Person 1", + field_13: "Age of Person 2", + field_14: "Age of Person 3", + field_15: "Age of Person 4", + field_16: "Age of Person 5", + field_17: "Age of Person 6", + field_18: "Age of Person 7", + field_19: "Age of Person 8", + field_20: "Gender identity of Person 1", + field_21: "Gender identity of Person 2", + field_22: "Gender identity of Person 3", + field_23: "Gender identity of Person 4", + field_24: "Gender identity of Person 5", + field_25: "Gender identity of Person 6", + field_26: "Gender identity of Person 7", + field_27: "Gender identity of Person 8", + field_28: "Relationship to Person 1 for Person 2", + field_29: "Relationship to Person 1 for Person 3", + field_30: "Relationship to Person 1 for Person 4", + field_31: "Relationship to Person 1 for Person 5", + field_32: "Relationship to Person 1 for Person 6", + field_33: "Relationship to Person 1 for Person 7", + field_34: "Relationship to Person 1 for Person 8", + field_35: "Working situation of Person 1", + field_36: "Working situation of Person 2", + field_37: "Working situation of Person 3", + field_38: "Working situation of Person 4", + field_39: "Working situation of Person 5", + field_40: "Working situation of Person 6", + field_41: "Working situation of Person 7", + field_42: "Working situation of Person 8", + field_43: "What is the lead tenant's ethnic group?", + field_44: "What is the lead tenant's nationality?", + field_45: "Does anybody in the household have links to the UK armed forces?", + field_46: "Was the person seriously injured or ill as a result of serving in the UK armed forces?", + field_47: "Is anybody in the household pregnant?", + field_48: "Is the tenant likely to be receiving benefits related to housing?", + field_49: "How much of the household's income is from Universal Credit, state pensions or benefits?", + field_50: "How much income does the household have in total?", + field_51: "Do you know the household's income?", + field_52: "What is the tenant's main reason for the household leaving their last settled home?", + field_53: "If 'Other', what was the main reason for leaving their last settled home?", + field_54: "This question has been removed", + field_55: "Does anybody in the household have any disabled access needs?", + field_56: "Does anybody in the household have any disabled access needs?", + field_57: "Does anybody in the household have any disabled access needs?", + field_58: "Does anybody in the household have any disabled access needs?", + field_59: "Does anybody in the household have any disabled access needs?", + field_60: "Does anybody in the household have any disabled access needs?", + field_61: "Where was the household immediately before this letting?", + field_62: "What is the local authority of the household's last settled home?", + field_63: "Part 1 of postcode of last settled home", + field_64: "Part 2 of postcode of last settled home", + field_65: "Do you know the postcode of last settled home?", + field_66: "How long has the household continuously lived in the local authority area of the new letting?", + field_67: "How long has the household been on the waiting list for the new letting?", + field_68: "Was the tenant homeless directly before this tenancy?", + field_69: "Was the household given 'reasonable preference' by the local authority?", + field_70: "Reasonable preference. They were homeless or about to lose their home (within 56 days)", + field_71: "Reasonable preference. They were living in insanitary, overcrowded or unsatisfactory housing", + field_72: "Reasonable preference. They needed to move on medical and welfare grounds (including a disability)", + field_73: "Reasonable preference. They needed to move to avoid hardship to themselves or others", + field_74: "Reasonable preference. Don't know", + field_75: "Was the letting made under any of the following allocations systems?", + field_76: "Was the letting made under any of the following allocations systems?", + field_77: "Was the letting made under any of the following allocations systems?", + field_78: "What was the source of referral for this letting?", + field_79: "How often does the household pay rent and other charges?", + field_80: "What is the basic rent?", + field_81: "What is the service charge?", + field_82: "What is the personal service charge?", + field_83: "What is the support charge?", + field_84: "Total Charge", + field_85: "If this is a care home, how much does the household pay every [time period]?", + field_86: "Does the household pay rent or other charges for the accommodation?", + field_87: "After the household has received any housing-related benefits, will they still need to pay basic rent and other charges?", + field_88: "What do you expect the outstanding amount to be?", + field_89: "What is the void or renewal date?", + field_90: "What is the void or renewal date?", + field_91: "What is the void or renewal date?", + field_92: "What date were major repairs completed on?", + field_93: "What date were major repairs completed on?", + field_94: "What date were major repairs completed on?", + field_95: "This question has been removed", + field_96: "What date did the tenancy start?", + field_97: "What date did the tenancy start?", + field_98: "What date did the tenancy start?", + field_99: "Since becoming available, how many times has the property been previously offered?", + field_100: "What is the property reference?", + field_101: "How many bedrooms does the property have?", + field_102: "What type of unit is the property?", + field_103: "Which type of building is the property?", + field_104: "Is the property built or adapted to wheelchair-user standards?", + field_105: "What type was the property most recently let as?", + field_106: "What is the reason for the property being vacant?", + field_107: "What is the local authority of the property?", + field_108: "Part 1 of postcode of the property", + field_109: "Part 2 of postcode of the property", + field_110: "This question has been removed", + field_111: "Which organisation owns this property?", + field_112: "Username field", + field_113: "Which organisation manages this property?", + field_114: "Is the person still serving in the UK armed forces?", + field_115: "This question has been removed", + field_116: "How often does the household receive income?", + field_117: "Is this letting sheltered accommodation?", + field_118: "Does anybody in the household have a physical or mental health condition (or other illness) expected to last for 12 months or more?", + field_119: "Vision, for example blindness or partial sight", + field_120: "Hearing, for example deafness or partial hearing", + field_121: "Mobility, for example walking short distances or climbing stairs", + field_122: "Dexterity, for example lifting and carrying objects, using a keyboard", + field_123: "Learning or understanding or concentrating", + field_124: "Memory", + field_125: "Mental health", + field_126: "Stamina or breathing or fatigue", + field_127: "Socially or behaviourally, for example associated with autism spectral disorder (ASD) which includes Aspergers' or attention deficit hyperactivity disorder (ADHD)", + field_128: "Other", + field_129: "Is this letting a London Affordable Rent letting?", + field_130: "Which type of Intermediate Rent is this letting?", + field_131: "Which 'Other' type of Intermediate Rent is this letting?", + field_132: "Data Protection", + field_133: "Is this a joint tenancy?", + field_134: "Is this letting a renewal?", + }.freeze + + attr_reader :bulk_upload, :path + + validate :validate_file_not_empty + validate :validate_max_columns + + def initialize(bulk_upload:, path:) + @bulk_upload = bulk_upload + @path = path + end + + def call + row_parsers.each do |row_parser| + row_parser.valid? + + row_parser.errors.each do |error| + bulk_upload.bulk_upload_errors.create!(field: error.attribute, error: error.type) + end + end + end + + def self.question_for_field(field) + QUESTIONS[field] + end + +private + + def row_parsers + @row_parsers ||= body_rows.map do |row| + stripped_row = row[1..] + headers = ("field_1".."field_134").to_a + hash = Hash[headers.zip(stripped_row)] + + BulkUpload::Lettings::RowParser.new(hash) + end + end + + # determine the row seperator from CSV + # Windows will use \r\n + def row_sep + contents = "" + + File.open(path, "r") do |f| + f.seek(9900) + contents = f.read + end + + rn_count = contents.scan("\r\n").count + n_count = contents.scan(/[^\r]\n/).count + + if rn_count > n_count + "\r\n" + else + "\n" + end + end + + def rows + @rows ||= CSV.read(path, row_sep:) + end + + def body_rows + rows[6..] + end + + def validate_file_not_empty + if File.size(path).zero? + errors.add(:file, :blank) + + halt_validations! + end + end + + def validate_max_columns + return if halt_validations? + + max_row_size = rows.map(&:size).max + + errors.add(:file, :max_row_size) if max_row_size > 136 + end + + def halt_validations! + @halt_validations = true + end + + def halt_validations? + @halt_validations ||= false + end +end diff --git a/spec/services/bulk_upload/lettings_validator_spec.rb b/spec/services/bulk_upload/lettings_validator_spec.rb new file mode 100644 index 000000000..01a883ffe --- /dev/null +++ b/spec/services/bulk_upload/lettings_validator_spec.rb @@ -0,0 +1,38 @@ +require "rails_helper" + +RSpec.describe BulkUpload::LettingsValidator do + subject(:validator) { described_class.new(path:) } + + let(:path) { file.path } + let(:file) { Tempfile.new } + + describe "validations" do + context "when file is empty" do + it "is not valid" do + expect(validator).not_to be_valid + end + end + + context "when file has too many columns" do + before do + file.write("a," * 136) + file.write("\n") + file.rewind + end + + it "is not valid" do + expect(validator).not_to be_valid + end + end + + context "incorrect headers" + end + + context do + let(:path) { file_fixture("2021_22_lettings_bulk_upload.csv") } + + it do + validator.call + end + end +end