From 118cce2f8f60aa3d2990d2f86be6a02683c433fd Mon Sep 17 00:00:00 2001 From: Bertocq Date: Tue, 17 Oct 2017 23:13:21 +0200 Subject: [PATCH 1/9] Add letter origin back to poll voters --- app/models/poll/voter.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/models/poll/voter.rb b/app/models/poll/voter.rb index 522c28de2..c899cabc9 100644 --- a/app/models/poll/voter.rb +++ b/app/models/poll/voter.rb @@ -1,7 +1,7 @@ class Poll class Voter < ApplicationRecord - VALID_ORIGINS = %w{web booth}.freeze + VALID_ORIGINS = %w[web booth letter].freeze belongs_to :poll belongs_to :user @@ -20,8 +20,9 @@ class Poll before_validation :set_demographic_info, :set_document_info, :set_denormalized_booth_assignment_id - scope :web, -> { where(origin: "web") } - scope :booth, -> { where(origin: "booth") } + scope :web, -> { where(origin: "web") } + scope :booth, -> { where(origin: "booth") } + scope :letter, -> { where(origin: "letter") } def set_demographic_info return if user.blank? From 607b2362a377cf5b1a981bd0cf030091eb2a604f Mon Sep 17 00:00:00 2001 From: decabeza Date: Mon, 23 Oct 2017 16:37:24 +0200 Subject: [PATCH 2/9] adds missing id --- app/views/polls/show.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/polls/show.html.erb b/app/views/polls/show.html.erb index 2984e16d9..4c33f6b97 100644 --- a/app/views/polls/show.html.erb +++ b/app/views/polls/show.html.erb @@ -59,7 +59,7 @@ -
+
<% @poll_questions_answers.each do |answer| %> From dbe404d9ade2d36bad890f884f91a875d4e419f3 Mon Sep 17 00:00:00 2001 From: Bertocq Date: Mon, 23 Oct 2017 21:58:49 +0200 Subject: [PATCH 3/9] Fix Officer final recount access without voting shift --- app/controllers/officing/base_controller.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/controllers/officing/base_controller.rb b/app/controllers/officing/base_controller.rb index e73f2b696..96f185d24 100644 --- a/app/controllers/officing/base_controller.rb +++ b/app/controllers/officing/base_controller.rb @@ -20,10 +20,7 @@ class Officing::BaseController < ApplicationController end def load_officer_assignment - @officer_assignments ||= current_user.poll_officer. - officer_assignments. - voting_days. - where(date: Time.current.to_date) + @officer_assignments ||= current_user.poll_officer.officer_assignments.where(date: Time.current.to_date) end def verify_officer_assignment From ba9346162c659199ea3239b9806599d98671805b Mon Sep 17 00:00:00 2001 From: Bertocq Date: Mon, 23 Oct 2017 22:37:14 +0200 Subject: [PATCH 4/9] Improve officing result specs --- spec/features/officing/results_spec.rb | 67 +++++++++++++------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/spec/features/officing/results_spec.rb b/spec/features/officing/results_spec.rb index d4f9f3621..7ce642e8d 100644 --- a/spec/features/officing/results_spec.rb +++ b/spec/features/officing/results_spec.rb @@ -1,27 +1,27 @@ require "rails_helper" feature "Officing Results", :with_frozen_time do + let(:poll) { create(:poll, ends_at: 1.day.ago) } + let(:booth) { create(:poll_booth) } + let(:poll_officer) { create(:poll_officer) } background do - @poll_officer = create(:poll_officer) - @officer_assignment = create(:poll_officer_assignment, :final, officer: @poll_officer) - create(:poll_shift, officer: @poll_officer, booth: @officer_assignment.booth, date: Date.current) - @poll = @officer_assignment.booth_assignment.poll - @poll.update(ends_at: 1.day.ago) - @question_1 = create(:poll_question, poll: @poll) + create(:poll_booth_assignment, poll: poll, booth: booth) + create(:poll_shift, :recount_scrutiny_task, officer: poll_officer, booth: booth, date: Date.current) + @question_1 = create(:poll_question, poll: poll) create(:poll_question_answer, title: "Yes", question: @question_1, given_order: 1) create(:poll_question_answer, title: "No", question: @question_1, given_order: 2) - @question_2 = create(:poll_question, poll: @poll) + @question_2 = create(:poll_question, poll: poll) create(:poll_question_answer, title: "Today", question: @question_2, given_order: 1) create(:poll_question_answer, title: "Tomorrow", question: @question_2, given_order: 2) - login_as(@poll_officer.user) - set_officing_booth(@officer_assignment.booth) + login_as(poll_officer.user) + set_officing_booth(booth) end scenario "Only polls where user is officer for results are accessible" do - regular_officer_assignment_1 = create(:poll_officer_assignment, officer: @poll_officer) - regular_officer_assignment_2 = create(:poll_officer_assignment, officer: @poll_officer) + regular_officer_assignment_1 = create(:poll_officer_assignment, officer: poll_officer) + regular_officer_assignment_2 = create(:poll_officer_assignment, officer: poll_officer) not_allowed_poll_1 = create(:poll, :expired) not_allowed_poll_2 = regular_officer_assignment_1.booth_assignment.poll @@ -40,7 +40,7 @@ feature "Officing Results", :with_frozen_time do expect(page).not_to have_content(not_allowed_poll_1.name) expect(page).not_to have_content(not_allowed_poll_2.name) expect(page).not_to have_content(not_allowed_poll_3.name) - expect(page).to have_content(@poll.name) + expect(page).to have_content(poll.name) visit new_officing_poll_result_path(not_allowed_poll_1) expect(page).to have_content("You are not allowed to add results for this poll") @@ -53,15 +53,14 @@ feature "Officing Results", :with_frozen_time do click_link "Total recounts and results" end - within("#poll_#{@poll.id}") do - expect(page).to have_content(@poll.name) + within("#poll_#{poll.id}") do + expect(page).to have_content(poll.name) click_link "Add results" end expect(page).not_to have_content("Your results") - booth_name = @officer_assignment.booth_assignment.booth.name - select booth_name, from: "officer_assignment_id" + select booth.name, from: "officer_assignment_id" fill_in "questions[#{@question_1.id}][0]", with: "100" fill_in "questions[#{@question_1.id}][1]", with: "200" @@ -77,27 +76,27 @@ feature "Officing Results", :with_frozen_time do expect(page).to have_content("Your results") - within("#results_#{@officer_assignment.booth_assignment_id}_#{Date.current.strftime("%Y%m%d")}") do + within("#results_#{poll_officer.officer_assignments.first.booth_assignment_id}_#{Date.current.strftime("%Y%m%d")}") do expect(page).to have_content(I18n.l(Date.current, format: :long)) - expect(page).to have_content(booth_name) + expect(page).to have_content(booth.name) end end scenario "Edit result" do partial_result = create(:poll_partial_result, - officer_assignment: @officer_assignment, - booth_assignment: @officer_assignment.booth_assignment, + officer_assignment: poll_officer.officer_assignments.first, + booth_assignment: poll_officer.officer_assignments.first.booth_assignment, date: Date.current, question: @question_1, answer: @question_1.question_answers.first.title, - author: @poll_officer.user, + author: poll_officer.user, amount: 7777) - visit officing_poll_results_path(@poll, date: I18n.l(partial_result.date), booth_assignment_id: partial_result.booth_assignment_id) + visit officing_poll_results_path(poll, date: I18n.l(partial_result.date), booth_assignment_id: partial_result.booth_assignment_id) within("#question_#{@question_1.id}_0_result") { expect(page).to have_content("7777") } - visit new_officing_poll_result_path(@poll) + visit new_officing_poll_result_path(poll) booth_name = partial_result.booth_assignment.booth.name select booth_name, from: "officer_assignment_id" @@ -126,25 +125,25 @@ feature "Officing Results", :with_frozen_time do scenario "Index lists all questions and answers" do partial_result = create(:poll_partial_result, - officer_assignment: @officer_assignment, - booth_assignment: @officer_assignment.booth_assignment, - date: @poll.ends_at, + officer_assignment: poll_officer.officer_assignments.first, + booth_assignment: poll_officer.officer_assignments.first.booth_assignment, + date: poll.ends_at, question: @question_1, amount: 33) poll_recount = create(:poll_recount, - officer_assignment: @officer_assignment, - booth_assignment: @officer_assignment.booth_assignment, - date: @poll.ends_at, + officer_assignment: poll_officer.officer_assignments.first, + booth_assignment: poll_officer.officer_assignments.first.booth_assignment, + date: poll.ends_at, white_amount: 21, null_amount: 44, total_amount: 66) - visit officing_poll_results_path(@poll, - date: I18n.l(@poll.ends_at.to_date), - booth_assignment_id: @officer_assignment.booth_assignment_id) + visit officing_poll_results_path(poll, + date: I18n.l(poll.ends_at.to_date), + booth_assignment_id: poll_officer.officer_assignments.first.booth_assignment_id) - expect(page).to have_content(I18n.l(@poll.ends_at.to_date, format: :long)) - expect(page).to have_content(@officer_assignment.booth_assignment.booth.name) + expect(page).to have_content(I18n.l(poll.ends_at.to_date, format: :long)) + expect(page).to have_content(poll_officer.officer_assignments.first.booth_assignment.booth.name) expect(page).to have_content(@question_1.title) @question_1.question_answers.each_with_index do |answer, i| From f4260788eb808ea14ed12017452811c85ebdc8f1 Mon Sep 17 00:00:00 2001 From: Bertocq Date: Mon, 23 Oct 2017 23:51:55 +0200 Subject: [PATCH 5/9] Add Partial Results counts to question answers --- app/models/poll/question.rb | 2 +- app/models/poll/question/answer.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index 22a63168a..6ec328864 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -59,7 +59,7 @@ class Poll::Question < ApplicationRecord end def answers_total_votes - question_answers.map { |a| Poll::Answer.where(question_id: self, answer: a.title).count }.sum + question_answers.inject(0) { |total, question_answer| total + question_answer.total_votes } end end diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb index b2434aa3b..fa6ba013d 100644 --- a/app/models/poll/question/answer.rb +++ b/app/models/poll/question/answer.rb @@ -32,7 +32,8 @@ class Poll::Question::Answer < ApplicationRecord end def total_votes - Poll::Answer.where(question_id: question, answer: title).count + Poll::Answer.where(question_id: question, answer: title).count + + ::Poll::PartialResult.where(question: question).where(answer: title).sum(:amount) end def most_voted? From 4be44835a8f9be8dc165fd5d1d902fad32550f19 Mon Sep 17 00:00:00 2001 From: Bertocq Date: Tue, 24 Oct 2017 00:18:44 +0200 Subject: [PATCH 6/9] Add calculate_percentaje helper function to stats helper --- app/helpers/stats_helper.rb | 4 ++++ app/models/poll/stats.rb | 37 ++++++++++--------------------------- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/app/helpers/stats_helper.rb b/app/helpers/stats_helper.rb index 57db7166a..5dabd3f82 100644 --- a/app/helpers/stats_helper.rb +++ b/app/helpers/stats_helper.rb @@ -31,4 +31,8 @@ module StatsHelper content_tag :div, "", opt end + def calculate_percentage(fraction, total) + percent = fraction / total.to_f + percent.nan? ? 0.0 : (percent * 100).round(3) + end end diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index 7f12e2a56..54f617b8d 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -1,5 +1,6 @@ class Poll class Stats + include StatsHelper def initialize(poll) @poll = poll @@ -26,9 +27,7 @@ class Poll end def total_participants_web_percentage - stats_cache("total_participants_web_percentage") do - total_participants.zero? ? 0 : total_participants_web * 100 / total_participants - end + stats_cache("total_participants_web_percentage") { calculate_percentage(total_participants_web, total_participants) } end def total_participants_booth @@ -36,9 +35,7 @@ class Poll end def total_participants_booth_percentage - stats_cache("total_participants_booth_percentage") do - total_participants.zero? ? 0 : total_participants_booth * 100 / total_participants.to_f - end + stats_cache("total_participants_booth_percentage") { calculate_percentage(total_participants_booth, total_participants) } end def total_web_valid @@ -46,9 +43,7 @@ class Poll end def valid_percentage_web - stats_cache("valid_percentage_web") do - total_valid_votes.zero? ? 0 : total_web_valid * 100 / total_valid_votes.to_f - end + stats_cache("valid_percentage_web") { calculate_percentage(total_web_valid, total_valid_votes) } end def total_web_white @@ -72,9 +67,7 @@ class Poll end def valid_percentage_booth - stats_cache("valid_percentage_booth") do - total_valid_votes.zero? ? 0 : total_booth_valid * 100 / total_valid_votes.to_f - end + stats_cache("valid_percentage_booth") { calculate_percentage(total_booth_valid, total_valid_votes) } end def total_booth_white @@ -82,9 +75,7 @@ class Poll end def white_percentage_booth - stats_cache("white_percentage_booth") do - total_white_votes.zero? ? 0 : total_booth_white * 100 / total_white_votes.to_f - end + stats_cache("white_percentage_booth") { calculate_percentage(total_booth_white, total_white_votes) } end def total_booth_null @@ -92,9 +83,7 @@ class Poll end def null_percentage_booth - stats_cache("null_percentage_booth") do - total_null_votes.zero? ? 0 : total_booth_null * 100 / total_null_votes.to_f - end + stats_cache("null_percentage_booth") { calculate_percentage(total_booth_null, total_null_votes) } end def total_valid_votes @@ -102,9 +91,7 @@ class Poll end def total_valid_percentage - stats_cache("total_valid_percentage") do - total_participants.zero? ? 0 : total_valid_votes * 100 / total_participants.to_f - end + stats_cache("total_valid_percentage"){ calculate_percentage(total_valid_votes, total_participants) } end def total_white_votes @@ -112,9 +99,7 @@ class Poll end def total_white_percentage - stats_cache("total_white_percentage") do - total_participants.zero? ? 0 : total_white_votes * 100 / total_participants.to_f - end + stats_cache("total_white_percentage") { calculate_percentage(total_white_votes, total_participants) } end def total_null_votes @@ -122,9 +107,7 @@ class Poll end def total_null_percentage - stats_cache("total_null_percentage") do - total_participants.zero? ? 0 : total_null_votes * 100 / total_participants.to_f - end + stats_cache("total_null_percentage") { calculate_percentage(total_null_votes, total_participants) } end def voters From b2b5ab1d48c6e7323270b4023be4f641ca2913b7 Mon Sep 17 00:00:00 2001 From: Bertocq Date: Tue, 24 Oct 2017 00:33:54 +0200 Subject: [PATCH 7/9] Calculate white votes percentage of the total --- app/models/poll/stats.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index 54f617b8d..d7735456f 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -39,7 +39,7 @@ class Poll end def total_web_valid - stats_cache("total_web_valid") { voters.where(origin: "web").count } + stats_cache("total_web_valid") { voters.where(origin: "web").count - total_web_white } end def valid_percentage_web @@ -51,7 +51,7 @@ class Poll end def white_percentage_web - stats_cache("white_percentage_web") { 0 } + stats_cache("white_percentage_web") { calculate_percentage(total_web_white, total_white_votes) } end def total_web_null @@ -59,7 +59,7 @@ class Poll end def null_percentage_web - stats_cache("null_percentage_web") { 0 } + stats_cache("null_percentage_web") { calculate_percentage(total_web_null, total_null_votes) } end def total_booth_valid From 84eac6ff68d2f0af48669fd881f822fd2914162a Mon Sep 17 00:00:00 2001 From: Bertocq Date: Tue, 24 Oct 2017 01:39:57 +0200 Subject: [PATCH 8/9] Correct booth valid and total participants calculations --- app/models/poll/stats.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index d7735456f..fe9560c34 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -31,7 +31,7 @@ class Poll end def total_participants_booth - stats_cache("total_participants_booth") { voters.where(origin: "booth").count } + stats_cache("total_participants_booth") { total_booth_valid + total_booth_white + total_booth_null } end def total_participants_booth_percentage From b8dbdaf9a7a9c95d251fcddf435fc28f83bccbef Mon Sep 17 00:00:00 2001 From: Bertocq Date: Tue, 24 Oct 2017 14:57:37 +0200 Subject: [PATCH 9/9] Move question most voted answer from boolean to a enumerable max by total_votes --- app/controllers/polls/questions_controller.rb | 3 --- app/models/poll/question.rb | 3 +++ app/models/poll/question/answer.rb | 12 ------------ app/views/polls/results.html.erb | 7 ++++--- 4 files changed, 7 insertions(+), 18 deletions(-) diff --git a/app/controllers/polls/questions_controller.rb b/app/controllers/polls/questions_controller.rb index 6773282ef..eb054dd1e 100644 --- a/app/controllers/polls/questions_controller.rb +++ b/app/controllers/polls/questions_controller.rb @@ -13,9 +13,6 @@ class Polls::QuestionsController < ApplicationController answer.touch if answer.persisted? answer.save! answer.record_voter_participation(token) - @question.question_answers.where(question_id: @question).each do |question_answer| - question_answer.set_most_voted - end @answers_by_question_id = { @question.id => params[:answer] } end diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index 6ec328864..7395c1c03 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -62,4 +62,7 @@ class Poll::Question < ApplicationRecord question_answers.inject(0) { |total, question_answer| total + question_answer.total_votes } end + def most_voted_answer_id + question_answers.max_by { |answer| answer.total_votes }.id + end end diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb index fa6ba013d..cde5e00d1 100644 --- a/app/models/poll/question/answer.rb +++ b/app/models/poll/question/answer.rb @@ -36,19 +36,7 @@ class Poll::Question::Answer < ApplicationRecord ::Poll::PartialResult.where(question: question).where(answer: title).sum(:amount) end - def most_voted? - most_voted - end - def total_votes_percentage question.answers_total_votes.zero? ? 0 : (total_votes * 100.0) / question.answers_total_votes end - - def set_most_voted - answers = question.question_answers - .map { |a| Poll::Answer.where(question_id: a.question, answer: a.title).count } - is_most_voted = answers.none?{ |a| a > total_votes } - - update(most_voted: is_most_voted) - end end diff --git a/app/views/polls/results.html.erb b/app/views/polls/results.html.erb index cafb1c9a9..7627fe018 100644 --- a/app/views/polls/results.html.erb +++ b/app/views/polls/results.html.erb @@ -17,13 +17,14 @@
<%- @poll.questions.each do |question| %> + <% most_voted_answer_id = question.most_voted_answer_id %>

<%= question.title %>

<%- question.question_answers.each do |answer| %> - <%- question.question_answers.each do |answer| %> -
> - <% if answer.most_voted %> + > + <% if answer.id == most_voted_answer_id %> <%= t("polls.show.results.most_voted_answer") %> <% end %> <%= answer.title %> @@ -34,7 +35,7 @@
> + > <%= answer.total_votes %> (<%= answer.total_votes_percentage.round(2) %>%)