Submit social housing lettings and sales data (CORE)
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.
 
 
 
 

293 lines
11 KiB

module Csv
class SalesLogCsvService
def initialize(user:, export_type:, year:)
@user = user
@export_type = export_type
@year = year
@attributes = sales_log_attributes
@definitions = sales_log_definitions
end
def prepare_csv(logs)
CSV.generate(headers: true) do |csv|
formatted_attributes = formatted_attribute_headers
if @year >= 2023
csv << formatted_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 << formatted_attributes
logs.find_each do |log|
csv << @attributes.map { |attribute| value(attribute, log) }
end
end
end
private
CUSTOM_CALL_CHAINS = {
day: {
labels: %i[saledate day],
codes: %i[saledate day],
},
month: {
labels: %i[saledate month],
codes: %i[saledate month],
},
year: {
labels: %i[saledate year],
codes: %i[saledate year],
},
is_dpo: {
labels: %i[assigned_to is_dpo],
codes: %i[assigned_to is_dpo],
},
assigned_to: {
labels: %i[assigned_to email],
codes: %i[assigned_to email],
},
created_by: {
labels: %i[created_by email],
codes: %i[created_by email],
},
owning_organisation_name: {
labels: %i[owning_organisation name],
codes: %i[owning_organisation name],
},
managing_organisation_name: {
labels: %i[managing_organisation name],
codes: %i[managing_organisation name],
},
creation_method: {
labels: %i[creation_method],
codes: %i[creation_method_before_type_cast],
},
mscharge_value_check: {
labels: %i[monthly_charges_value_check],
codes: %i[monthly_charges_value_check],
},
}.freeze
PERSON_DETAILS = {}.tap { |hash|
hash["age1"] = { "refused_code" => "-9", "refused_label" => "Not known", "age_known_field" => "age1_known" }
(2..6).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
ORDERED_ADDRESS_FIELDS = %w[uprn address_line1 address_line2 town_or_city county postcode_full is_la_inferred la_label la 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[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 value_value_check mscharge_value_check].freeze
SUPPORT_ATTRIBUTE_NAME_MAPPINGS = {
"duplicate_set_id" => "DUPLICATESET",
"bulk_upload_id" => "BULKUPLOADID",
"created_at" => "CREATEDDATE",
"updated_at" => "UPLOADDATE",
"old_form_id" => "FORM",
"collection_start_year" => "COLLECTIONYEAR",
"creation_method" => "CREATIONMETHOD",
"is_dpo" => "DATAPROTECT",
"created_by" => "CREATEDBY",
"owning_organisation_name" => "OWNINGORGNAME",
"managing_organisation_name" => "MANINGORGNAME",
"assigned_to" => "USERNAME",
"ownershipsch" => "OWNERSHIP",
"companybuy" => "COMPANY",
"buylivein" => "LIVEINBUYER",
"jointpur" => "JOINT",
"address_line1" => "ADDRESS1",
"address_line2" => "ADDRESS2",
"town_or_city" => "TOWNCITY",
"postcode_full" => "POSTCODE",
"is_la_inferred" => "ISLAINFERRED",
"la_label" => "LANAME",
"uprn_selection" => "UPRNSELECTED",
"address_line1_input" => "ADDRESS1INPUT",
"postcode_full_input" => "POSTCODEINPUT",
"address_line1_as_entered" => "BULKADDRESS1",
"address_line2_as_entered" => "BULKADDRESS2",
"town_or_city_as_entered" => "BULKTOWNCITY",
"county_as_entered" => "BULKCOUNTY",
"postcode_full_as_entered" => "BULKPOSTCODE",
"la_as_entered" => "BULKLA",
"ethnic_group" => "ETHNICGROUP1",
"nationality_all" => "NATIONALITYALL1",
"buy1livein" => "LIVEINBUYER1",
"ethnic_group2" => "ETHNICGROUP2",
"ethnicbuy2" => "ETHNIC2",
"nationality_all_buyer2" => "NATIONALITYALL2",
"buy2livein" => "LIVEINBUYER2",
"hholdcount" => "HHTYPE",
"previous_la_known" => "PREVIOUSLAKNOWN",
"prevloc_label" => "PREVLOCNAME",
"prevtenbuy2" => "PREVTEN2",
"income1nk" => "INC1NK",
"income2nk" => "INC2NK",
"staircasesale" => "STAIRCASETOSALE",
"soctenant" => "SOCTEN",
"mortlen" => "MORTLEN1",
"has_mscharge" => "HASMSCHARGE",
"nationalbuy2" => "NATIONAL2",
"uprn_confirmed" => "UPRNCONFIRMED",
}.freeze
def formatted_attribute_headers
return @attributes unless @user.support?
@attributes.map do |attribute|
SUPPORT_ATTRIBUTE_NAME_MAPPINGS[attribute] || attribute.upcase
end
end
def sales_log_attributes
ordered_questions = FormHandler.instance.ordered_questions_for_year(@year, "sales")
ordered_questions.reject! { |q| q.id.match?(/((?<!la)_known)|(_check)|(_asked)|nationality_all_group|nationality_all_buyer2_group/) }
attributes = insert_derived_and_related_attributes(ordered_questions)
order_address_fields_for_support(attributes)
final_attributes = non_question_fields + attributes
@user.support? ? final_attributes : final_attributes - SUPPORT_ONLY_ATTRIBUTES
end
def sales_log_definitions
CsvVariableDefinition.sales.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
elsif attribute_mappings.key? question.id
attribute_mappings[question.id]
else
question.id
end
end
end
def attribute_mappings
mappings = {
"saledate" => %w[day month year],
"exdate" => %w[exday exmonth exyear],
"hodate" => %w[hoday homonth hoyear],
"ppostcode_full" => %w[ppostc1 ppostc2],
"la" => %w[la la_label],
"prevloc" => %w[prevloc prevloc_label],
"assigned_to_id" => %w[created_by assigned_to],
"owning_organisation_id" => %w[owning_organisation_name],
"managing_organisation_id" => %w[managing_organisation_name],
"value" => %w[value value_value_check],
"mscharge" => %w[mscharge mscharge_value_check],
}
unless @user.support? && @year >= 2024
mappings["postcode_full"] = %w[pcode1 pcode2]
end
mappings
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 non_question_fields
case @year
when 2022
%w[id status created_at updated_at old_form_id collection_start_year creation_method is_dpo]
when 2023
%w[id status duplicate_set_id created_at updated_at old_form_id collection_start_year creation_method is_dpo]
when 2024
%w[id status duplicate_set_id created_at updated_at collection_start_year creation_method bulk_upload_id is_dpo]
else
%w[id status duplicate_set_id created_at updated_at collection_start_year creation_method bulk_upload_id is_dpo]
end
end
def all_address_fields
ORDERED_ADDRESS_FIELDS + %w[uprn_confirmed]
end
def value(attribute, log)
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.send(attribute)
elsif FIELDS_ALWAYS_EXPORTED_AS_LABELS.key? attribute
attribute = FIELDS_ALWAYS_EXPORTED_AS_LABELS[attribute]
value = log.send(attribute)
get_label(value, attribute, log)
elsif SYSTEM_DATE_FIELDS.include? attribute
log.public_send(attribute)&.iso8601
elsif PERSON_DETAILS.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) || 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] == 2 # 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)
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
end
end