You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
385 lines
16 KiB
385 lines
16 KiB
module Csv |
|
class LettingsLogCsvService |
|
def initialize(user:, export_type:, year:) |
|
@user = user |
|
@export_type = export_type |
|
@year = year |
|
@attributes = lettings_log_attributes |
|
@definitions = lettings_log_definitions |
|
end |
|
|
|
def prepare_csv(logs) |
|
CSV.generate(headers: true) do |csv| |
|
if @year >= 2023 |
|
csv << @attributes.map do |attribute| |
|
record = @definitions.find { |r| r.variable == attribute.downcase } |
|
record&.tap { |r| r.update!(last_accessed: Time.zone.now) }&.definition |
|
end |
|
end |
|
csv << @attributes |
|
|
|
logs.find_each do |log| |
|
csv << @attributes.map { |attribute| value(attribute, log) } |
|
end |
|
end |
|
end |
|
|
|
private |
|
|
|
CUSTOM_CALL_CHAINS = { |
|
assigned_to: { |
|
labels: %i[assigned_to email], |
|
codes: %i[assigned_to email], |
|
}, |
|
created_by: { |
|
labels: %i[created_by email], |
|
codes: %i[created_by email], |
|
}, |
|
updated_by: { |
|
labels: %i[updated_by email], |
|
codes: %i[updated_by email], |
|
}, |
|
location_code: { |
|
labels: %i[location id], |
|
codes: %i[location id], |
|
}, |
|
location_postcode: { |
|
labels: %i[location postcode], |
|
codes: %i[location postcode], |
|
}, |
|
location_name: { |
|
labels: %i[location name], |
|
codes: %i[location name], |
|
}, |
|
location_units: { |
|
labels: %i[location units], |
|
codes: %i[location units], |
|
}, |
|
location_type_of_unit: { |
|
labels: %i[location type_of_unit], |
|
codes: %i[location type_of_unit_before_type_cast], |
|
}, |
|
location_mobility_type: { |
|
labels: %i[location mobility_type], |
|
codes: %i[location mobility_type_before_type_cast], |
|
}, |
|
location_local_authority: { |
|
labels: %i[location location_admin_district], |
|
codes: %i[location location_admin_district], |
|
}, |
|
location_startdate: { |
|
labels: %i[location startdate], |
|
codes: %i[location startdate], |
|
}, |
|
scheme_service_name: { |
|
labels: %i[scheme service_name], |
|
codes: %i[scheme service_name], |
|
}, |
|
scheme_confidential: { |
|
labels: %i[scheme sensitive], |
|
codes: %i[scheme sensitive_before_type_cast], |
|
}, |
|
SCHTYPE: { |
|
labels: %i[scheme scheme_type], |
|
codes: %i[scheme scheme_type_before_type_cast], |
|
}, |
|
scheme_registered_under_care_act: { |
|
labels: %i[scheme registered_under_care_act], |
|
codes: %i[scheme registered_under_care_act_before_type_cast], |
|
}, |
|
scheme_owning_organisation_name: { |
|
labels: %i[scheme owning_organisation name], |
|
codes: %i[scheme owning_organisation name], |
|
}, |
|
scheme_primary_client_group: { |
|
labels: %i[scheme primary_client_group], |
|
codes: %i[scheme primary_client_group_before_type_cast], |
|
}, |
|
scheme_has_other_client_group: { |
|
labels: %i[scheme has_other_client_group], |
|
codes: %i[scheme has_other_client_group_before_type_cast], |
|
}, |
|
scheme_secondary_client_group: { |
|
labels: %i[scheme secondary_client_group], |
|
codes: %i[scheme secondary_client_group_before_type_cast], |
|
}, |
|
scheme_support_type: { |
|
labels: %i[scheme support_type], |
|
codes: %i[scheme support_type_before_type_cast], |
|
}, |
|
scheme_intended_stay: { |
|
labels: %i[scheme intended_stay], |
|
codes: %i[scheme intended_stay_before_type_cast], |
|
}, |
|
scheme_created_at: { |
|
labels: %i[scheme created_at], |
|
codes: %i[scheme created_at], |
|
}, |
|
scheme_code: { |
|
labels: %i[scheme id_to_display], |
|
codes: %i[scheme id_to_display], |
|
}, |
|
creation_method: { |
|
labels: %i[creation_method], |
|
codes: %i[creation_method_before_type_cast], |
|
}, |
|
is_dpo: { |
|
labels: %i[assigned_to is_dpo?], |
|
codes: %i[assigned_to is_dpo?], |
|
}, |
|
renttype_detail: { |
|
labels: %i[renttype_detail], |
|
codes: %i[renttype_detail_code], |
|
}, |
|
}.freeze |
|
|
|
PERSON_DETAILS = {}.tap { |hash| |
|
hash["age1"] = { "refused_code" => "-9", "refused_label" => "Not known", "age_known_field" => "age1_known" } |
|
(2..8).each do |i| |
|
hash["age#{i}"] = { "refused_code" => "-9", "refused_label" => "Not known", "details_known_field" => "details_known_#{i}", "age_known_field" => "age#{i}_known" } |
|
hash["sex#{i}"] = { "refused_code" => "R", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" } |
|
hash["relat#{i}"] = { "refused_code" => "R", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" } |
|
hash["ecstat#{i}"] = { "refused_code" => "10", "refused_label" => "Prefers not to say", "details_known_field" => "details_known_#{i}" } |
|
end |
|
}.freeze |
|
|
|
FIELDS_ALWAYS_EXPORTED_AS_CODES = %w[ |
|
la |
|
prevloc |
|
].freeze |
|
|
|
FIELDS_ALWAYS_EXPORTED_AS_LABELS = { |
|
"la_label" => "la", |
|
"prevloc_label" => "prevloc", |
|
}.freeze |
|
|
|
SYSTEM_DATE_FIELDS = %w[ |
|
created_at |
|
updated_at |
|
].freeze |
|
|
|
USER_DATE_FIELDS = %w[ |
|
mrcdate |
|
startdate |
|
voiddate |
|
].freeze |
|
|
|
LETTYPE_LABELS = { |
|
1 => "Social rent general needs private registered provider", |
|
2 => "Social rent supported housing private registered provider", |
|
3 => "Social rent general needs local authority", |
|
4 => "Social rent supported housing local authority", |
|
5 => "Affordable rent general needs private registered provider", |
|
6 => "Affordable rent supported housing private registered provider", |
|
7 => "Affordable rent general needs local authority", |
|
8 => "Affordable rent supported housing local authority", |
|
9 => "Intermediate rent general needs private registered provider", |
|
10 => "Intermediate rent supported housing private registered provider", |
|
11 => "Intermediate rent general needs local authority", |
|
12 => "Intermediate rent supported housing local authority", |
|
}.freeze |
|
|
|
IRPRODUCT_LABELS = { |
|
1 => "Rent to Buy", |
|
2 => "London Living Rent", |
|
3 => "Other intermediate rent product", |
|
}.freeze |
|
|
|
LAR_LABELS = { |
|
1 => "Yes", |
|
2 => "No", |
|
3 => "Don't know", |
|
}.freeze |
|
|
|
NEWPROP_LABELS = { |
|
1 => "Yes", |
|
2 => "No", |
|
}.freeze |
|
|
|
INCREF_LABELS = { |
|
0 => "No", |
|
2 => "Yes", |
|
1 => "Prefers not to say", |
|
}.freeze |
|
|
|
RENTTYPE_LABELS = { |
|
1 => "Social Rent", |
|
2 => "Affordable Rent", |
|
3 => "Intermediate Rent", |
|
}.freeze |
|
|
|
LABELS = { |
|
"lettype" => LETTYPE_LABELS, |
|
"irproduct" => IRPRODUCT_LABELS, |
|
"lar" => LAR_LABELS, |
|
"newprop" => NEWPROP_LABELS, |
|
"incref" => INCREF_LABELS, |
|
"renttype" => RENTTYPE_LABELS, |
|
}.freeze |
|
|
|
CONVENTIONAL_YES_NO_ATTRIBUTES = %w[illness_type_1 illness_type_2 illness_type_3 illness_type_4 illness_type_5 illness_type_6 illness_type_7 illness_type_8 illness_type_9 illness_type_10 refused cbl cap chr accessible_register letting_allocation_none housingneeds_a housingneeds_b housingneeds_c housingneeds_d housingneeds_e housingneeds_f housingneeds_g housingneeds_h has_benefits nocharge postcode_known].freeze |
|
|
|
YES_OR_BLANK_ATTRIBUTES = %w[declaration rp_homeless rp_insan_unsat rp_medwel rp_hardship rp_dontknow].freeze |
|
|
|
ATTRIBUTE_MAPPINGS = { |
|
"owning_organisation_id" => %w[owning_organisation_name], |
|
"managing_organisation_id" => %w[managing_organisation_name], |
|
"assigned_to_id" => [], |
|
"scheme_id" => [], |
|
"location_id" => [], |
|
"rent_type" => %w[renttype renttype_detail], |
|
"hb" => %w[hb has_benefits], |
|
"age1" => %w[refused hhtype totchild totelder totadult age1], |
|
"housingneeds_type" => %w[housingneeds_type housingneeds_a housingneeds_b housingneeds_c housingneeds_f housingneeds_g housingneeds_h], |
|
"net_income_known" => %w[net_income_known incref], |
|
"irproduct_other" => %w[irproduct irproduct_other lar], |
|
"la" => %w[is_la_inferred la_label la], |
|
"prevloc" => %w[is_previous_la_inferred prevloc_label prevloc], |
|
"needstype" => %w[needstype lettype], |
|
"voiddate" => %w[voiddate vacdays], |
|
"rsnvac" => %w[rsnvac newprop], |
|
"household_charge" => %w[household_charge nocharge], |
|
"brent" => %w[brent wrent rent_value_check], |
|
"scharge" => %w[scharge wscharge], |
|
"pscharge" => %w[pscharge wpschrge], |
|
"supcharg" => %w[supcharg wsupchrg], |
|
"tcharge" => %w[tcharge wtcharge], |
|
"chcharge" => %w[chcharge wchchrg], |
|
"tshortfall" => %w[tshortfall wtshortfall], |
|
"letting_allocation_unknown" => %w[letting_allocation_none], |
|
}.freeze |
|
|
|
ORDERED_ADDRESS_FIELDS = %w[uprn address_line1 address_line2 town_or_city county postcode_full is_la_inferred la_label la uprn_known uprn_selection address_search_value_check address_line1_input postcode_full_input address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered].freeze |
|
|
|
SUPPORT_ONLY_ATTRIBUTES = %w[postcode_known is_la_inferred totchild totelder totadult net_income_known previous_la_known is_previous_la_inferred age1_known age2_known age3_known age4_known age5_known age6_known age7_known age8_known details_known_2 details_known_3 details_known_4 details_known_5 details_known_6 details_known_7 details_known_8 wrent wscharge wpschrge wsupchrg wtcharge wtshortfall old_form_id old_id tshortfall_known hhtype la prevloc updated_by_id uprn_confirmed address_line1_input postcode_full_input uprn_selection address_line1_as_entered address_line2_as_entered town_or_city_as_entered county_as_entered postcode_full_as_entered la_as_entered created_by].freeze |
|
|
|
SCHEME_AND_LOCATION_ATTRIBUTES = %w[scheme_code scheme_service_name scheme_confidential SCHTYPE scheme_registered_under_care_act scheme_owning_organisation_name scheme_primary_client_group scheme_has_other_client_group scheme_secondary_client_group scheme_support_type scheme_intended_stay scheme_created_at location_code location_postcode location_name location_units location_type_of_unit location_mobility_type location_local_authority location_startdate].freeze |
|
|
|
def lettings_log_attributes |
|
ordered_questions = FormHandler.instance.ordered_questions_for_year(@year, "lettings") |
|
soft_validations_attributes = soft_validations_attributes(ordered_questions) |
|
ordered_questions.reject! { |q| q.id.match?(/age\d_known|nationality_all_group|rent_value_check/) } |
|
attributes = insert_derived_and_related_attributes(ordered_questions) |
|
order_address_fields_for_support(attributes) |
|
final_attributes = non_question_fields + attributes + SCHEME_AND_LOCATION_ATTRIBUTES |
|
@user.support? ? final_attributes : final_attributes - SUPPORT_ONLY_ATTRIBUTES - soft_validations_attributes |
|
end |
|
|
|
def lettings_log_definitions |
|
CsvVariableDefinition.lettings.group_by { |record| [record.variable, record.definition] } |
|
.map do |_, options| |
|
exact_match = options.find { |definition| definition.year == @year } |
|
next exact_match if exact_match |
|
|
|
options.max_by(&:year) |
|
end |
|
end |
|
|
|
def insert_derived_and_related_attributes(ordered_questions) |
|
ordered_questions.flat_map do |question| |
|
if question.type == "checkbox" |
|
question.answer_options.keys.reject { |key| key == "divider" }.map { |key| |
|
ATTRIBUTE_MAPPINGS.fetch(key, key) |
|
}.flatten |
|
else |
|
ATTRIBUTE_MAPPINGS.fetch(question.id, question.id) |
|
end |
|
end |
|
end |
|
|
|
def order_address_fields_for_support(attributes) |
|
if @user.support? && @year >= 2024 |
|
first_address_field_index = attributes.find_index { |q| all_address_fields.include?(q) } |
|
if first_address_field_index |
|
attributes.reject! { |q| all_address_fields.include?(q) } |
|
attributes.insert(first_address_field_index, *ORDERED_ADDRESS_FIELDS) |
|
end |
|
end |
|
end |
|
|
|
def all_address_fields |
|
ORDERED_ADDRESS_FIELDS + %w[uprn_confirmed] |
|
end |
|
|
|
def non_question_fields |
|
case @year |
|
when 2022 |
|
%w[id status created_by assigned_to is_dpo created_at updated_by updated_at creation_method old_id old_form_id collection_start_year] |
|
when 2023 |
|
%w[id status duplicate_set_id created_by assigned_to is_dpo created_at updated_by updated_at creation_method old_id old_form_id collection_start_year] |
|
when 2024 |
|
%w[id status duplicate_set_id created_by assigned_to is_dpo created_at updated_by updated_at creation_method collection_start_year bulk_upload_id] |
|
else |
|
%w[id status duplicate_set_id created_by assigned_to is_dpo created_at updated_by updated_at creation_method collection_start_year bulk_upload_id] |
|
end |
|
end |
|
|
|
def soft_validations_attributes(ordered_questions) |
|
ordered_questions.select { |q| q.type == "interruption_screen" }.map(&:id) |
|
end |
|
|
|
def value(attribute, log) |
|
attribute = "rent_type" if attribute == "rent_type_detail" # rent_type_detail is the requested column header for rent_type, so as not to confuse with renttype. It can be exported as label or code. |
|
if CUSTOM_CALL_CHAINS.key? attribute.to_sym |
|
call_chain = CUSTOM_CALL_CHAINS[attribute.to_sym][@export_type.to_sym] |
|
call_chain.reduce(log) { |object, next_call| object&.public_send(next_call) } |
|
elsif FIELDS_ALWAYS_EXPORTED_AS_CODES.include? attribute |
|
log.public_send(attribute) |
|
elsif FIELDS_ALWAYS_EXPORTED_AS_LABELS.key? attribute |
|
attribute = FIELDS_ALWAYS_EXPORTED_AS_LABELS[attribute] |
|
value = log.public_send(attribute) |
|
get_label(value, attribute, log) |
|
elsif SYSTEM_DATE_FIELDS.include? attribute |
|
log.public_send(attribute)&.iso8601 |
|
elsif USER_DATE_FIELDS.include? attribute |
|
log.public_send(attribute)&.strftime("%F") |
|
elsif PERSON_DETAILS.any? { |key, _value| key == attribute } && (person_details_not_known?(log, attribute) || age_not_known?(log, attribute)) |
|
case @export_type |
|
when "codes" |
|
PERSON_DETAILS.find { |key, _value| key == attribute }[1]["refused_code"] |
|
when "labels" |
|
PERSON_DETAILS.find { |key, _value| key == attribute }[1]["refused_label"] |
|
end |
|
else |
|
value = log.public_send(attribute) |
|
case @export_type |
|
when "codes" |
|
value |
|
when "labels" |
|
answer_label = get_label(value, attribute, log) |
|
answer_label || label_if_boolean_value(value) || (YES_OR_BLANK_ATTRIBUTES.include?(attribute) && value != 1 ? nil : value) |
|
end |
|
end |
|
end |
|
|
|
def person_details_not_known?(log, attribute) |
|
details_known_field = PERSON_DETAILS.find { |key, _value| key == attribute }[1]["details_known_field"] |
|
log[details_known_field] == 1 # 1 for lettings logs, 2 for sales logs |
|
end |
|
|
|
def age_not_known?(log, attribute) |
|
age_known_field = PERSON_DETAILS.find { |key, _value| key == attribute }[1]["age_known_field"] |
|
log[age_known_field] == 1 |
|
end |
|
|
|
def get_label(value, attribute, log) |
|
return LABELS[attribute][value] if LABELS.key?(attribute) |
|
return conventional_yes_no_label(value) if CONVENTIONAL_YES_NO_ATTRIBUTES.include?(attribute) |
|
return "Yes" if YES_OR_BLANK_ATTRIBUTES.include?(attribute) && value == 1 |
|
|
|
log.form |
|
.get_question(attribute, log) |
|
&.label_from_value(value) |
|
end |
|
|
|
def label_if_boolean_value(value) |
|
return "Yes" if value == true |
|
return "No" if value == false |
|
end |
|
|
|
def conventional_yes_no_label(value) |
|
return "Yes" if value == 1 |
|
return "No" if value&.zero? |
|
end |
|
end |
|
end
|
|
|