Group people by age instead of birth year in stats

When calculating the stats on, say, January 1st 2024, and using a group
age of, say, between 20 and 24 years, we were considering that everyone
born between 2000 and 2004 had between 20 and 24 years. This wasn't
accurate, since most people born in 2004 would have 19 years at that
point, and most people born in 1999 would have 24 years.
This commit is contained in:
Javi Martín
2024-06-04 19:10:35 +02:00
parent cca7465221
commit a86f584791
4 changed files with 81 additions and 4 deletions

View File

@@ -120,7 +120,10 @@ class User < ApplicationRecord
where("username ILIKE ? OR email ILIKE ? OR document_number ILIKE ?", search, search, search)
end
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)
start_date = at_time - (to + 1).years + 1.day
end_date = at_time - from.years
where(date_of_birth: start_date.beginning_of_day..end_date.end_of_day)
end
before_validation :clean_document_number

View File

@@ -159,7 +159,7 @@ describe Budget::Stats do
describe "#participants_by_age" 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: budget.phases.balloting.ends_at - age.years)
create(:user, date_of_birth: budget.phases.balloting.ends_at - age.years - rand(0..11).months)
end
allow(stats).to receive(:participants).and_return(User.all)
@@ -182,7 +182,7 @@ describe Budget::Stats do
budget = travel_to(50.years.ago) { create(:budget, :finished) }
[21, 22, 23, 23, 34, 42, 43, 44, 50, 51].each do |age|
create(:user, date_of_birth: budget.phases.balloting.ends_at - age.years)
create(:user, date_of_birth: budget.phases.balloting.ends_at - age.years - rand(0..11).months)
end
stats = Budget::Stats.new(budget)

View File

@@ -169,7 +169,7 @@ describe Poll::Stats 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)
create(:user, date_of_birth: age.years.ago - rand(0..11).months)
end
create(:poll, starts_at: 1.minute.from_now, ends_at: 2.minutes.from_now)

View File

@@ -438,6 +438,80 @@ describe User do
expect(User.between_ages(51, 100).count).to eq 1
end
it "does not consider that people born in the same year share the same age" do
april_1st_user = create(:user, date_of_birth: Time.zone.local(2001, 4, 1))
april_2nd_user = create(:user, date_of_birth: Time.zone.local(2001, 4, 2))
april_3rd_user = create(:user, date_of_birth: Time.zone.local(2001, 4, 3))
travel_to "2021-04-02" do
expect(User.between_ages(18, 19)).to eq [april_3rd_user]
expect(User.between_ages(20, 21)).to match_array [april_1st_user, april_2nd_user]
expect(User.between_ages(19, 20)).to match_array [april_1st_user, april_2nd_user, april_3rd_user]
end
end
it "works on leap years with users born on leap years" do
leap_day_user = create(:user, date_of_birth: Time.zone.local(2000, 2, 29))
march_1st_user = create(:user, date_of_birth: Time.zone.local(2000, 3, 1))
travel_to "2024-02-28" do
expect(User.between_ages(22, 23)).to match_array [leap_day_user, march_1st_user]
expect(User.between_ages(23, 24)).to match_array [leap_day_user, march_1st_user]
expect(User.between_ages(24, 25)).to eq []
end
travel_to "2024-02-29" do
expect(User.between_ages(22, 23)).to eq [march_1st_user]
expect(User.between_ages(23, 24)).to eq [leap_day_user, march_1st_user]
expect(User.between_ages(24, 25)).to eq [leap_day_user]
end
travel_to "2024-03-01" do
expect(User.between_ages(22, 23)).to eq []
expect(User.between_ages(23, 24)).to match_array [leap_day_user, march_1st_user]
expect(User.between_ages(24, 25)).to match_array [leap_day_user, march_1st_user]
end
end
it "works on non-leap years with users born on leap years" do
leap_day_user = create(:user, date_of_birth: Time.zone.local(2000, 2, 29))
march_1st_user = create(:user, date_of_birth: Time.zone.local(2000, 3, 1))
travel_to "2023-02-28" do
expect(User.between_ages(21, 22)).to match_array [leap_day_user, march_1st_user]
expect(User.between_ages(22, 23)).to match_array [leap_day_user, march_1st_user]
expect(User.between_ages(23, 24)).to eq []
end
travel_to "2023-03-01" do
expect(User.between_ages(21, 22)).to eq []
expect(User.between_ages(22, 23)).to match_array [leap_day_user, march_1st_user]
expect(User.between_ages(23, 24)).to match_array [leap_day_user, march_1st_user]
end
end
it "works on leap years with users born on non-leap years" do
march_1st_user = create(:user, date_of_birth: Time.zone.local(2001, 3, 1))
travel_to "2024-02-28" do
expect(User.between_ages(21, 22)).to eq [march_1st_user]
expect(User.between_ages(22, 23)).to eq [march_1st_user]
expect(User.between_ages(23, 24)).to eq []
end
travel_to "2024-02-29" do
expect(User.between_ages(21, 22)).to eq [march_1st_user]
expect(User.between_ages(22, 23)).to eq [march_1st_user]
expect(User.between_ages(23, 24)).to eq []
end
travel_to "2024-03-01" do
expect(User.between_ages(21, 22)).to eq []
expect(User.between_ages(22, 23)).to match_array [march_1st_user]
expect(User.between_ages(23, 24)).to match_array [march_1st_user]
end
end
it "returns users between certain ages on a reference date" do
reference_date = 20.years.ago