diff --git a/app/components/polls/callout_component.html.erb b/app/components/polls/callout_component.html.erb index 16a0333c7..0db5062e3 100644 --- a/app/components/polls/callout_component.html.erb +++ b/app/components/polls/callout_component.html.erb @@ -2,7 +2,11 @@ <% if voted_in_booth? %> <%= callout(t("polls.show.already_voted_in_booth")) %> <% elsif voted_in_web? %> - <%= callout(t("polls.show.already_voted_in_web")) %> + <% if voted_blank? %> + <%= callout(t("polls.show.already_voted_blank_in_web")) %> + <% else %> + <%= callout(t("polls.show.already_voted_in_web")) %> + <% end %> <% end %> <% else %> <% if current_user.nil? %> diff --git a/app/components/polls/callout_component.rb b/app/components/polls/callout_component.rb index 61b87f492..aa1dbf2f9 100644 --- a/app/components/polls/callout_component.rb +++ b/app/components/polls/callout_component.rb @@ -16,6 +16,10 @@ class Polls::CalloutComponent < ApplicationComponent poll.voted_in_web?(current_user) end + def voted_blank? + poll.answers.where(author: current_user).none? + end + def callout(text, html_class: "warning") tag.div(text, class: "callout #{html_class}") end diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index f32332f3c..d09e15812 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -26,7 +26,11 @@ class PollsController < ApplicationController @web_vote = Poll::WebVote.new(@poll, current_user) if @web_vote.update(answer_params) - redirect_to @poll, notice: t("flash.actions.create.poll_voter") + if answer_params.blank? + redirect_to @poll, notice: t("flash.actions.create.poll_voter_blank") + else + redirect_to @poll, notice: t("flash.actions.create.poll_voter") + end else @comment_tree = CommentTree.new(@poll, params[:page], @current_order) render :show diff --git a/app/models/poll.rb b/app/models/poll.rb index 68c15face..8012f2fcd 100644 --- a/app/models/poll.rb +++ b/app/models/poll.rb @@ -23,6 +23,7 @@ class Poll < ApplicationRecord has_many :officer_assignments, through: :booth_assignments has_many :officers, through: :officer_assignments has_many :questions, inverse_of: :poll, dependent: :destroy + has_many :answers, through: :questions has_many :comments, as: :commentable, inverse_of: :commentable has_many :ballot_sheets diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index 8a0b1562b..5e03114b4 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -42,11 +42,11 @@ class Poll::Stats end def total_web_valid - voters.where(origin: "web").count - total_web_white + voters.where(origin: "web", user_id: poll.answers.select(:author_id).distinct).count end def total_web_white - 0 + voters.where(origin: "web").count - total_web_valid end def total_web_null diff --git a/app/models/poll/web_vote.rb b/app/models/poll/web_vote.rb index 2dba50fe5..669ad8f1e 100644 --- a/app/models/poll/web_vote.rb +++ b/app/models/poll/web_vote.rb @@ -14,10 +14,6 @@ class Poll::WebVote all_valid = true user.with_lock do - unless questions.any? { |question| params.dig(question.id.to_s, :option_id).present? } - Poll::Voter.find_by(user: user, poll: poll, origin: "web")&.destroy! - end - questions.each do |question| question.answers.where(author: user).destroy_all next unless params[question.id.to_s] diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index d48184f08..1298ca00a 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -604,6 +604,7 @@ en: show: already_voted_in_booth: "You have already participated in a physical booth. You can not participate again." already_voted_in_web: "You have already participated in this poll. If you vote again it will be overwritten." + already_voted_blank_in_web: "You have already participated in this poll by casting a blank vote. If you vote again it will be overwritten." back: Back to voting cant_answer_not_logged_in: "You must %{signin} or %{signup} to participate." comments_tab: Comments diff --git a/config/locales/en/responders.yml b/config/locales/en/responders.yml index dc5527d48..27db5c6cc 100644 --- a/config/locales/en/responders.yml +++ b/config/locales/en/responders.yml @@ -11,6 +11,7 @@ en: poll_question_option_video: "Video created successfully" poll_question_option_image: "Image uploaded successfully" poll_voter: "Thank you for voting!" + poll_voter_blank: "Thank you for voting! Your vote has been registered as a blank vote." proposal: "Proposal created successfully." proposal_notification: "Your message has been sent correctly." budget_investment: "Budget Investment created successfully." diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index 0ee5caf11..9cd99d0be 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -604,6 +604,7 @@ es: show: already_voted_in_booth: "Ya has participado en esta votación en urnas presenciales, no puedes volver a participar." already_voted_in_web: "Ya has participado en esta votación. Si vuelves a votar se sobreescribirá tu resultado anterior." + already_voted_blank_in_web: "Ya has participado en esta votación mediante un voto en blanco. Si vuelves a votar se sobreescribirá tu resultado anterior." back: Volver a votaciones cant_answer_not_logged_in: "Necesitas %{signin} o %{signup} para participar." comments_tab: Comentarios diff --git a/config/locales/es/responders.yml b/config/locales/es/responders.yml index 50fddc758..059804ad9 100644 --- a/config/locales/es/responders.yml +++ b/config/locales/es/responders.yml @@ -11,6 +11,7 @@ es: poll_question_option_video: "Vídeo creado correctamente" poll_question_option_image: "Imagen cargada correctamente" poll_voter: "¡Gracias por votar!" + poll_voter_blank: "¡Gracias por votar! Tu voto se ha contabilizado como en blanco." proposal: "Propuesta creada correctamente." proposal_notification: "Tu mensaje ha sido enviado correctamente." budget_investment: "Proyecto de gasto creado correctamente." diff --git a/spec/models/poll/stats_spec.rb b/spec/models/poll/stats_spec.rb index 7b962a2c0..0debf20e0 100644 --- a/spec/models/poll/stats_spec.rb +++ b/spec/models/poll/stats_spec.rb @@ -14,8 +14,6 @@ describe Poll::Stats do end describe "total participants" do - before { allow(stats).to receive(:total_web_white).and_return(1) } - it "supports every channel" do 3.times { create(:poll_voter, :from_web, poll: poll) } create(:poll_recount, :from_booth, poll: poll, @@ -49,15 +47,29 @@ describe Poll::Stats do end describe "#total_web_valid" do - before { allow(stats).to receive(:total_web_white).and_return(1) } + it "returns only votes containing answers" do + question = create(:poll_question, :yes_no, poll: poll) - it "returns only valid votes" do - 3.times { create(:poll_voter, :from_web, poll: poll) } + 2.times do + voter = create(:poll_voter, :from_web, poll: poll) + create(:poll_answer, author: voter.user, question: question) + end + create(:poll_voter, :from_web, poll: poll) expect(stats.total_web_valid).to eq(2) end end + describe "#total_web_white" do + it "returns voters with no answers" do + question = create(:poll_question, :yes_no, poll: poll) + 3.times { create(:poll_voter, :from_web, poll: poll) } + create(:poll_answer, author: poll.voters.last.user, question: question) + + expect(stats.total_web_white).to eq(2) + end + end + describe "#total_web_null" do it "returns 0" do expect(stats.total_web_null).to eq(0) @@ -93,8 +105,8 @@ describe Poll::Stats do describe "valid percentage by channel" do it "is relative to the total amount of valid votes" do + allow(stats).to receive(:total_web_valid).and_return(1) create(:poll_recount, :from_booth, poll: poll, total_amount: 2) - create(:poll_voter, :from_web, poll: poll) expect(stats.valid_percentage_web).to eq(33.333) expect(stats.valid_percentage_booth).to eq(66.667) @@ -123,7 +135,7 @@ describe Poll::Stats do describe "#total_valid_votes" do it "counts valid votes from every channel" do - 2.times { create(:poll_voter, :from_web, poll: poll) } + allow(stats).to receive(:total_web_valid).and_return(2) create(:poll_recount, :from_booth, poll: poll, total_amount: 3, white_amount: 10) create(:poll_recount, :from_booth, poll: poll, total_amount: 4, null_amount: 20) @@ -150,10 +162,9 @@ describe Poll::Stats do end describe "total percentage by type" do - before { allow(stats).to receive(:total_web_white).and_return(1) } + before { allow(stats).to receive_messages(total_web_white: 1, total_web_valid: 2) } it "is relative to the total amount of votes" do - 3.times { create(:poll_voter, :from_web, poll: poll) } create(:poll_recount, :from_booth, poll: poll, total_amount: 8, white_amount: 5, diff --git a/spec/models/poll/web_vote_spec.rb b/spec/models/poll/web_vote_spec.rb index 3b4b86eb1..caa6205bd 100644 --- a/spec/models/poll/web_vote_spec.rb +++ b/spec/models/poll/web_vote_spec.rb @@ -55,20 +55,21 @@ describe Poll::WebVote do expect(question.answers).to be_blank end - it "does not create voters or answers when leaving everything blank" do + it "creates a voter but does not create answers when leaving everything blank" do web_vote.update({}) - expect(poll.reload.voters.size).to eq 0 + expect(poll.reload.voters.size).to eq 1 expect(question.reload.answers.size).to eq 0 end - it "deletes existing answers and voter when no answers are given" do + it "deletes existing answers but keeps voters when no answers are given" do create(:poll_answer, question: question, author: user, option: option_yes) create(:poll_voter, poll: poll, user: user) web_vote.update({}) - expect(poll.reload.voters.size).to eq 0 + expect(poll.reload.voters.size).to eq 1 + expect(poll.voters.first.user).to eq user expect(question.reload.answers.size).to eq 0 end diff --git a/spec/system/polls/polls_spec.rb b/spec/system/polls/polls_spec.rb index 5d9c55e3d..c18cbbdd2 100644 --- a/spec/system/polls/polls_spec.rb +++ b/spec/system/polls/polls_spec.rb @@ -247,8 +247,9 @@ describe "Polls" do within_fieldset("Which ones are better?") { uncheck "Answer A" } click_button "Vote" - expect(page).to have_content "Thank you for voting!" - expect(page).not_to have_content "You have already participated" + expect(page).to have_content "Thank you for voting! Your vote has been registered as a blank vote." + expect(page).to have_content "You have already participated in this poll by casting a blank vote. " \ + "If you vote again it will be overwritten." within_fieldset("Which ones are better?") do expect(page).to have_field type: :checkbox, checked: false, count: 3