diff --git a/lib/tasks/polls.rake b/lib/tasks/polls.rake index 2d7d2d064..2fb3a5adf 100644 --- a/lib/tasks/polls.rake +++ b/lib/tasks/polls.rake @@ -116,33 +116,54 @@ namespace :polls do logger.info "Removing duplicate partial results in polls" Tenant.run_on_each do - duplicate_ids = Poll::PartialResult.where(option_id: nil) - .select(:question_id, :booth_assignment_id, :date, :answer) - .group(:question_id, :booth_assignment_id, :date, :answer) - .having("count(*) > 1") - .pluck(:question_id, :booth_assignment_id, :date, :answer) + Poll::Question.find_each do |question| + manageable_titles = PollPartialResultOptionFinder.new(question).manageable_choices.keys - duplicate_ids.each do |question_id, booth_assignment_id, date, answer| - partial_results = Poll::PartialResult.where( - question_id: question_id, - booth_assignment_id: booth_assignment_id, - date: date, - answer: answer, - option_id: nil - ) + question.question_options.each do |option| + titles = option.translations.where(title: manageable_titles).select(:title).distinct - partial_results.excluding(partial_results.first).each do |partial_result| - partial_result.delete + groups = question.partial_results.where(option_id: nil, answer: titles) + .select(:booth_assignment_id, :date) + .group(:booth_assignment_id, :date) + .having("count(*) > 1") + .pluck(:booth_assignment_id, :date) - tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default? - log_message = "Deleted duplicate record with ID #{partial_result.id} " \ - "from the #{Poll::PartialResult.table_name} table " \ - "with question_id #{question_id}, " \ - "booth_assignment_id #{booth_assignment_id}, " \ - "date #{date} " \ - "and answer #{answer}" + tenant_info.to_s - logger.info(log_message) - duplicate_records_logger.info(log_message) + groups.each do |booth_assignment_id, date| + partial_results = question.partial_results.where( + option_id: nil, + booth_assignment_id: booth_assignment_id, + date: date, + answer: titles + ) + + tenant_info = " on tenant #{Tenant.current_schema}" unless Tenant.default? + + amounts_by_id = partial_results.pluck(:id, :amount).to_h + if amounts_by_id.values.uniq.size > 1 + log_message = "Found duplicate partial results with different amounts " \ + "for question_id #{question.id}, " \ + "booth_assignment_id #{booth_assignment_id} " \ + "and date #{date}. " \ + "Keeping ID #{partial_results.first.id} " \ + "with amount #{partial_results.first.amount}. " \ + "Deleting partial results with these IDs and amounts: " \ + "#{amounts_by_id.except(partial_results.first.id)}" + tenant_info.to_s + logger.info(log_message) + duplicate_records_logger.info(log_message) + end + + partial_results.excluding(partial_results.first).each do |partial_result| + partial_result.delete + + log_message = "Deleted duplicate record with ID #{partial_result.id} " \ + "from the #{Poll::PartialResult.table_name} table " \ + "with question_id #{question.id}, " \ + "booth_assignment_id #{booth_assignment_id} " \ + "and date #{date}" + tenant_info.to_s + logger.info(log_message) + duplicate_records_logger.info(log_message) + end + end end end end diff --git a/spec/lib/tasks/polls_spec.rb b/spec/lib/tasks/polls_spec.rb index 98ed48283..10ad6a93b 100644 --- a/spec/lib/tasks/polls_spec.rb +++ b/spec/lib/tasks/polls_spec.rb @@ -258,6 +258,60 @@ describe "polls tasks" do expect(Poll::PartialResult.all).to match_array [result, other_result, other_booth_result] end + it "removes duplicate partial results 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_partial_result, + question: question, + booth_assignment_id: booth_assignment.id, + date: Date.current, + answer: "Yes", + option: nil) + create(:poll_partial_result, + question: question, + booth_assignment_id: booth_assignment.id, + date: Date.current, + answer: "Ja", + option: nil) + + expect(Poll::PartialResult.count).to eq 2 + + Rake.application.invoke_task("polls:remove_duplicate_partial_results") + + expect(Poll::PartialResult.count).to eq 1 + end + + it "does not remove duplicate partial results when many options are possible" do + question = create(:poll_question, title: "How do you pronounce it?") + + 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_partial_result, + question: question, + booth_assignment_id: booth_assignment.id, + date: Date.current, + answer: "I", + option: nil) + create(:poll_partial_result, + question: question, + booth_assignment_id: booth_assignment.id, + date: Date.current, + answer: "AI", + option: nil) + + expect(Poll::PartialResult.count).to eq 2 + + Rake.application.invoke_task("polls:remove_duplicate_partial_results") + + expect(Poll::PartialResult.count).to eq 2 + end + it "does not remove partial results with the same text and different options" do question = create(:poll_question_multiple, :abcde, max_votes: 4) option_a = question.question_options.find_by(title: "Answer A") @@ -305,6 +359,34 @@ describe "polls tasks" do expect(Poll::PartialResult.count).to eq 1 end end + + it "removes duplicates in different languages even when amounts differ" do + question = create(:poll_question_multiple, max_votes: 2) + + create(:poll_question_option, question: question, title_en: "Yes", title_de: "Ja") + + create(:poll_partial_result, + question: question, + booth_assignment_id: booth_assignment.id, + date: Date.current, + answer: "Yes", + option: nil, + amount: 3) + + create(:poll_partial_result, + question: question, + booth_assignment_id: booth_assignment.id, + date: Date.current, + answer: "Ja", + option: nil, + amount: 5) + + expect(Poll::PartialResult.count).to eq 2 + + Rake.application.invoke_task("polls:remove_duplicate_partial_results") + + expect(Poll::PartialResult.count).to eq 1 + end end describe "polls:populate_partial_results_option_id" do @@ -381,6 +463,11 @@ describe "polls tasks" do it "removes duplicate partial results before populating the option_id column" do 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") + result_attributes = { question_id: question.id, booth_assignment_id: booth_assignment.id, @@ -391,14 +478,28 @@ describe "polls tasks" do result = create(:poll_partial_result, result_attributes) insert(:poll_partial_result, result_attributes) + localized_result_attributes = { + question: localized_question, + booth_assignment_id: booth_assignment.id, + date: Date.current, + option: nil + } + localized_result = create(:poll_partial_result, localized_result_attributes.merge(answer: "Yes")) + create(:poll_partial_result, localized_result_attributes.merge(answer: "Ja")) + result.reload + localized_result.reload + expect(result.option_id).to be nil + expect(localized_result.option_id).to be nil Rake.application.invoke_task("polls:populate_partial_results_option_id") result.reload + localized_result.reload - expect(Poll::PartialResult.count).to eq 1 + expect(Poll::PartialResult.count).to eq 2 expect(result.option_id).to eq question.question_options.find_by(title: "Answer A").id + expect(localized_result.option_id).to eq localized_question.question_options.find_by(title: "Yes").id end it "populates the option_id column on tenants" do