Delete duplicate records in different languages

This commit is contained in:
Javi Martín
2024-05-16 03:07:54 +02:00
parent 58f88d6805
commit 5dbd2ede14
3 changed files with 109 additions and 28 deletions

View File

@@ -0,0 +1,31 @@
class PollOptionFinder
attr_reader :question
def initialize(question)
@question = question
end
def manageable_choices
choices_map.select { |choice, ids| ids.count == 1 }
end
def unmanageable_choices
choices_map.reject { |choice, ids| ids.count == 1 }
end
private
def choices_map
@choices_map ||= existing_choices.to_h do |choice|
[choice, options.where("lower(title) = lower(?)", choice).distinct.ids]
end
end
def options
question.question_options.joins(:translations).reorder(:id)
end
def existing_choices
question.answers.where(option_id: nil).distinct.pluck(:answer)
end
end

View File

@@ -37,26 +37,35 @@ namespace :polls do
logger.info "Removing duplicate answers in polls"
Tenant.run_on_each do
duplicate_ids = Poll::Answer.where(option_id: nil)
.select(:question_id, :author_id, :answer)
.group(:question_id, :author_id, :answer)
.having("count(*) > 1")
.pluck(:question_id, :author_id, :answer)
Poll::Question.find_each do |question|
manageable_titles = PollOptionFinder.new(question).manageable_choices.keys
duplicate_ids.each do |question_id, author_id, answer|
poll_answers = Poll::Answer.where(question_id: question_id, author_id: author_id, answer: answer)
question.question_options.each do |option|
titles = option.translations.where(title: manageable_titles).select(:title).distinct
poll_answers.excluding(poll_answers.first).each do |poll_answer|
poll_answer.delete
author_ids = question.answers
.where(answer: titles)
.select(:author_id)
.group(:author_id)
.having("count(*) > 1")
.pluck(:author_id)
tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default?
log_message = "Deleted duplicate record with ID #{poll_answer.id} " \
"from the #{Poll::Answer.table_name} table " \
"with question_id #{question_id}, " \
"author_id #{author_id} " \
"and answer #{answer}" + tenant_info.to_s
logger.info(log_message)
duplicate_records_logger.info(log_message)
author_ids.each do |author_id|
poll_answers = question.answers.where(option_id: nil, answer: titles, author_id: author_id)
poll_answers.excluding(poll_answers.first).each do |poll_answer|
poll_answer.delete
tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default?
log_message = "Deleted duplicate record with ID #{poll_answer.id} " \
"from the #{Poll::Answer.table_name} table " \
"with question_id #{question.id}, " \
"author_id #{author_id} " \
"and answer #{poll_answer.answer}" + tenant_info.to_s
logger.info(log_message)
duplicate_records_logger.info(log_message)
end
end
end
end
end
@@ -76,20 +85,13 @@ namespace :polls do
end
questions.each do |question|
options = question.question_options.joins(:translations).reorder(:id)
existing_choices = question.answers.where(option_id: nil).distinct.pluck(:answer)
option_finder = PollOptionFinder.new(question)
choices_map = existing_choices.to_h do |choice|
[choice, options.where("lower(title) = lower(?)", choice).distinct.ids]
end
manageable_choices, unmanageable_choices = choices_map.partition { |choice, ids| ids.count == 1 }
manageable_choices.each do |choice, ids|
option_finder.manageable_choices.each do |choice, ids|
question.answers.where(option_id: nil, answer: choice).update_all(option_id: ids.first)
end
unmanageable_choices.each do |choice, ids|
option_finder.unmanageable_choices.each do |choice, ids|
tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default?
if ids.count == 0

View File

@@ -94,6 +94,40 @@ describe "polls tasks" do
expect(Poll::Answer.count).to eq 2
end
it "removes duplicate answers in different languages" do
question = create(:poll_question_multiple, max_votes: 2)
create(:poll_question_option, question: question, title_en: "Yes", title_de: "Ja")
create(:poll_question_option, question: question, title_en: "No", title_de: "Nein")
create(:poll_question_option, question: question, title_en: "Maybe", title_de: "Vielleicht")
create(:poll_answer, author: user, question: question, answer: "Yes", option: nil)
create(:poll_answer, author: user, question: question, answer: "Ja", option: nil)
expect(Poll::Answer.count).to eq 2
Rake.application.invoke_task("polls:remove_duplicate_answers")
expect(Poll::Answer.count).to eq 1
end
it "does not remove duplicate answers when many options are possible" do
question = create(:poll_question_multiple, title: "How do you pronounce it?", max_votes: 2)
create(:poll_question_option, question: question, title_en: "A", title_es: "EI")
create(:poll_question_option, question: question, title_en: "E", title_es: "I")
create(:poll_question_option, question: question, title_en: "I", title_es: "AI")
create(:poll_answer, question: question, author: user, answer: "I", option: nil)
create(:poll_answer, question: question, author: user, answer: "AI", option: nil)
expect(Poll::Answer.count).to eq 2
Rake.application.invoke_task("polls:remove_duplicate_answers")
expect(Poll::Answer.count).to eq 2
end
it "removes duplicate answers on tenants" do
create(:tenant, schema: "answers")
@@ -176,6 +210,11 @@ describe "polls tasks" do
user = create(:user, :level_two)
question = create(:poll_question_multiple, :abc)
localized_question = create(:poll_question_multiple)
create(:poll_question_option, question: localized_question, title_en: "Yes", title_de: "Ja")
create(:poll_question_option, question: localized_question, title_en: "No", title_de: "Nein")
create(:poll_question_option, question: localized_question, title_en: "Maybe", title_de: "Vielleicht")
answer_attributes = {
question_id: question.id,
author_id: user.id,
@@ -185,14 +224,23 @@ describe "polls tasks" do
answer = create(:poll_answer, answer_attributes)
insert(:poll_answer, answer_attributes)
localized_answer_attributes = { author: user, question: localized_question, option: nil }
localized_answer = create(:poll_answer, localized_answer_attributes.merge(answer: "Yes"))
create(:poll_answer, localized_answer_attributes.merge(answer: "Ja"))
answer.reload
localized_answer.reload
expect(answer.option_id).to be nil
expect(localized_answer.option_id).to be nil
Rake.application.invoke_task("polls:populate_option_id")
answer.reload
localized_answer.reload
expect(Poll::Answer.count).to eq 1
expect(Poll::Answer.count).to eq 2
expect(answer.option_id).to eq question.question_options.find_by(title: "Answer A").id
expect(localized_answer.option_id).to eq localized_question.question_options.find_by(title: "Yes").id
end
it "populates the option_id column on tenants" do