diff --git a/app/models/budget/stats.rb b/app/models/budget/stats.rb index 7db855aa9..4d62cd6de 100644 --- a/app/models/budget/stats.rb +++ b/app/models/budget/stats.rb @@ -37,6 +37,10 @@ class Budget::Stats budget.finished? end + def participation_date + send("#{phases.last}_phase_participation_date") + end + def total_participants participants.distinct.count end @@ -98,6 +102,14 @@ class Budget::Stats phases.map { |phase| self.class.send("#{phase}_phase_methods") }.flatten end + def support_phase_participation_date + budget.phases.selecting.ends_at + end + + def vote_phase_participation_date + budget.phases.balloting.ends_at + end + def participant_ids phases.map { |phase| send("participant_ids_#{phase}_phase") }.flatten.uniq end diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index 5d3c67a07..53533b4ed 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -63,7 +63,11 @@ module Statisticable end def age? - participants.between_ages(age_groups.flatten.min, age_groups.flatten.max).any? + participants.between_ages( + age_groups.flatten.min, + age_groups.flatten.max, + at_time: participation_date + ).any? end def geozone? @@ -96,7 +100,7 @@ module Statisticable def participants_by_age age_groups.to_h do |start, finish| - count = participants.between_ages(start, finish).count + count = participants.between_ages(start, finish, at_time: participation_date).count [ "#{start} - #{finish}", diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index facb27b6d..c449bf17d 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -21,6 +21,10 @@ class Poll::Stats total_participants_web + total_participants_booth end + def participation_date + poll.ends_at + end + def channels CHANNELS.select { |channel| send(:"total_participants_#{channel}") > 0 } end diff --git a/app/models/user.rb b/app/models/user.rb index e25162a43..5421b56ce 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -119,12 +119,8 @@ class User < ApplicationRecord search = "%#{search_string.strip}%" where("username ILIKE ? OR email ILIKE ? OR document_number ILIKE ?", search, search, search) 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 - ) + scope :between_ages, ->(from, to, at_time: Time.current) do + where(date_of_birth: (at_time - to.years).beginning_of_year..(at_time - from.years).end_of_year) end before_validation :clean_document_number diff --git a/spec/models/budget/stats_spec.rb b/spec/models/budget/stats_spec.rb index e61abf100..a8a97cc77 100644 --- a/spec/models/budget/stats_spec.rb +++ b/spec/models/budget/stats_spec.rb @@ -157,27 +157,79 @@ describe Budget::Stats do end describe "#participants_by_age" do - before do + it "returns the age groups hash" do [21, 22, 23, 23, 34, 42, 43, 44, 50, 51].each do |age| create(:user, date_of_birth: age.years.ago) end allow(stats).to receive(:participants).and_return(User.all) + + expect(stats.participants_by_age["16 - 19"][:count]).to eq 0 + expect(stats.participants_by_age["20 - 24"][:count]).to eq 4 + expect(stats.participants_by_age["25 - 29"][:count]).to eq 0 + expect(stats.participants_by_age["30 - 34"][:count]).to eq 1 + expect(stats.participants_by_age["35 - 39"][:count]).to eq 0 + expect(stats.participants_by_age["40 - 44"][:count]).to eq 3 + expect(stats.participants_by_age["45 - 49"][:count]).to eq 0 + expect(stats.participants_by_age["50 - 54"][:count]).to eq 2 + expect(stats.participants_by_age["55 - 59"][:count]).to eq 0 + expect(stats.participants_by_age["60 - 64"][:count]).to eq 0 + expect(stats.participants_by_age["65 - 69"][:count]).to eq 0 + expect(stats.participants_by_age["70 - 74"][:count]).to eq 0 end - it "returns the age groups hash" do - expect(stats.participants_by_age["16 - 19"][:count]).to be 0 - expect(stats.participants_by_age["20 - 24"][:count]).to be 4 - expect(stats.participants_by_age["25 - 29"][:count]).to be 0 - expect(stats.participants_by_age["30 - 34"][:count]).to be 1 - expect(stats.participants_by_age["35 - 39"][:count]).to be 0 - expect(stats.participants_by_age["40 - 44"][:count]).to be 3 - expect(stats.participants_by_age["45 - 49"][:count]).to be 0 - expect(stats.participants_by_age["50 - 54"][:count]).to be 2 - expect(stats.participants_by_age["55 - 59"][:count]).to be 0 - expect(stats.participants_by_age["60 - 64"][:count]).to be 0 - expect(stats.participants_by_age["65 - 69"][:count]).to be 0 - expect(stats.participants_by_age["70 - 74"][:count]).to be 0 + it "returns stats based on what happened when the voting took place" do + travel_to(50.years.ago) do + [21, 22, 23, 23, 34, 42, 43, 44, 50, 51].each do |age| + create(:user, date_of_birth: age.years.ago) + end + + create(:budget, :finished) + end + + stats = Budget::Stats.new(Budget.last) + allow(stats).to receive(:participants).and_return(User.all) + + expect(stats.participants_by_age["16 - 19"][:count]).to eq 0 + expect(stats.participants_by_age["20 - 24"][:count]).to eq 4 + expect(stats.participants_by_age["25 - 29"][:count]).to eq 0 + expect(stats.participants_by_age["30 - 34"][:count]).to eq 1 + expect(stats.participants_by_age["35 - 39"][:count]).to eq 0 + expect(stats.participants_by_age["40 - 44"][:count]).to eq 3 + expect(stats.participants_by_age["45 - 49"][:count]).to eq 0 + expect(stats.participants_by_age["50 - 54"][:count]).to eq 2 + expect(stats.participants_by_age["55 - 59"][:count]).to eq 0 + expect(stats.participants_by_age["60 - 64"][:count]).to eq 0 + expect(stats.participants_by_age["65 - 69"][:count]).to eq 0 + expect(stats.participants_by_age["70 - 74"][:count]).to eq 0 + end + end + + describe "#participation_date", :with_frozen_time do + let(:budget) do + create(:budget).tap do |budget| + budget.phases.informing.update!(starts_at: 10.months.ago, ends_at: 9.months.ago) + budget.phases.accepting.update!(starts_at: 9.months.ago, ends_at: 8.months.ago) + budget.phases.reviewing.update!(starts_at: 8.months.ago, ends_at: 7.months.ago) + budget.phases.selecting.update!(starts_at: 7.months.ago, ends_at: 6.months.ago) + budget.phases.valuating.update!(starts_at: 6.months.ago, ends_at: 5.months.ago) + budget.phases.publishing_prices.update!(starts_at: 5.months.ago, ends_at: 4.months.ago) + budget.phases.balloting.update!(starts_at: 4.months.ago, ends_at: 3.months.ago) + budget.phases.reviewing_ballots.update!(starts_at: 3.months.ago, ends_at: 2.months.ago) + budget.phases.finished.update!(starts_at: 2.months.ago, ends_at: 1.month.ago) + end + end + + it "returns the date when balloting ended on finished budgets" do + budget.update!(phase: "finished") + + expect(stats.participation_date).to eq 3.months.ago + end + + it "returns the date when selecting ended on unfinished budgets" do + budget.update!(phase: "reviewing_ballots") + + expect(stats.participation_date).to eq 6.months.ago end end diff --git a/spec/models/poll/stats_spec.rb b/spec/models/poll/stats_spec.rb index e9c93f1f7..ddb95a361 100644 --- a/spec/models/poll/stats_spec.rb +++ b/spec/models/poll/stats_spec.rb @@ -165,6 +165,46 @@ describe Poll::Stats do end end + describe "#participants_by_age" do + it "returns stats based on what happened when the voting took place" do + travel_to(100.years.ago) do + [16, 18, 32, 32, 33, 34, 64, 65, 71, 73, 90, 99, 105].each do |age| + create(:user, date_of_birth: age.years.ago) + end + + create(:poll, starts_at: 1.minute.from_now, ends_at: 2.minutes.from_now) + end + + stats = Poll::Stats.new(Poll.last) + allow(stats).to receive(:participants).and_return(User.all) + + expect(stats.participants_by_age["16 - 19"][:count]).to eq 2 + expect(stats.participants_by_age["20 - 24"][:count]).to eq 0 + expect(stats.participants_by_age["25 - 29"][:count]).to eq 0 + expect(stats.participants_by_age["30 - 34"][:count]).to eq 4 + expect(stats.participants_by_age["35 - 39"][:count]).to eq 0 + expect(stats.participants_by_age["40 - 44"][:count]).to eq 0 + expect(stats.participants_by_age["45 - 49"][:count]).to eq 0 + expect(stats.participants_by_age["50 - 54"][:count]).to eq 0 + expect(stats.participants_by_age["55 - 59"][:count]).to eq 0 + expect(stats.participants_by_age["60 - 64"][:count]).to eq 1 + expect(stats.participants_by_age["65 - 69"][:count]).to eq 1 + expect(stats.participants_by_age["70 - 74"][:count]).to eq 2 + expect(stats.participants_by_age["75 - 79"][:count]).to eq 0 + expect(stats.participants_by_age["80 - 84"][:count]).to eq 0 + expect(stats.participants_by_age["85 - 89"][:count]).to eq 0 + expect(stats.participants_by_age["90 - 300"][:count]).to eq 3 + end + end + + describe "#participation_date", :with_frozen_time do + let(:poll) { create(:poll, starts_at: 3.years.ago, ends_at: 2.years.ago) } + + it "returns the date when the poll finishes" do + expect(stats.participation_date).to eq 2.years.ago + end + end + describe "#participants_by_geozone" do it "groups by geozones in alphabetic order" do %w[Oceania Eurasia Eastasia].each { |name| create(:geozone, name: name) } diff --git a/spec/models/statisticable_spec.rb b/spec/models/statisticable_spec.rb index f9e4ffc9a..9768b39ee 100644 --- a/spec/models/statisticable_spec.rb +++ b/spec/models/statisticable_spec.rb @@ -8,6 +8,10 @@ describe Statisticable do def participants User.all end + + def participation_date + Time.current + end end stub_const("DummyStats", dummy_stats) @@ -80,6 +84,17 @@ describe Statisticable do expect(stats.age?).to be true end end + + context "Partipation took place a long time ago" do + before do + create(:user, date_of_birth: 2000.years.ago) + allow(stats).to receive(:participation_date).and_return(1900.years.ago) + end + + it "is true" do + expect(stats.age?).to be true + end + end end describe "#geozone?" do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index ed2bf468e..f3e59ffd6 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -423,6 +423,41 @@ describe User do expect(User.by_username_email_or_document_number(" 12345678Z ")).to eq [larry] end end + + describe ".between_ages" do + it "returns users between certain ages, including both" do + [21, 22, 23, 23, 42, 43, 44, 51].each do |age| + create(:user, date_of_birth: age.years.ago) + end + + expect(User.between_ages(0, 20).count).to eq 0 + expect(User.between_ages(0, 21).count).to eq 1 + expect(User.between_ages(21, 23).count).to eq 4 + expect(User.between_ages(24, 41).count).to eq 0 + expect(User.between_ages(41, 45).count).to eq 3 + expect(User.between_ages(51, 100).count).to eq 1 + end + + it "returns users between certain ages on a reference date" do + reference_date = 20.years.ago + + travel_to(reference_date) do + [21, 22, 23, 23, 34, 42, 43, 44, 50, 51].each do |age| + create(:user, date_of_birth: age.years.ago) + end + end + + expect(User.between_ages(0, 20, at_time: reference_date).count).to eq 0 + expect(User.between_ages(21, 25, at_time: reference_date).count).to eq 4 + expect(User.between_ages(25, 30, at_time: reference_date).count).to eq 0 + expect(User.between_ages(30, 34, at_time: reference_date).count).to eq 1 + expect(User.between_ages(35, 39, at_time: reference_date).count).to eq 0 + expect(User.between_ages(40, 44, at_time: reference_date).count).to eq 3 + expect(User.between_ages(45, 49, at_time: reference_date).count).to eq 0 + expect(User.between_ages(50, 54, at_time: reference_date).count).to eq 2 + expect(User.between_ages(55, 100, at_time: reference_date).count).to eq 0 + end + end end describe "self.search" do