From 202fb440080c793e6d445ed1b7fec1300e39de04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 18:51:31 +0100 Subject: [PATCH] Add poll stats by age and channel --- app/models/concerns/statisticable.rb | 39 ++++++++++++++++++---------- app/models/poll/stats.rb | 9 ++++++- app/models/user.rb | 7 +++++ app/views/polls/stats.html.erb | 38 ++++++++++++++++++++++++++- config/locales/en/stats.yml | 1 + config/locales/es/stats.yml | 1 + spec/models/poll/stats_spec.rb | 39 ++++++++++++++++++++++++++++ 7 files changed, 118 insertions(+), 16 deletions(-) diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index a47826aa3..3360dc047 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -33,20 +33,7 @@ module Statisticable end def participants_by_age - age_groups.map do |start, finish| - users = participants.where("date_of_birth > ? AND date_of_birth < ?", - finish.years.ago.beginning_of_year, - start.years.ago.end_of_year) - - [ - "#{start} - #{finish}", - { - range: range_description(start, finish), - count: users.count, - percentage: calculate_percentage(users.count, total_participants) - } - ] - end.to_h + participants_by_age_for(participants) end def participants_by_geozone @@ -92,6 +79,30 @@ module Statisticable ] end + def participants_by_age_for(users, relative_to: :participants) + age_groups.map do |start, finish| + group_count = users.between_ages(start, finish).count + total_count = if relative_to == :participants + total_participants + else + send(relative_to, start, finish).count + end + + [ + "#{start} - #{finish}", + { + range: range_description(start, finish), + count: group_count, + percentage: calculate_percentage(group_count, total_count) + } + ] + end.to_h + end + + def participants_between_ages(from, to) + participants.between_ages(from, to) + end + def calculate_percentage(fraction, total) return 0.0 if total.zero? diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index dda2f74c1..b9938df64 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -18,9 +18,11 @@ class Poll::Stats total_male_web total_male_booth total_male_mail total_female_web total_female_booth total_female_mail male_web_percentage male_booth_percentage male_mail_percentage - female_web_percentage female_booth_percentage female_mail_percentage] + female_web_percentage female_booth_percentage female_mail_percentage + web_participants_by_age booth_participants_by_age mail_participants_by_age] end + def total_participants total_participants_web + total_participants_booth end @@ -40,6 +42,11 @@ class Poll::Stats User.where(id: voters.where(origin: channel).pluck(:user_id)) end + define_method :"#{channel}_participants_by_age" do + participants_by_age_for(send(:"#{channel}_participants"), + relative_to: :participants_between_ages) + end + %i[male female].each do |gender| define_method :"total_#{gender}_#{channel}" do send(:"#{channel}_participants").public_send(gender).count diff --git a/app/models/user.rb b/app/models/user.rb index 948cc4082..3f67b4748 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -72,6 +72,13 @@ class User < ApplicationRecord string = "%#{search_string}%" where("username ILIKE ? OR email ILIKE ? OR document_number ILIKE ?", string, string, string) end + scope :between_ages, -> (from, to) do + where( + "date_of_birth > ? AND date_of_birth < ?", + to.years.ago.beginning_of_year, + from.years.ago.end_of_year + ) + end before_validation :clean_document_number diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index 7fdbed498..f30dc7175 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -15,7 +15,10 @@ <%= link_to t("stats.polls.by_channel"), "#stats_by_channel" %>
  • - <%= link_to t("stats.polls.by_gender_and_channel"), "#stats_by_gender_and_channel" %> + <%= link_to t("stats.polls.by_gender_and_channel"), "#stats_by_gender_and_channel"%> +
  • +
  • + <%= link_to t("stats.polls.by_age_and_channel"), "#stats_by_age_and_channel" %>
  • <%= link_to t("stats.polls.vote_by_channel"), "#vote_stats_by_channel" %> @@ -71,6 +74,39 @@ +
    +

    <%= t("stats.polls.by_age_and_channel") %>

    + + + + + + <% Poll::Stats::CHANNELS.each do |channel| %> + + <% end %> + + + + <% @stats[:participants_by_age].keys.each do |age_range| %> + + + + <% Poll::Stats::CHANNELS.each do |channel| %> + <% group = @stats[:"#{channel}_participants_by_age"][age_range] %> + + <% end %> + + <% end %> + +
    <%= t("stats.age") %><%= t("stats.polls.#{channel}") %>
    <%= @stats[:participants_by_age][age_range][:range] %> +
    + + +
    + <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> +
    +
    +

    <%= t("stats.polls.vote_by_channel") %>

    diff --git a/config/locales/en/stats.yml b/config/locales/en/stats.yml index 03dd439bf..db3ce9f0e 100644 --- a/config/locales/en/stats.yml +++ b/config/locales/en/stats.yml @@ -19,6 +19,7 @@ en: polls: by_channel: "Participants by channel" by_gender_and_channel: "Participants by gender and channel" + by_age_and_channel: "Participants by age and channel" vote_by_channel: "Vote type by channel" web: "Web" booth: "Booths" diff --git a/config/locales/es/stats.yml b/config/locales/es/stats.yml index 2b23b1619..39cca0d47 100644 --- a/config/locales/es/stats.yml +++ b/config/locales/es/stats.yml @@ -19,6 +19,7 @@ es: polls: by_channel: "Participación por medio" by_gender_and_channel: "Participación por género y medio" + by_age_and_channel: "Participación por edad y medio" vote_by_channel: "Votos emitidos por medio" web: "Web" booth: "Urnas" diff --git a/spec/models/poll/stats_spec.rb b/spec/models/poll/stats_spec.rb index 3c3f6a434..898ee4091 100644 --- a/spec/models/poll/stats_spec.rb +++ b/spec/models/poll/stats_spec.rb @@ -221,6 +221,45 @@ describe Poll::Stats do expect(stats.male_booth_percentage).to eq(25.0) end end + + describe "participants by age and channel" do + before do + 4.times do + create :poll_voter, :from_web, poll: poll, + user: create(:user, :level_two, date_of_birth: 37.years.ago) + end + + 3.times do + create :poll_voter, :from_web, poll: poll, + user: create(:user, :level_two, date_of_birth: 52.years.ago) + end + + 2.times do + create :poll_voter, :from_booth, poll: poll, + user: create(:user, :level_two, date_of_birth: 37.years.ago) + end + + 1.times do + create :poll_voter, :from_booth, poll: poll, + user: create(:user, :level_two, date_of_birth: 52.years.ago) + end + end + + it "calculates the count of users by channel and age" do + expect(stats.web_participants_by_age["35 - 39"][:count]).to eq(4) + expect(stats.web_participants_by_age["50 - 54"][:count]).to eq(3) + expect(stats.booth_participants_by_age["35 - 39"][:count]).to eq(2) + expect(stats.booth_participants_by_age["50 - 54"][:count]).to eq(1) + end + + it "calculates percentage relative to the participants for that age" do + expect(stats.web_participants_by_age["35 - 39"][:percentage]).to eq(66.667) + expect(stats.booth_participants_by_age["35 - 39"][:percentage]).to eq(33.333) + + expect(stats.web_participants_by_age["50 - 54"][:percentage]).to eq(75.0) + expect(stats.booth_participants_by_age["50 - 54"][:percentage]).to eq(25.0) + end + end end describe "#generate" do