Merge pull request #5533 from consuldemocracy/age_stats

Calculate age stats based on the participation date
This commit is contained in:
Javi Martín
2024-05-17 16:05:59 +02:00
committed by GitHub
8 changed files with 180 additions and 22 deletions

View File

@@ -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

View File

@@ -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}",

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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) }

View File

@@ -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

View File

@@ -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