We were calculating the age stats based on the age of the users who participated... at the moment where we were calculating the stats. That means that, if 20 years ago, 1000 people who were 16 years old participated, they would be shown as having 36 years in the stats. Instead, we want to show the stats at the time when the process took place, so we're implementing a `participation_date` method. Note that, for polls, we could actually use the `age` column in the `poll_voters` table. However, doing so would be harder, would only work for polls but not for budgets, and it wouldn't be statistically very relevant, since the stats are shown by age groups, and only a small percentage of people would change their age group (and only to the nearest one) between the time they participate and the time the process ends. We might use the `poll_voters` table in the future, though, since we have a similar issue with geozones and genders, and using the information in `poll_voters` would solve it as well (only for polls, though). Also note that we're using the `ends_at` dates because some people but be too young to vote when a process starts but old enough to vote when the process ends. Finally, note that we might need to change the way we calculate the participation date for a budget, since some budgets might not enabled every phase. Not sure how stats work in that scenario (even before these changes).
327 lines
12 KiB
Ruby
327 lines
12 KiB
Ruby
require "rails_helper"
|
|
|
|
describe Budget::Stats do
|
|
let(:budget) { create(:budget, :finished) }
|
|
let(:stats) { Budget::Stats.new(budget) }
|
|
let(:investment) { create(:budget_investment, :selected, budget: budget) }
|
|
|
|
describe "#participants" do
|
|
let!(:author) { investment.author }
|
|
let!(:author_and_voter) { create(:user, :hidden, votables: [investment]) }
|
|
let!(:voter) { create(:user, votables: [investment]) }
|
|
let!(:voter_and_balloter) { create(:user, votables: [investment], ballot_lines: [investment]) }
|
|
let!(:balloter) { create(:user, ballot_lines: [investment]) }
|
|
let!(:poll_balloter) { create(:user, :level_two) }
|
|
let!(:non_participant) { create(:user, :level_two) }
|
|
|
|
before do
|
|
balloter.hide
|
|
|
|
create(:budget_investment, :selected, budget: budget, author: author_and_voter)
|
|
|
|
create(:poll_voter, :from_booth, user: poll_balloter, budget: budget)
|
|
|
|
create(:poll_voter, :from_booth, user: non_participant, budget: create(:budget))
|
|
end
|
|
|
|
it "returns unique participants, including authors and hidden users" do
|
|
expect(stats.participants).to match_array(
|
|
[author, author_and_voter, voter, voter_and_balloter, balloter, poll_balloter]
|
|
)
|
|
expect(stats.total_participants).to be 6
|
|
end
|
|
end
|
|
|
|
describe "#total_participants_support_phase" do
|
|
it "returns the number of total participants in the support phase" do
|
|
2.times { create(:vote, votable: investment) }
|
|
create(:budget_ballot_line, investment: investment)
|
|
|
|
expect(stats.total_participants_support_phase).to be 2
|
|
end
|
|
|
|
it "counts a user who is voter and balloter" do
|
|
create(:user, votables: [investment], ballot_lines: [investment])
|
|
|
|
expect(stats.total_participants_support_phase).to be 1
|
|
end
|
|
end
|
|
|
|
describe "#total_participants_vote_phase" do
|
|
it "returns the number of total participants in the votes phase" do
|
|
2.times { create(:budget_ballot_line, investment: investment) }
|
|
create(:vote, votable: investment)
|
|
|
|
expect(stats.total_participants_vote_phase).to be 2
|
|
end
|
|
|
|
it "counts a user who is voter and balloter" do
|
|
create(:user, votables: [investment], ballot_lines: [investment])
|
|
|
|
expect(stats.total_participants_vote_phase).to be 1
|
|
end
|
|
|
|
it "includes balloters and poll balloters" do
|
|
create(:budget_ballot_line, investment: investment)
|
|
create(:poll_voter, :from_booth, budget: budget)
|
|
|
|
expect(stats.total_participants_vote_phase).to be 2
|
|
end
|
|
|
|
it "counts once a user who is balloter and poll balloter" do
|
|
poller_and_balloter = create(:user, :level_two, ballot_lines: [investment])
|
|
create(:poll_voter, :from_booth, user: poller_and_balloter, budget: budget)
|
|
|
|
expect(stats.total_participants_vote_phase).to be 1
|
|
end
|
|
|
|
it "doesn't count nil user ids" do
|
|
create(:budget_ballot_line,
|
|
investment: investment,
|
|
ballot: create(:budget_ballot, budget: budget.reload, user: nil, physical: true))
|
|
|
|
expect(stats.total_participants_vote_phase).to be 0
|
|
end
|
|
end
|
|
|
|
describe "#total_budget_investments" do
|
|
it "returns the number of total budget investments" do
|
|
2.times { create(:budget_investment, budget: budget) }
|
|
create(:budget_investment, budget: create(:budget))
|
|
|
|
expect(stats.total_budget_investments).to be 2
|
|
end
|
|
end
|
|
|
|
describe "#total_votes" do
|
|
it "returns the number of total votes" do
|
|
create(:budget_ballot_line, investment: investment)
|
|
create(:budget_ballot_line, investment: create(:budget_investment, :selected, budget: budget))
|
|
|
|
expect(stats.total_votes).to be 2
|
|
end
|
|
end
|
|
|
|
describe "#total_selected_investments" do
|
|
it "returns the number of total selected investments" do
|
|
3.times { create(:budget_investment, :selected, budget: budget) }
|
|
create(:budget_investment, :selected, budget: create(:budget))
|
|
create(:budget_investment, :unfeasible, budget: budget)
|
|
|
|
expect(stats.total_selected_investments).to be 3
|
|
end
|
|
end
|
|
|
|
describe "#total_unfeasible_investments" do
|
|
it "returns the number of total unfeasible investments" do
|
|
3.times { create(:budget_investment, :unfeasible, budget: budget) }
|
|
create(:budget_investment, :unfeasible, budget: create(:budget))
|
|
create(:budget_investment, :selected, budget: budget)
|
|
|
|
expect(stats.total_unfeasible_investments).to be 3
|
|
end
|
|
end
|
|
|
|
describe "Participants by gender" do
|
|
before do
|
|
3.times { create(:user, gender: "male") }
|
|
2.times { create(:user, gender: "female") }
|
|
create(:user, gender: nil)
|
|
|
|
allow(stats).to receive(:participants).and_return(User.all)
|
|
end
|
|
|
|
describe "#total_male_participants" do
|
|
it "returns the number of total male participants" do
|
|
expect(stats.total_male_participants).to be 3
|
|
end
|
|
end
|
|
|
|
describe "#total_female_participants" do
|
|
it "returns the number of total female participants" do
|
|
expect(stats.total_female_participants).to be 2
|
|
end
|
|
end
|
|
|
|
describe "#male_percentage" do
|
|
it "returns the percentage of male participants" do
|
|
expect(stats.male_percentage).to be 60.0
|
|
end
|
|
end
|
|
|
|
describe "#female_percentage" do
|
|
it "returns the percentage of female participants" do
|
|
expect(stats.female_percentage).to be 40.0
|
|
end
|
|
end
|
|
end
|
|
|
|
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: 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 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
|
|
|
|
describe "#headings" do
|
|
before do
|
|
investment.heading.update_column(:population, 1234)
|
|
create(:budget_investment, heading: investment.heading)
|
|
2.times { create(:vote, votable: investment) }
|
|
create(:budget_ballot_line, investment: investment)
|
|
end
|
|
|
|
it "returns headings data" do
|
|
heading_stats = stats.headings[investment.heading.id]
|
|
expect(heading_stats[:total_investments_count]).to be 2
|
|
expect(heading_stats[:total_participants_support_phase]).to be 2
|
|
expect(heading_stats[:total_participants_vote_phase]).to be 1
|
|
expect(heading_stats[:total_participants_every_phase]).to be 3
|
|
expect(heading_stats[:percentage_participants_support_phase]).to be 100.0
|
|
expect(heading_stats[:percentage_district_population_support_phase]).to be 0.162
|
|
expect(heading_stats[:percentage_participants_vote_phase]).to be 100.0
|
|
expect(heading_stats[:percentage_district_population_vote_phase]).to be 0.081
|
|
expect(heading_stats[:percentage_participants_every_phase]).to be 100.0
|
|
expect(heading_stats[:percentage_district_population_every_phase]).to be 0.243
|
|
end
|
|
end
|
|
|
|
describe "#support_phase_finished?" do
|
|
context "support phase isn't finished" do
|
|
before { budget.phase = "selecting" }
|
|
|
|
it "is false" do
|
|
expect(stats.support_phase_finished?).to be false
|
|
end
|
|
end
|
|
|
|
context "support phase is finished" do
|
|
before { budget.phase = "valuating" }
|
|
|
|
it "is false" do
|
|
expect(stats.support_phase_finished?).to be true
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#vote_phase_finished" do
|
|
context "support phase isn't finished" do
|
|
before { budget.phase = "reviewing_ballots" }
|
|
|
|
it "is false" do
|
|
expect(stats.vote_phase_finished?).to be false
|
|
end
|
|
end
|
|
|
|
context "vote phase is finished" do
|
|
before { budget.phase = "finished" }
|
|
|
|
it "is false" do
|
|
expect(stats.vote_phase_finished?).to be true
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#all_phases" do
|
|
context "no phases are finished" do
|
|
before do
|
|
allow(stats).to receive_messages(support_phase_finished?: false, vote_phase_finished?: false)
|
|
end
|
|
|
|
it "returns an empty array" do
|
|
expect(stats.all_phases).to eq []
|
|
end
|
|
end
|
|
|
|
context "one phase is finished" do
|
|
before do
|
|
allow(stats).to receive_messages(support_phase_finished?: true, vote_phase_finished?: false)
|
|
end
|
|
|
|
it "returns the finished phase" do
|
|
expect(stats.all_phases).to eq ["support"]
|
|
end
|
|
end
|
|
|
|
context "all phases are finished" do
|
|
before do
|
|
allow(stats).to receive_messages(support_phase_finished?: true, vote_phase_finished?: true)
|
|
end
|
|
|
|
it "returns the finished phases and a total phase" do
|
|
expect(stats.all_phases).to eq ["support", "vote", "every"]
|
|
end
|
|
end
|
|
end
|
|
end
|