From c2489e320961a69ab6ef744b115a63a52863ef46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 12 Dec 2018 16:12:08 +0100 Subject: [PATCH 01/82] Increase number of age groups We would now like to differenciate between 70-year-old people and 90-year-old people. --- app/models/concerns/statisticable.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index 6548df8c6..28cdd92b6 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -51,9 +51,13 @@ module Statisticable "55 - 59", "60 - 64", "65 - 69", - "70 - 140"].each do |group| + "70 - 74", + "75 - 79", + "80 - 84", + "85 - 89", + "90 - 140"].each do |group| start, finish = group.split(" - ") - group_name = (group == "70 - 140" ? "+ 70" : group) + group_name = (group == "90 - 140" ? "+ 90" : group) groups[group_name] = User.where(id: participants) .where("date_of_birth > ? AND date_of_birth < ?", finish.to_i.years.ago.beginning_of_year, From c1b76a7ebfce16f44b2719864eb41e2896b79bc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 20 Dec 2018 13:12:03 +0100 Subject: [PATCH 02/82] Simplify age groups method --- app/models/concerns/statisticable.rb | 39 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index 28cdd92b6..f5a605b84 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -40,28 +40,27 @@ module Statisticable def age_groups groups = Hash.new(0) - ["16 - 19", - "20 - 24", - "25 - 29", - "30 - 34", - "35 - 39", - "40 - 44", - "45 - 49", - "50 - 54", - "55 - 59", - "60 - 64", - "65 - 69", - "70 - 74", - "75 - 79", - "80 - 84", - "85 - 89", - "90 - 140"].each do |group| - start, finish = group.split(" - ") - group_name = (group == "90 - 140" ? "+ 90" : group) + [[16, 19], + [20, 24], + [25, 29], + [30, 34], + [35, 39], + [40, 44], + [45, 49], + [50, 54], + [55, 59], + [60, 64], + [65, 69], + [70, 74], + [75, 79], + [80, 84], + [85, 89], + [90, 140]].each do |start, finish| + group_name = (finish == 140 ? "+ 90" : "#{start} - #{finish}") groups[group_name] = User.where(id: participants) .where("date_of_birth > ? AND date_of_birth < ?", - finish.to_i.years.ago.beginning_of_year, - start.to_i.years.ago.end_of_year).count + finish.years.ago.beginning_of_year, + start.years.ago.end_of_year).count end groups end From afa8bf8dbad8ad7fe2e9544e685f1e53c9a4c506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Wed, 12 Dec 2018 18:01:34 +0100 Subject: [PATCH 03/82] Add basic headings and navigaton styles for stats --- app/assets/stylesheets/application.scss | 1 + app/assets/stylesheets/stats.scss | 34 +++++++++++++++++++++++++ app/views/polls/_poll_header.html.erb | 2 +- app/views/polls/_poll_subnav.html.erb | 2 +- app/views/polls/stats.html.erb | 16 ++++++++---- 5 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 app/assets/stylesheets/stats.scss diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 85a9ed041..ad1a0dd0b 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -12,6 +12,7 @@ @import "legislation"; @import "legislation_process"; @import "community"; +@import "stats"; @import "custom"; @import "c3"; @import "annotator.min"; diff --git a/app/assets/stylesheets/stats.scss b/app/assets/stylesheets/stats.scss new file mode 100644 index 000000000..97004acb4 --- /dev/null +++ b/app/assets/stylesheets/stats.scss @@ -0,0 +1,34 @@ +.polls-results-stats { + + .sidebar { + border-right: 1px solid $border; + + > ul { + display: block; + font-size: rem-calc(20); + padding: 0; + + ul { + font-size: rem-calc(15); + list-style-type: none; + margin-left: 0.8rem; + margin-top: 1rem; + } + + li a { + color: $link; + padding: 0; + } + } + } + + h2 { + border-bottom: 1px solid $border; + } + + h3 { + font-size: rem-calc(20); + margin-bottom: 0; + margin-top: rem-calc(30); + } +} diff --git a/app/views/polls/_poll_header.html.erb b/app/views/polls/_poll_header.html.erb index 93f41f9d1..d241e455c 100644 --- a/app/views/polls/_poll_header.html.erb +++ b/app/views/polls/_poll_header.html.erb @@ -7,7 +7,7 @@ <%= link_to t("polls.poll_header.back_to_proposal"), [@poll.related] %> <% end %> -

<%= @poll.name %>

+

<%= @poll.name %>

<%= safe_html_with_links simple_format(@poll.summary) %> diff --git a/app/views/polls/_poll_subnav.html.erb b/app/views/polls/_poll_subnav.html.erb index e50d64e0a..1fec98bdb 100644 --- a/app/views/polls/_poll_subnav.html.erb +++ b/app/views/polls/_poll_subnav.html.erb @@ -15,7 +15,7 @@ <% if @poll.stats_enabled? %> <% if stats_menu? %>
  • -

    <%= t("polls.show.stats_menu") %>

    + <%= t("polls.show.stats_menu") %>
  • <% else %>
  • <%= link_to t("polls.show.stats_menu"), stats_poll_path(id: @poll.slug || @poll.id) %>
  • diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index d472ebc3d..98767e2b6 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -7,15 +7,21 @@
    -

    <%= t("polls.show.stats.total_participation") %>

    +

    <%= t("polls.show.stats.title") %>

    +

    <%= t("polls.show.stats.total_participation") %>

    <%= t("polls.show.stats.total_votes") %>
    From 5a3eb70a4622e32662a4a4ac7917ed41fc9cb74e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 13 Dec 2018 19:25:32 +0100 Subject: [PATCH 04/82] Use same translations for poll and budget stats --- app/views/budgets/stats/show.html.erb | 4 ++-- app/views/polls/stats.html.erb | 8 ++++---- config/i18n-tasks.yml | 1 + config/locales/en/budgets.yml | 2 -- config/locales/en/general.yml | 2 -- config/locales/en/stats.yml | 4 ++++ config/locales/es/budgets.yml | 2 -- config/locales/es/general.yml | 2 -- config/locales/es/stats.yml | 4 ++++ 9 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 config/locales/en/stats.yml create mode 100644 config/locales/es/stats.yml diff --git a/app/views/budgets/stats/show.html.erb b/app/views/budgets/stats/show.html.erb index 51c12e292..46c09d574 100644 --- a/app/views/budgets/stats/show.html.erb +++ b/app/views/budgets/stats/show.html.erb @@ -15,7 +15,7 @@

    <%= back_link_to budgets_path %>

    - <%= t("budgets.stats.title") %>
    + <%= t("stats.title") %>
    <%= @budget.name %>

    @@ -42,7 +42,7 @@
    - <%= t("budgets.stats.total_participants") %> + <%= t("stats.total_participants") %>

    <%= @stats[:total_participants] %>

    diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index 98767e2b6..d718f2c1b 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -9,10 +9,10 @@
    -

    <%= t("polls.show.stats.title") %>

    -

    <%= t("polls.show.stats.total_participation") %>

    +

    <%= t("stats.title") %>

    +

    <%= t("stats.total_participants") %>

    <%= t("polls.show.stats.total_votes") %>
    diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 251efb113..35d06d172 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -45,6 +45,7 @@ data: - config/locales/%{locale}/user_groups.yml - config/locales/%{locale}/i18n.yml - config/locales/%{locale}/milestones.yml + - config/locales/%{locale}/stats.yml # Locale files to write new keys to, based on a list of key pattern => file rules. Matched from top to bottom: # `i18n-tasks normalize -p` will force move the keys according to these rules diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index 8616539fc..b5039aea6 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -192,10 +192,8 @@ en: prev_phase_dates_invalid: "Start date must be later than the start date of the previous enabled phase (%{phase_name})" next_phase_dates_invalid: "End date must be earlier than the end date of the next enabled phase (%{phase_name})" stats: - title: Participation stats link: Stats page_title: "%{budget} - Participation stats" - total_participants: Total Participants total_budget_investments: Total Proposed Investments total_selected_investments: Proposals on final phase total_unfeasible_investments: Unfeasible proposals diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 5773c1c95..49a74c6e6 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -654,8 +654,6 @@ en: stats_menu: "Participation statistics" results_menu: "Poll results" stats: - title: "Participation data" - total_participation: "Total participation" total_votes: "Total amount of given votes" votes: "VOTES" web: "WEB" diff --git a/config/locales/en/stats.yml b/config/locales/en/stats.yml new file mode 100644 index 000000000..c1f7f1b23 --- /dev/null +++ b/config/locales/en/stats.yml @@ -0,0 +1,4 @@ +en: + stats: + title: "Participation data" + total_participants: "Participants" diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index cc849d1c6..eaa78c1d8 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -192,10 +192,8 @@ es: prev_phase_dates_invalid: "La fecha de inicio debe ser posterior a la fecha de inicio de la anterior fase habilitada (%{phase_name})" next_phase_dates_invalid: "La fecha de fin debe ser anterior a la fecha de fin de la siguiente fase habilitada (%{phase_name})" stats: - title: Estadísticas de participación link: Estadísticas page_title: "%{budget} - Estadísticas de participación" - total_participants: Total participantes total_budget_investments: Total propuestas enviadas total_selected_investments: Propuestas en la fase final total_unfeasible_investments: Propuestas inviables diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index 76125ee5a..377768318 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -654,8 +654,6 @@ es: stats_menu: "Estadísticas de participación" results_menu: "Resultados de la votación" stats: - title: "Datos de participación" - total_participation: "Participación total" total_votes: "Nº total de votos emitidos" votes: "VOTOS" web: "WEB" diff --git a/config/locales/es/stats.yml b/config/locales/es/stats.yml new file mode 100644 index 000000000..46c87d551 --- /dev/null +++ b/config/locales/es/stats.yml @@ -0,0 +1,4 @@ +es: + stats: + title: "Estadísticas de participación" + total_participants: "Participantes" From 80486b819396ca1c3ecbf7157613f43f003b1c51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 13 Dec 2018 19:27:47 +0100 Subject: [PATCH 05/82] Use section tags in stats This way we can easily see the h3 tag's parent is the h2 tag. --- app/views/polls/stats.html.erb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index d718f2c1b..e7513e3b6 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -20,13 +20,18 @@

    -

    <%= t("stats.title") %>

    -

    <%= t("stats.total_participants") %>

    +
    +

    <%= t("stats.title") %>

    -

    - <%= t("polls.show.stats.total_votes") %>
    - <%= @stats[:total_participants] %> -

    +
    +

    <%= t("stats.total_participants") %>

    + +

    + <%= t("polls.show.stats.total_votes") %>
    + <%= @stats[:total_participants] %> +

    +
    +
    From c0044f4e56ffc28add3d8d6556e800ea76569e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 20 Dec 2018 12:47:23 +0100 Subject: [PATCH 06/82] Extract method to show stats percentage --- app/helpers/stats_helper.rb | 4 ++++ app/views/budgets/stats/show.html.erb | 24 ++++++------------------ 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/app/helpers/stats_helper.rb b/app/helpers/stats_helper.rb index 1bacf1139..5fd42027e 100644 --- a/app/helpers/stats_helper.rb +++ b/app/helpers/stats_helper.rb @@ -30,4 +30,8 @@ module StatsHelper opt[:data][:graph] = admin_api_stats_path(budget_investments: true) content_tag :div, "", opt end + + def number_to_stats_percentage(number, options = {}) + number_to_percentage(number, { strip_insignificant_zeros: true, precision: 2 }.merge(options)) + end end diff --git a/app/views/budgets/stats/show.html.erb b/app/views/budgets/stats/show.html.erb index 46c09d574..c38852615 100644 --- a/app/views/budgets/stats/show.html.erb +++ b/app/views/budgets/stats/show.html.erb @@ -108,9 +108,7 @@

     <%= t("budgets.stats.total_female_participants").upcase %> - (<%= number_to_percentage(@stats[:female_percentage], - strip_insignificant_zeros: true, - precision: 2) %>) + (<%= number_to_stats_percentage(@stats[:female_percentage]) %>)

    <%= @stats[:total_female_participants] %> @@ -121,9 +119,7 @@

     <%= t("budgets.stats.total_male_participants").upcase %> - (<%= number_to_percentage(@stats[:male_percentage], - strip_insignificant_zeros: true, - precision: 2) %>) + (<%= number_to_stats_percentage(@stats[:male_percentage]) %>)

    <%= @stats[:total_male_participants] %> @@ -154,17 +150,13 @@ <% percentage_age_count = all_ages_count == 0 ? 0 : (count / all_ages_count * 100) - formatted_percentage_age_count = number_to_percentage(percentage_age_count, - strip_insignificant_zeros: true, - precision: 2) + formatted_percentage_age_count = number_to_stats_percentage(percentage_age_count) %> <%= count %> (<%= formatted_percentage_age_count %>)

    - +
    @@ -217,15 +209,11 @@
    <% end %> From be68c8cdbf638e8a2476c6d97a4ab8545664410f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 20 Dec 2018 11:56:38 +0100 Subject: [PATCH 07/82] Add gender stats to polls --- app/assets/stylesheets/participation.scss | 6 --- app/assets/stylesheets/stats.scss | 53 ++++++++++++++++++++++- app/models/poll/stats.rb | 16 ++++--- app/views/polls/stats.html.erb | 27 +++++++++++- config/locales/en/budgets.yml | 1 - config/locales/en/stats.yml | 3 ++ config/locales/es/budgets.yml | 1 - config/locales/es/stats.yml | 3 ++ 8 files changed, 92 insertions(+), 18 deletions(-) diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index 2e35b8256..11fe4b482 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -2030,10 +2030,4 @@ } } } - - .number { - font-size: rem-calc(60); - font-weight: bold; - line-height: rem-calc(60); - } } diff --git a/app/assets/stylesheets/stats.scss b/app/assets/stylesheets/stats.scss index 97004acb4..418e93def 100644 --- a/app/assets/stylesheets/stats.scss +++ b/app/assets/stylesheets/stats.scss @@ -26,9 +26,58 @@ border-bottom: 1px solid $border; } + section section { + clear: both; + margin-top: rem-calc(30); + margin-bottom: rem-calc(30); + overflow: hidden; + } + h3 { font-size: rem-calc(20); - margin-bottom: 0; - margin-top: rem-calc(30); + margin-bottom: rem-calc(12); + } + + .participants { + $background-width: 52px; + + float: left; + margin-left: $background-width; + margin-right: rem-calc(22); + position: relative; + + &::before { + // TODO: change image and move it to a non-custom folder + content: image-url("custom/polls/stats_man.png"); + left: -$background-width; + position: absolute; + } + + .number { + display: block; + font-size: rem-calc(60); + font-weight: bold; + line-height: rem-calc(60); + } + + .info { + color: $dark-gray; + font-style: italic; + } + + &.gender { + + &.male { + &::before { + content: image-url("custom/polls/stats_man.png"); + } + } + + &.female { + &::before { + content: image-url("custom/polls/stats_woman.png"); + } + } + } } } diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index ebba64b35..5a166457e 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -3,15 +3,19 @@ class Poll::Stats alias_method :poll, :resource def self.stats_methods - %i[total_participants total_participants_web total_web_valid total_web_white total_web_null - total_participants_booth total_booth_valid total_booth_white total_booth_null - total_valid_votes total_white_votes total_null_votes valid_percentage_web valid_percentage_booth - total_valid_percentage white_percentage_web white_percentage_booth total_white_percentage - null_percentage_web null_percentage_booth total_null_percentage total_participants_web_percentage - total_participants_booth_percentage] + super + + %i[total_participants_web total_web_valid total_web_white total_web_null + total_participants_booth total_booth_valid total_booth_white total_booth_null + total_valid_votes total_white_votes total_null_votes valid_percentage_web valid_percentage_booth + total_valid_percentage white_percentage_web white_percentage_booth total_white_percentage + null_percentage_web null_percentage_booth total_null_percentage total_participants_web_percentage + total_participants_booth_percentage] end private + def participants + User.where(id: voters.pluck(:user_id)) + end def total_participants total_participants_web + total_participants_booth diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index e7513e3b6..346653654 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -14,6 +14,9 @@
  • <%= link_to t("stats.total_participants"), "#total_participants" %>
  • +
  • + <%= link_to t("stats.by_gender"), "#participants_by_gender" %> +
  • @@ -26,9 +29,29 @@

    <%= t("stats.total_participants") %>

    -

    - <%= t("polls.show.stats.total_votes") %>
    +

    <%= @stats[:total_participants] %> + <%= t("polls.show.stats.total_votes") %>
    +

    +
    + +
    +

    <%= t("stats.by_gender") %>

    + +

    + <%= @stats[:total_male_participants] %> + + <%= I18n.t("stats.men_percentage", + percentage: number_to_stats_percentage(@stats[:male_percentage])) %> + +

    + +

    + <%= @stats[:total_female_participants] %> + + <%= I18n.t("stats.women_percentage", + percentage: number_to_stats_percentage(@stats[:female_percentage])) %> +

    diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index b5039aea6..e1757b7a8 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -202,7 +202,6 @@ en: total_participants_booths: Vote phase booth participants participants: Participants supports: Supports - by_gender: "Participants by gender" total_male_participants: Mens total_female_participants: Women by_age: "Participants by age groups" diff --git a/config/locales/en/stats.yml b/config/locales/en/stats.yml index c1f7f1b23..101419948 100644 --- a/config/locales/en/stats.yml +++ b/config/locales/en/stats.yml @@ -2,3 +2,6 @@ en: stats: title: "Participation data" total_participants: "Participants" + by_gender: "Participants by gender" + men_percentage: "%{percentage} Men" + women_percentage: "%{percentage} Women" diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index eaa78c1d8..7b5c25b8e 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -203,7 +203,6 @@ es: participants: Participantes supports: Apoyos total: Total - by_gender: "Participación por género" total_male_participants: Hombres total_female_participants: Mujeres by_age: "Participación por grupos de edad" diff --git a/config/locales/es/stats.yml b/config/locales/es/stats.yml index 46c87d551..3ef82915e 100644 --- a/config/locales/es/stats.yml +++ b/config/locales/es/stats.yml @@ -2,3 +2,6 @@ es: stats: title: "Estadísticas de participación" total_participants: "Participantes" + by_gender: "Participación por género" + men_percentage: "%{percentage} Hombres" + women_percentage: "%{percentage} Mujeres" From 9a01ff53234ccbab6e865cb8eb5b4b982568d5c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 20 Dec 2018 16:39:00 +0100 Subject: [PATCH 08/82] Refactor age groups method We try to make the method return data which is easier to handle in the view. --- app/models/concerns/statisticable.rb | 29 +++++++++++++++++++-------- app/views/budgets/stats/show.html.erb | 28 +++++++++----------------- config/locales/en/budgets.yml | 3 --- config/locales/en/stats.yml | 5 +++++ config/locales/es/budgets.yml | 3 --- config/locales/es/stats.yml | 5 +++++ spec/models/budget/stats_spec.rb | 24 +++++++++++----------- 7 files changed, 52 insertions(+), 45 deletions(-) diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index f5a605b84..5ff689641 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -39,7 +39,6 @@ module Statisticable end def age_groups - groups = Hash.new(0) [[16, 19], [20, 24], [25, 29], @@ -55,14 +54,20 @@ module Statisticable [75, 79], [80, 84], [85, 89], - [90, 140]].each do |start, finish| - group_name = (finish == 140 ? "+ 90" : "#{start} - #{finish}") - groups[group_name] = User.where(id: participants) - .where("date_of_birth > ? AND date_of_birth < ?", - finish.years.ago.beginning_of_year, - start.years.ago.end_of_year).count + [90, 300]].reduce({}) do |groups, (start, finish)| + users = User.where(id: participants) + .where("date_of_birth > ? AND date_of_birth < ?", + finish.years.ago.beginning_of_year, + start.years.ago.end_of_year) + + groups.tap do |age_groups| + age_groups["#{start} - #{finish}"] = { + range: range_description(start, finish), + count: users.count, + percentage: calculate_percentage(users.count, total_participants) + } + end end - groups end def calculate_percentage(fraction, total) @@ -70,6 +75,14 @@ module Statisticable (fraction * 100.0 / total).round(3) end + + def range_description(start, finish) + if finish > 200 + I18n.t("stats.age_more_than", start: start) + else + I18n.t("stats.age_range", start: start, finish: finish) + end + end end class_methods do diff --git a/app/views/budgets/stats/show.html.erb b/app/views/budgets/stats/show.html.erb index c38852615..5c55be1dd 100644 --- a/app/views/budgets/stats/show.html.erb +++ b/app/views/budgets/stats/show.html.erb @@ -96,7 +96,7 @@
    -

    <%= t("budgets.stats.by_gender") %>

    +

    <%= t("stats.by_gender") %>

    @@ -130,7 +130,7 @@
    -

    <%= t("budgets.stats.by_age") %>

    +

    <%= t("stats.by_age") %>

    - <%= number_to_percentage(@stats[:headings][heading.id]["percentage_participants_#{phase}_phase".to_sym], - strip_insignificant_zeros: true, - precision: 2) %> + <%= number_to_stats_percentage(@stats[:headings][heading.id]["percentage_participants_#{phase}_phase".to_sym]) %> - <%= number_to_percentage(@stats[:headings][heading.id]["percentage_district_population_#{phase}_phase".to_sym], - strip_insignificant_zeros: true, - precision: 2) %> + <%= number_to_stats_percentage(@stats[:headings][heading.id]["percentage_district_population_#{phase}_phase".to_sym]) %>
    @@ -140,23 +140,13 @@ - <% all_ages_count = @stats[:age_groups].values.sum.to_f %> - <% @stats[:age_groups].each do |age_group, count| %> - "> - - + + diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml index e1757b7a8..4aa814def 100644 --- a/config/locales/en/budgets.yml +++ b/config/locales/en/budgets.yml @@ -204,11 +204,8 @@ en: supports: Supports total_male_participants: Mens total_female_participants: Women - by_age: "Participants by age groups" age: Age total: Total - more_than: More than - years: years by_heading: "Participants by heading" heading: Heading investments_sent_html: "Investment proposals sent" diff --git a/config/locales/en/stats.yml b/config/locales/en/stats.yml index 101419948..947e87b11 100644 --- a/config/locales/en/stats.yml +++ b/config/locales/en/stats.yml @@ -3,5 +3,10 @@ en: title: "Participation data" total_participants: "Participants" by_gender: "Participants by gender" + by_age: "Participants by age" men_percentage: "%{percentage} Men" women_percentage: "%{percentage} Women" + age: "Age" + age_more_than: "%{start} years old and older" + age_range: "%{start} - %{finish} years old" + total: "Total" diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml index 7b5c25b8e..67599b725 100644 --- a/config/locales/es/budgets.yml +++ b/config/locales/es/budgets.yml @@ -205,10 +205,7 @@ es: total: Total total_male_participants: Hombres total_female_participants: Mujeres - by_age: "Participación por grupos de edad" age: Edad - more_than: Más de - years: años by_heading: "Participación por distritos" heading: Distrito investments_sent_html: "Propuestas
    enviadas" diff --git a/config/locales/es/stats.yml b/config/locales/es/stats.yml index 3ef82915e..926a024f8 100644 --- a/config/locales/es/stats.yml +++ b/config/locales/es/stats.yml @@ -3,5 +3,10 @@ es: title: "Estadísticas de participación" total_participants: "Participantes" by_gender: "Participación por género" + by_age: "Participación por grupos de edad" men_percentage: "%{percentage} Hombres" women_percentage: "%{percentage} Mujeres" + age: "Edad" + age_more_than: "De %{start} y más años" + age_range: "De %{start} a %{finish} años" + total: "Total" diff --git a/spec/models/budget/stats_spec.rb b/spec/models/budget/stats_spec.rb index 7beafb4e0..aa0a836de 100644 --- a/spec/models/budget/stats_spec.rb +++ b/spec/models/budget/stats_spec.rb @@ -151,18 +151,18 @@ describe Budget::Stats do context "#age_groups" do it "returns the age groups hash" do - expect(@stats[:age_groups]["16 - 19"]).to be 0 - expect(@stats[:age_groups]["20 - 24"]).to be 7 - expect(@stats[:age_groups]["25 - 29"]).to be 1 - expect(@stats[:age_groups]["30 - 34"]).to be 0 - expect(@stats[:age_groups]["35 - 39"]).to be 1 - expect(@stats[:age_groups]["40 - 44"]).to be 1 - expect(@stats[:age_groups]["45 - 49"]).to be 0 - expect(@stats[:age_groups]["50 - 54"]).to be 1 - expect(@stats[:age_groups]["55 - 59"]).to be 0 - expect(@stats[:age_groups]["60 - 64"]).to be 0 - expect(@stats[:age_groups]["65 - 69"]).to be 0 - expect(@stats[:age_groups]["70 - 140"]).to be 0 + expect(@stats[:age_groups]["16 - 19"][:count]).to be 0 + expect(@stats[:age_groups]["20 - 24"][:count]).to be 7 + expect(@stats[:age_groups]["25 - 29"][:count]).to be 1 + expect(@stats[:age_groups]["30 - 34"][:count]).to be 0 + expect(@stats[:age_groups]["35 - 39"][:count]).to be 1 + expect(@stats[:age_groups]["40 - 44"][:count]).to be 1 + expect(@stats[:age_groups]["45 - 49"][:count]).to be 0 + expect(@stats[:age_groups]["50 - 54"][:count]).to be 1 + expect(@stats[:age_groups]["55 - 59"][:count]).to be 0 + expect(@stats[:age_groups]["60 - 64"][:count]).to be 0 + expect(@stats[:age_groups]["65 - 69"][:count]).to be 0 + expect(@stats[:age_groups]["70 - 74"][:count]).to be 0 end end From 1a1ca1352561b8567b2e1141e43064a2a333d5d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 20 Dec 2018 16:39:16 +0100 Subject: [PATCH 09/82] Add age stats to polls --- app/views/polls/stats.html.erb | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index 346653654..f1da5ae07 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -17,6 +17,9 @@
  • <%= link_to t("stats.by_gender"), "#participants_by_gender" %>
  • +
  • + <%= link_to t("stats.by_age"), "#participants_by_age" %> +
  • @@ -54,6 +57,32 @@

    + +
    +

    <%= t("stats.by_age") %>

    + +
    - <%= age_group.gsub("+", t("budgets.stats.more_than")) + " " + t("budgets.stats.years") %> - - - <% - percentage_age_count = all_ages_count == 0 ? 0 : (count / all_ages_count * 100) - formatted_percentage_age_count = number_to_stats_percentage(percentage_age_count) - %> - <%= count %> - (<%= formatted_percentage_age_count %>) - -
    - + <% @stats[:age_groups].values.each do |group| %> +
    <%= group[:range] %> + <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> +
    +
    + + + + + + + <% @stats[:age_groups].values.each do |group| %> + + + + + <% end %> + +
    <%= t("stats.age") %><%= t("stats.total") %>
    <%= group[:range] %> + <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> + +
    + +
    +
    + From 57a29455900ea6821e567dc868f37d6d4523a8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 20 Dec 2018 16:50:09 +0100 Subject: [PATCH 10/82] Use `let` for stats in budget stats spec The rest of the `before` block still uses instance variables, but at least the rest of the file doesn't use instance variables anymore. --- spec/models/budget/stats_spec.rb | 62 ++++++++++++++++---------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/spec/models/budget/stats_spec.rb b/spec/models/budget/stats_spec.rb index aa0a836de..3ff9dfa9f 100644 --- a/spec/models/budget/stats_spec.rb +++ b/spec/models/budget/stats_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" describe Budget::Stats do - before(:each) do + before do @budget = create(:budget) @group = create(:budget_group, budget: @budget) @heading = create(:budget_heading, group: @group, price: 1000) @@ -40,14 +40,14 @@ describe Budget::Stats do @budget_ballot4 = create(:budget_ballot, budget: @budget, physical: true, user: nil) @budget_ballot_line4 = create(:budget_ballot_line, ballot: @budget_ballot4, investment: @investment4) - - @stats = Budget::Stats.new(@budget).generate end + let(:stats) { Budget::Stats.new(@budget).generate } + context "#total_participants" do it "returns the number of total participants" do - expect(@stats[:total_participants]).to be 11 + expect(stats[:total_participants]).to be 11 end end @@ -55,7 +55,7 @@ describe Budget::Stats do context "#total_participants_support_phase" do it "returns the number of total participants in the support phase" do - expect(@stats[:total_participants_support_phase]).to be 2 + expect(stats[:total_participants_support_phase]).to be 2 end end @@ -63,7 +63,7 @@ describe Budget::Stats do context "#total_participants_vote_phase" do it "returns the number of total participants in the votes phase" do - expect(@stats[:total_participants_vote_phase]).to be 4 + expect(stats[:total_participants_vote_phase]).to be 4 end end @@ -71,7 +71,7 @@ describe Budget::Stats do context "#total_participants_web" do it "returns the number of total participants in the votes phase via web" do - expect(@stats[:total_participants_web]).to be 3 + expect(stats[:total_participants_web]).to be 3 end end @@ -79,7 +79,7 @@ describe Budget::Stats do context "#total_participants_booths" do it "returns the number of total participants in the votes phase in booths" do - expect(@stats[:total_participants_booths]).to be 1 + expect(stats[:total_participants_booths]).to be 1 end end @@ -87,7 +87,7 @@ describe Budget::Stats do context "#total_budget_investments" do it "returns the number of total budget investments" do - expect(@stats[:total_budget_investments]).to be 5 + expect(stats[:total_budget_investments]).to be 5 end end @@ -95,7 +95,7 @@ describe Budget::Stats do context "#total_votes" do it "returns the number of total votes" do - expect(@stats[:total_votes]).to be 4 + expect(stats[:total_votes]).to be 4 end end @@ -103,7 +103,7 @@ describe Budget::Stats do context "#total_selected_investments" do it "returns the number of total selected investments" do - expect(@stats[:total_selected_investments]).to be 4 + expect(stats[:total_selected_investments]).to be 4 end end @@ -111,7 +111,7 @@ describe Budget::Stats do context "#total_unfeasible_investments" do it "returns the number of total unfeasible investments" do - expect(@stats[:total_unfeasible_investments]).to be 1 + expect(stats[:total_unfeasible_investments]).to be 1 end end @@ -119,7 +119,7 @@ describe Budget::Stats do context "#total_male_participants" do it "returns the number of total male participants" do - expect(@stats[:total_male_participants]).to be 4 + expect(stats[:total_male_participants]).to be 4 end end @@ -127,7 +127,7 @@ describe Budget::Stats do context "#total_female_participants" do it "returns the number of total female participants" do - expect(@stats[:total_female_participants]).to be 6 + expect(stats[:total_female_participants]).to be 6 end end @@ -135,7 +135,7 @@ describe Budget::Stats do context "#total_supports" do it "returns the number of total supports" do - expect(@stats[:total_supports]).to be 2 + expect(stats[:total_supports]).to be 2 end end @@ -143,7 +143,7 @@ describe Budget::Stats do context "#total_unknown_gender_or_age" do it "returns the number of total unknown participants' gender or age" do - expect(@stats[:total_unknown_gender_or_age]).to be 1 + expect(stats[:total_unknown_gender_or_age]).to be 1 end end @@ -151,18 +151,18 @@ describe Budget::Stats do context "#age_groups" do it "returns the age groups hash" do - expect(@stats[:age_groups]["16 - 19"][:count]).to be 0 - expect(@stats[:age_groups]["20 - 24"][:count]).to be 7 - expect(@stats[:age_groups]["25 - 29"][:count]).to be 1 - expect(@stats[:age_groups]["30 - 34"][:count]).to be 0 - expect(@stats[:age_groups]["35 - 39"][:count]).to be 1 - expect(@stats[:age_groups]["40 - 44"][:count]).to be 1 - expect(@stats[:age_groups]["45 - 49"][:count]).to be 0 - expect(@stats[:age_groups]["50 - 54"][:count]).to be 1 - expect(@stats[:age_groups]["55 - 59"][:count]).to be 0 - expect(@stats[:age_groups]["60 - 64"][:count]).to be 0 - expect(@stats[:age_groups]["65 - 69"][:count]).to be 0 - expect(@stats[:age_groups]["70 - 74"][:count]).to be 0 + expect(stats[:age_groups]["16 - 19"][:count]).to be 0 + expect(stats[:age_groups]["20 - 24"][:count]).to be 7 + expect(stats[:age_groups]["25 - 29"][:count]).to be 1 + expect(stats[:age_groups]["30 - 34"][:count]).to be 0 + expect(stats[:age_groups]["35 - 39"][:count]).to be 1 + expect(stats[:age_groups]["40 - 44"][:count]).to be 1 + expect(stats[:age_groups]["45 - 49"][:count]).to be 0 + expect(stats[:age_groups]["50 - 54"][:count]).to be 1 + expect(stats[:age_groups]["55 - 59"][:count]).to be 0 + expect(stats[:age_groups]["60 - 64"][:count]).to be 0 + expect(stats[:age_groups]["65 - 69"][:count]).to be 0 + expect(stats[:age_groups]["70 - 74"][:count]).to be 0 end end @@ -170,7 +170,7 @@ describe Budget::Stats do context "#male_percentage" do it "returns the percentage of male participants" do - expect(@stats[:male_percentage]).to be 40.0 + expect(stats[:male_percentage]).to be 40.0 end end @@ -178,7 +178,7 @@ describe Budget::Stats do context "#female_percentage" do it "returns the percentage of female participants" do - expect(@stats[:female_percentage]).to be 60.0 + expect(stats[:female_percentage]).to be 60.0 end end @@ -186,7 +186,7 @@ describe Budget::Stats do context "#headings" do it "returns headings data" do - heading_stats = @stats[:headings][@heading.id] + heading_stats = stats[:headings][@heading.id] expect(heading_stats[:total_investments_count]).to be 5 expect(heading_stats[:total_participants_support_phase]).to be 2 expect(heading_stats[:total_participants_vote_phase]).to be 4 From 0037ce55465d7cb5cfcdb3b1fa1b4b2896fdfe7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 20 Dec 2018 17:20:28 +0100 Subject: [PATCH 11/82] Reorder poll stats method names So related methods are on the same line. --- app/models/poll/stats.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index 5a166457e..7b5ed28e8 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -4,12 +4,13 @@ class Poll::Stats def self.stats_methods super + - %i[total_participants_web total_web_valid total_web_white total_web_null + %i[total_valid_votes total_white_votes total_null_votes + total_participants_web total_web_valid total_web_white total_web_null total_participants_booth total_booth_valid total_booth_white total_booth_null - total_valid_votes total_white_votes total_null_votes valid_percentage_web valid_percentage_booth - total_valid_percentage white_percentage_web white_percentage_booth total_white_percentage - null_percentage_web null_percentage_booth total_null_percentage total_participants_web_percentage - total_participants_booth_percentage] + total_participants_web_percentage total_participants_booth_percentage + valid_percentage_web valid_percentage_booth total_valid_percentage + white_percentage_web white_percentage_booth total_white_percentage + null_percentage_web null_percentage_booth total_null_percentage] end private From 7ce57f649fb39af9d08a150876500c420b41cc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Thu, 20 Dec 2018 18:31:48 +0100 Subject: [PATCH 12/82] Use a `` tag to display age percentages --- app/assets/stylesheets/stats.scss | 25 +++++++++++++++++++++++++ app/views/budgets/stats/show.html.erb | 4 +--- app/views/polls/stats.html.erb | 5 +---- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/stats.scss b/app/assets/stylesheets/stats.scss index 418e93def..ea1177e6a 100644 --- a/app/assets/stylesheets/stats.scss +++ b/app/assets/stylesheets/stats.scss @@ -80,4 +80,29 @@ } } } + + meter { + background: none; + background-color: #ebf0f4; + border-radius: 0.3rem; + display: block; + width: 100%; + } + + meter::-moz-meter-bar { + background: #52a4ee; + border-bottom-left-radius: 0.3rem; + border-top-left-radius: 0.3rem; + } + + meter::-webkit-meter-optimum-value { + background: #52a4ee; + border-bottom-left-radius: 0.3rem; + border-top-left-radius: 0.3rem; + } + + meter::-webkit-meter-bar { + background: #ebf0f4; + border-radius: 0.3rem; + } } diff --git a/app/views/budgets/stats/show.html.erb b/app/views/budgets/stats/show.html.erb index 5c55be1dd..b7dffce48 100644 --- a/app/views/budgets/stats/show.html.erb +++ b/app/views/budgets/stats/show.html.erb @@ -145,9 +145,7 @@ <% end %> diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index f1da5ae07..ab90f94dd 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -73,10 +73,7 @@ <% end %> From b91b766e96b75ac79e05f20a814f1d3982e24a1b Mon Sep 17 00:00:00 2001 From: decabeza Date: Fri, 28 Dec 2018 18:00:26 +0100 Subject: [PATCH 13/82] Adds styles and updates html markup to stats --- app/assets/images/stats_man.png | Bin 0 -> 1768 bytes app/assets/images/stats_woman.png | Bin 0 -> 1721 bytes app/assets/stylesheets/participation.scss | 2 +- app/assets/stylesheets/stats.scss | 100 +++++++--------------- app/views/budgets/stats/show.html.erb | 15 ++-- app/views/polls/_poll_subnav.html.erb | 2 +- app/views/polls/stats.html.erb | 57 ++++++------ 7 files changed, 76 insertions(+), 100 deletions(-) create mode 100644 app/assets/images/stats_man.png create mode 100644 app/assets/images/stats_woman.png diff --git a/app/assets/images/stats_man.png b/app/assets/images/stats_man.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb030dbcb58c58c5e3591d1ed1ec628e0f2accb GIT binary patch literal 1768 zcmaJ=c~BEq9A2(+tH?2^P}YDWLdZ#g1i}#(OsGK`kY?yG5|V|aB-u6D5J1ID3kIb{ zM`1*TDHcVr2%Z&CBvJvj<)~UgkVD&As6cHghg#ci5U_ugzS-UP-fzC|`<=TlE_SoM z%~~4(0PJ~ONPzF&_?u$&4*o@^DRO*UgGC9kcq9$eic~NVAwg1Mkf#u(!va_&$=-1r z4gml&Z&`v66Y{sP#E60oh-ncYRqbrmZse(lqqDnvz`C=90qzHznr3eZ}M$T6a^yZ7i zGNsYDX`aRBvv^81CQ^!F9>gZ#IwYA)!eY{)2zo>`6a_KpRBBWJlgaSs;7<^ZN~b_U z)I~0Yh&2jWi7j#^Z@E#+a*YF_Kyl9ytdeEIl4up80Ou2D$(E0Wv#j0%SF(I8k;`() zcravRd;it$MH42jXp1~6 zN`wX6{T1sAxHb-jAi;$|IK&Y^w(rL|oIiJNbp%9&h(w|wX@7ooh07+VJja^{3bv0X z_xu?21yS!70Zsn7vg^i$M;+H^)1D?CwcO(}lbN0y96U92P5Wii*~H>>Ux;}Hm>V{8 zIc4vCD)BL8#aa64Gd=6PyHtBxqLQ3+{1J3jztu#{>k!-Bc}mXKD9?(|bv}I-qb1jy z5B$M6;`LfRQpYK=Llt8g^*c@S$_cF-)^82A$g_|1a7B5y9DKmGPB(qK`Mud}6P!VW8(45IelHLq^;?=p6KmK9o~F)EJU(Xx0V1=qJkcG3E^d{X}VSxLHl) z&}X6R^!pprw$R#&y(XZ%hiTE6*`)_%b!!?rdgxhQc*K_ssK>D-|*++q!4&a^t7Gp4;!DdRm(9WvBcE5bM}l+Vy)C%P{t0rl_Y& zX)|K-BszsuVyEa7YPcWi-!o+Ht1L%~U1zlxmz;U+ox5y{Sin05z!fk{0qk@p&xb9E zD|D4ylUi%vnuL418Qg5DJj}CxgnQcZm2f(uuljbOZ_&u)v(TFsTl$gitujHl2_ELz>JAt8uc|SxE%qsmX{}CatJCEQd}@!nGuGa9zQ4ib z(X)cNF!zmzh*rS#&p!NK-603!fQ_b^=mO+Uo6PY6*H=LC&C#Zn2C}K)*h<4*0KH+E z!hZ7i)L(;4uuS9jTTAlXM8-GWNA(xWyjv}ZbdNg&RwUY_sJEV}(`@%GwY{~^Dr}c2 z=_mJtQ%z^G_q3hg8eVi&IAdMyK55oEJ$pC1AU^!?@wl&&HT62Xjt>jX+bvFFO+i+M z=%LLAS5Kb#B&TgEZ(Dc3Oa~lO!~SmAW?Nv3&>uaJvthieC0DIDamV9L-I!OxOznW~ V3bDU-e3%O0ix(9O)p1fj{s$-Eq!9oB literal 0 HcmV?d00001 diff --git a/app/assets/images/stats_woman.png b/app/assets/images/stats_woman.png new file mode 100644 index 0000000000000000000000000000000000000000..e61ff0ea64c29fc6f1f5dee4da759d051953b200 GIT binary patch literal 1721 zcmaJ?X;2eq7+x?zP*7yhh%#{#5W$d(gj^CK5Rj-r8jeyAU`ZB8A=zcKnBY}pQiUoq zVpUKPtb$w8HwWrkuA&suEh1_Aq{bZ2+J?|bKcp7(i=oh^ut zTH`Wj$s7OxTojRV6|wpfPqL!}@kU;7GKs|l4^O~jkW}0VVK5-mASp1Y&_QXi3WhW} zS=Zno0GQ>gjZMH4lAb?4DFbz3+95ONW2Cpdw zIc!j4S`^nJdeFuQr6Ae3lujspn}QCVrqvrJ%S056ZGuoXm&LKAH07vN{=chEH|=e} zRq#8#|4D3!%|T(d3N|3wn3`x@s+TPkDhb122uHA31j(GLVstu!BZhPY1;fH7ss{Qh zA+=U-Gx|-SluC(0Z@?kF8dk`qbb^Pa)oLVSo?OP0MTo=YfjllZTp$()@a?(N3q zB(ZAq0u|{XN-C(JuOKJd;zGGgbD`PIB&Ye!{u9d~)f6OhQuD!|} zb-b2kcBj{1-|Xd~)!SF`Mw&|*zqU~{M;-AAA>H(WhQHPP_}|_1FeC*Q4mc1pltMd&7eco~mZ#xB`*T!~n(P zp*0abylH-1o!`TBo~K{E4`+K>GgkV=r6Te}>+cs?S&BGDr(odNkk8YV$*D`%-@&`c zPpoI_&CO5Al-5Jkm_ZLpYn8T5!GBY<%t8sKRS6Dt3D@29o|~6z@T}2yKdHX^I^@u? zp@tBI{PO;Zov-f`~M(>zkQH@(3`9UJ4zr2=} z`;1ETj-&g3G*p$KD$|LnPT<$J&%`InM z5rhJ)r3bEuFlfMqOK)zVQc~Z^`JxY5dd~J$t11*bGWL-81xOvu&gfiLbM{s8?d6Gn zC8zc;Ia66iN;|jE?YwV?&f7VHeD82=N+YmI6}Mk7^y>9Uaqx(A-rvvqlEgs2uzplS evtQJW*f{`?N?JI*ABeBoekO|WD7hsxIqx5Uz=PNT literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index 11fe4b482..ad447f909 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -1996,7 +1996,7 @@ border-right: 1px solid $border; } - .menu { + .menu:not(.vertical) { padding: 0; li a { diff --git a/app/assets/stylesheets/stats.scss b/app/assets/stylesheets/stats.scss index ea1177e6a..f6d8845c5 100644 --- a/app/assets/stylesheets/stats.scss +++ b/app/assets/stylesheets/stats.scss @@ -1,55 +1,34 @@ -.polls-results-stats { +.participation-stats { .sidebar { border-right: 1px solid $border; - > ul { - display: block; - font-size: rem-calc(20); + ul { + margin-left: $line-height / 2; + margin-top: 0; padding: 0; - - ul { - font-size: rem-calc(15); - list-style-type: none; - margin-left: 0.8rem; - margin-top: 1rem; - } - - li a { - color: $link; - padding: 0; - } } } - h2 { + .section-title { + font-size: rem-calc(24); border-bottom: 1px solid $border; } - section section { - clear: both; - margin-top: rem-calc(30); - margin-bottom: rem-calc(30); - overflow: hidden; - } - - h3 { - font-size: rem-calc(20); - margin-bottom: rem-calc(12); + .stats-group { + margin-bottom: $line-height; + margin-top: $line-height; } .participants { - $background-width: 52px; - - float: left; - margin-left: $background-width; - margin-right: rem-calc(22); + display: inline-block; + margin-right: $line-height * 2; + padding-left: $line-height * 2.5; position: relative; &::before { - // TODO: change image and move it to a non-custom folder - content: image-url("custom/polls/stats_man.png"); - left: -$background-width; + content: image-url('stats_man.png'); + left: 0; position: absolute; } @@ -61,48 +40,31 @@ } .info { - color: $dark-gray; + color: $text-medium; font-style: italic; } + } - &.gender { + .gender { - &.male { - &::before { - content: image-url("custom/polls/stats_man.png"); - } - } + &.male::before { + content: image-url('stats_man.png'); + } - &.female { - &::before { - content: image-url("custom/polls/stats_woman.png"); - } - } + &.female::before { + content: image-url('stats_woman.png'); } } - meter { - background: none; - background-color: #ebf0f4; - border-radius: 0.3rem; - display: block; - width: 100%; - } - - meter::-moz-meter-bar { - background: #52a4ee; - border-bottom-left-radius: 0.3rem; - border-top-left-radius: 0.3rem; - } - - meter::-webkit-meter-optimum-value { - background: #52a4ee; - border-bottom-left-radius: 0.3rem; - border-top-left-radius: 0.3rem; - } - - meter::-webkit-meter-bar { + .progress { background: #ebf0f4; - border-radius: 0.3rem; + border-radius: rem-calc(20); + height: $line-height / 2; + } + + .progress-meter { + background: #52a4ee; + border-bottom-left-radius: rem-calc(20); + border-top-left-radius: rem-calc(20); } } diff --git a/app/views/budgets/stats/show.html.erb b/app/views/budgets/stats/show.html.erb index b7dffce48..dad8ee5e9 100644 --- a/app/views/budgets/stats/show.html.erb +++ b/app/views/budgets/stats/show.html.erb @@ -9,7 +9,7 @@ <% end %> <% cache [@stats] do %> -
    +
    @@ -135,8 +135,8 @@
    <%= group[:range] %> <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> -
    - -
    +
    <%= group[:range] %> <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> - -
    - -
    +
    - - + + @@ -144,8 +144,13 @@ <% end %> diff --git a/app/views/polls/_poll_subnav.html.erb b/app/views/polls/_poll_subnav.html.erb index 1fec98bdb..e50d64e0a 100644 --- a/app/views/polls/_poll_subnav.html.erb +++ b/app/views/polls/_poll_subnav.html.erb @@ -15,7 +15,7 @@ <% if @poll.stats_enabled? %> <% if stats_menu? %>
  • - <%= t("polls.show.stats_menu") %> +

    <%= t("polls.show.stats_menu") %>

  • <% else %>
  • <%= link_to t("polls.show.stats_menu"), stats_poll_path(id: @poll.slug || @poll.id) %>
  • diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index ab90f94dd..54ad19a59 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -1,16 +1,18 @@ <% provide :title do %><%= @poll.name %><% end %> -
    +
    <%= render "poll_header" %> <%= render "poll_subnav" %>
    +
    -
    -

    <%= t("stats.title") %>

    +
    +

    <%= t("stats.title") %>

    -
    -

    <%= t("stats.total_participants") %>

    +
    +

    <%= t("stats.total_participants") %>

    <%= @stats[:total_participants] %> - <%= t("polls.show.stats.total_votes") %>
    + <%= t("polls.show.stats.total_votes") %>

    -
    +
    -
    -

    <%= t("stats.by_gender") %>

    +
    +

    <%= t("stats.by_gender") %>

    <%= @stats[:total_male_participants] %> @@ -56,15 +58,17 @@ percentage: number_to_stats_percentage(@stats[:female_percentage])) %>

    -
    +
    -
    -

    <%= t("stats.by_age") %>

    +
    +

    <%= t("stats.by_age") %>

    <%= t("budgets.stats.age").upcase %><%= t("budgets.stats.total").upcase %><%= t("budgets.stats.age").upcase %><%= t("budgets.stats.total").upcase %>
    <%= group[:range] %> - <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> - + + <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> + +
    + + +
    - - + + + + @@ -72,15 +76,20 @@ <% end %>
    <%= t("stats.age") %><%= t("stats.total") %>
    <%= t("stats.age") %><%= t("stats.total") %>
    <%= group[:range] %> - <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> - + + <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> + +
    + + +
    - - +
    +
    From b4a1f0db5134d143b3aeead6d4cad4f6f7c2beb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 28 Dec 2018 19:04:36 +0100 Subject: [PATCH 14/82] Rename file to initialize Foundation Naming it "Dropdown" was misleading. --- app/assets/javascripts/application.js | 4 ++-- .../{dropdown.js.coffee => foundation_extras.js.coffee} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename app/assets/javascripts/{dropdown.js.coffee => foundation_extras.js.coffee} (67%) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f8903dd30..dbfcfea65 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -27,7 +27,7 @@ //= require app //= require check_all_none //= require comments -//= require dropdown +//= require foundation_extras //= require ie_alert //= require location_changer //= require moderator_comment @@ -93,7 +93,7 @@ var initialize_modules = function() { App.Votes.initialize(); App.AllowParticipation.initialize(); App.Tags.initialize(); - App.Dropdown.initialize(); + App.FoundationExtras.initialize(); App.LocationChanger.initialize(); App.CheckAllNone.initialize(); App.PreventDoubleSubmission.initialize(); diff --git a/app/assets/javascripts/dropdown.js.coffee b/app/assets/javascripts/foundation_extras.js.coffee similarity index 67% rename from app/assets/javascripts/dropdown.js.coffee rename to app/assets/javascripts/foundation_extras.js.coffee index fafa53542..cf1bcb768 100644 --- a/app/assets/javascripts/dropdown.js.coffee +++ b/app/assets/javascripts/foundation_extras.js.coffee @@ -1,4 +1,4 @@ -App.Dropdown = +App.FoundationExtras = initialize: -> $(document).foundation() From f7d4507ebea6ad2915741fb112e7c9c430ccf76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 28 Dec 2018 19:09:11 +0100 Subject: [PATCH 15/82] Keep sticky elements after AJAX requests Turbolinks doesn't get on well with Foundation's Sticky, and so we need to manually trigger the event on Turbolinks' `page:load`. --- app/assets/javascripts/foundation_extras.js.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/foundation_extras.js.coffee b/app/assets/javascripts/foundation_extras.js.coffee index cf1bcb768..720b62047 100644 --- a/app/assets/javascripts/foundation_extras.js.coffee +++ b/app/assets/javascripts/foundation_extras.js.coffee @@ -2,3 +2,4 @@ App.FoundationExtras = initialize: -> $(document).foundation() + $(window).trigger "load.zf.sticky" From 0e2e5a27c3d985cf7934ba57ed33f17bdb9af406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 19:15:14 +0100 Subject: [PATCH 16/82] Refactor valid, white and null votes calculations --- app/models/poll/stats.rb | 54 ++++++++++++---------------------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index 7b5ed28e8..e61a50541 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -42,72 +42,50 @@ class Poll::Stats voters.where(origin: "web").count - total_web_white end - def valid_percentage_web - calculate_percentage(total_web_valid, total_valid_votes) - end - def total_web_white 0 end - def white_percentage_web - calculate_percentage(total_web_white, total_white_votes) - end - def total_web_null 0 end - def null_percentage_web - calculate_percentage(total_web_null, total_null_votes) - end - def total_booth_valid recounts.sum(:total_amount) end - def valid_percentage_booth - calculate_percentage(total_booth_valid, total_valid_votes) - end - def total_booth_white recounts.sum(:white_amount) end - def white_percentage_booth - calculate_percentage(total_booth_white, total_white_votes) - end - def total_booth_null recounts.sum(:null_amount) end - def null_percentage_booth - calculate_percentage(total_booth_null, total_null_votes) + def valid_percentage_web + calculate_percentage(total_web_valid, total_valid_votes) end - def total_valid_votes - total_web_valid + total_booth_valid + def white_percentage_web + calculate_percentage(total_web_white, total_white_votes) end - def total_valid_percentage - calculate_percentage(total_valid_votes, total_participants) + def null_percentage_web + calculate_percentage(total_web_null, total_null_votes) end - def total_white_votes - total_web_white + total_booth_white - end + %i[valid white null].each do |type| + define_method :"#{type}_percentage_booth" do + calculate_percentage(send(:"total_booth_#{type}"), send(:"total_#{type}_votes")) + end - def total_white_percentage - calculate_percentage(total_white_votes, total_participants) - end + define_method :"total_#{type}_votes" do + send(:"total_web_#{type}") + send(:"total_booth_#{type}") + end - def total_null_votes - total_web_null + total_booth_null - end - - def total_null_percentage - calculate_percentage(total_null_votes, total_participants) + define_method :"total_#{type}_percentage" do + calculate_percentage(send(:"total_#{type}_votes"), total_participants) + end end def voters From 7dbe1dce860250d59cc9c6898dd42850c5fb589e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 19:15:44 +0100 Subject: [PATCH 17/82] Refactor web and booth votes calculations --- app/models/poll/stats.rb | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index e61a50541..f8ad25560 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -22,20 +22,16 @@ class Poll::Stats total_participants_web + total_participants_booth end - def total_participants_web - total_web_valid + total_web_white + total_web_null - end + %i[web booth].each do |channel| + define_method :"total_participants_#{channel}" do + send(:"total_#{channel}_valid") + + send(:"total_#{channel}_white") + + send(:"total_#{channel}_null") + end - def total_participants_web_percentage - calculate_percentage(total_participants_web, total_participants) - end - - def total_participants_booth - total_booth_valid + total_booth_white + total_booth_null - end - - def total_participants_booth_percentage - calculate_percentage(total_participants_booth, total_participants) + define_method :"total_participants_#{channel}_percentage" do + calculate_percentage(send(:"total_participants_#{channel}"), total_participants) + end end def total_web_valid From 88daaee9fe8f0e45244241f2a1aa0ccea1cf1034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 28 Dec 2018 20:39:01 +0100 Subject: [PATCH 18/82] Simplify code --- app/models/concerns/statisticable.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index 5ff689641..300b77387 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -54,20 +54,21 @@ module Statisticable [75, 79], [80, 84], [85, 89], - [90, 300]].reduce({}) do |groups, (start, finish)| - users = User.where(id: participants) - .where("date_of_birth > ? AND date_of_birth < ?", - finish.years.ago.beginning_of_year, - start.years.ago.end_of_year) + [90, 300] + ].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) - groups.tap do |age_groups| - age_groups["#{start} - #{finish}"] = { + [ + "#{start} - #{finish}", + { range: range_description(start, finish), count: users.count, percentage: calculate_percentage(users.count, total_participants) } - end - end + ] + end.to_h end def calculate_percentage(fraction, total) From 573e04ef8f3a7d00fc1a02c400135db14172b700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 19:17:33 +0100 Subject: [PATCH 19/82] Move common stats to partials --- app/views/polls/stats.html.erb | 76 +------------------ app/views/shared/stats/_links.html.erb | 12 +++ .../shared/stats/_participation.html.erb | 62 +++++++++++++++ 3 files changed, 76 insertions(+), 74 deletions(-) create mode 100644 app/views/shared/stats/_links.html.erb create mode 100644 app/views/shared/stats/_participation.html.erb diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index 54ad19a59..e09780af0 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -11,85 +11,13 @@
    -

    <%= link_to t("stats.title"), "#participation_statistics" %>

    - + <%= render "shared/stats/links" %>
    -
    -

    <%= t("stats.title") %>

    - -
    -

    <%= t("stats.total_participants") %>

    - -

    - <%= @stats[:total_participants] %> - <%= t("polls.show.stats.total_votes") %> -

    -
    - -
    -

    <%= t("stats.by_gender") %>

    - -

    - <%= @stats[:total_male_participants] %> - - <%= I18n.t("stats.men_percentage", - percentage: number_to_stats_percentage(@stats[:male_percentage])) %> - -

    - -

    - <%= @stats[:total_female_participants] %> - - <%= I18n.t("stats.women_percentage", - percentage: number_to_stats_percentage(@stats[:female_percentage])) %> - -

    -
    - -
    -

    <%= t("stats.by_age") %>

    - -
    - - - - - - - - - <% @stats[:age_groups].values.each do |group| %> - - - - - <% end %> - -
    <%= t("stats.age") %><%= t("stats.total") %>
    <%= group[:range] %> - - <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> - -
    - - -
    -
    -
    -
    + <%= render "shared/stats/participation", stats: @stats %> diff --git a/app/views/shared/stats/_links.html.erb b/app/views/shared/stats/_links.html.erb new file mode 100644 index 000000000..7b7d6d7ac --- /dev/null +++ b/app/views/shared/stats/_links.html.erb @@ -0,0 +1,12 @@ +

    <%= link_to t("stats.title"), "#participation_statistics" %>

    + diff --git a/app/views/shared/stats/_participation.html.erb b/app/views/shared/stats/_participation.html.erb new file mode 100644 index 000000000..cf31920db --- /dev/null +++ b/app/views/shared/stats/_participation.html.erb @@ -0,0 +1,62 @@ +
    +

    <%= t("stats.title") %>

    + +
    +

    <%= t("stats.total_participants") %>

    + +

    + <%= stats[:total_participants] %> + <%= t("polls.show.stats.total_votes") %> +

    +
    + +
    +

    <%= t("stats.by_gender") %>

    + +

    + <%= stats[:total_male_participants] %> + + <%= I18n.t("stats.men_percentage", + percentage: number_to_stats_percentage(stats[:male_percentage])) %> + +

    + +

    + <%= stats[:total_female_participants] %> + + <%= I18n.t("stats.women_percentage", + percentage: number_to_stats_percentage(stats[:female_percentage])) %> + +

    +
    + +
    +

    <%= t("stats.by_age") %>

    + +
    + + + + + + + + + <% stats[:age_groups].values.each do |group| %> + + + + + <% end %> + +
    <%= t("stats.age") %><%= t("stats.total") %>
    <%= group[:range] %> + + <%= "#{group[:count]} (#{number_to_stats_percentage(group[:percentage])})" %> + +
    + + +
    +
    +
    +
    From 1c9552b9220a72348564edcacb9ae10f98459268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 19:18:31 +0100 Subject: [PATCH 20/82] Add number-with-info HTML class to stats So it can be reused in advanced statistics. --- app/assets/stylesheets/stats.scss | 37 ++++++++++--------- .../shared/stats/_participation.html.erb | 6 +-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/assets/stylesheets/stats.scss b/app/assets/stylesheets/stats.scss index f6d8845c5..27fb84873 100644 --- a/app/assets/stylesheets/stats.scss +++ b/app/assets/stylesheets/stats.scss @@ -20,16 +20,30 @@ margin-top: $line-height; } - .participants { + .number-with-info { display: inline-block; margin-right: $line-height * 2; - padding-left: $line-height * 2.5; - position: relative; - &::before { + &.participants { + padding-left: $line-height * 2.5; + position: relative; + + &::before { + left: 0; + position: absolute; + } + } + + &.total-participants::before { content: image-url('stats_man.png'); - left: 0; - position: absolute; + } + + &.male::before { + content: image-url('stats_man.png'); + } + + &.female::before { + content: image-url('stats_woman.png'); } .number { @@ -45,17 +59,6 @@ } } - .gender { - - &.male::before { - content: image-url('stats_man.png'); - } - - &.female::before { - content: image-url('stats_woman.png'); - } - } - .progress { background: #ebf0f4; border-radius: rem-calc(20); diff --git a/app/views/shared/stats/_participation.html.erb b/app/views/shared/stats/_participation.html.erb index cf31920db..d49fb839b 100644 --- a/app/views/shared/stats/_participation.html.erb +++ b/app/views/shared/stats/_participation.html.erb @@ -4,7 +4,7 @@

    <%= t("stats.total_participants") %>

    -

    +

    <%= stats[:total_participants] %> <%= t("polls.show.stats.total_votes") %>

    @@ -13,7 +13,7 @@

    <%= t("stats.by_gender") %>

    -

    +

    <%= stats[:total_male_participants] %> <%= I18n.t("stats.men_percentage", @@ -21,7 +21,7 @@

    -

    +

    <%= stats[:total_female_participants] %> <%= I18n.t("stats.women_percentage", From 4ade857a1bec0b63afdcfaa241ce9e6abbb0b628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 19:19:07 +0100 Subject: [PATCH 21/82] Extract method to render number and info We're going to use it in many places, so removing duplication is useful. --- app/helpers/stats_helper.rb | 6 ++++ .../shared/stats/_participation.html.erb | 33 +++++++++---------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/app/helpers/stats_helper.rb b/app/helpers/stats_helper.rb index 5fd42027e..eef463a99 100644 --- a/app/helpers/stats_helper.rb +++ b/app/helpers/stats_helper.rb @@ -34,4 +34,10 @@ module StatsHelper def number_to_stats_percentage(number, options = {}) number_to_percentage(number, { strip_insignificant_zeros: true, precision: 2 }.merge(options)) end + + def number_with_info_tags(number, text, html_class: "") + content_tag :p, class: "number-with-info #{html_class}".strip do + content_tag(:span, number, class: "number") + content_tag(:span, text, class: "info") + end + end end diff --git a/app/views/shared/stats/_participation.html.erb b/app/views/shared/stats/_participation.html.erb index d49fb839b..a806e2d0a 100644 --- a/app/views/shared/stats/_participation.html.erb +++ b/app/views/shared/stats/_participation.html.erb @@ -4,30 +4,27 @@

    <%= t("stats.total_participants") %>

    -

    - <%= stats[:total_participants] %> - <%= t("polls.show.stats.total_votes") %> -

    + <%= number_with_info_tags( + stats[:total_participants], + t("polls.show.stats.total_votes"), + html_class: "participants total-participants" + ) %>

    <%= t("stats.by_gender") %>

    -

    - <%= stats[:total_male_participants] %> - - <%= I18n.t("stats.men_percentage", - percentage: number_to_stats_percentage(stats[:male_percentage])) %> - -

    + <%= number_with_info_tags( + stats[:total_male_participants], + t("stats.men_percentage", percentage: number_to_stats_percentage(stats[:male_percentage])), + html_class: "participants male" + ) %> -

    - <%= stats[:total_female_participants] %> - - <%= I18n.t("stats.women_percentage", - percentage: number_to_stats_percentage(stats[:female_percentage])) %> - -

    + <%= number_with_info_tags( + stats[:total_female_participants], + t("stats.women_percentage", percentage: number_to_stats_percentage(stats[:female_percentage])), + html_class: "participants female" + ) %>
    From c4c0301f3f11d44d0924f878a8664a8574600e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 19:19:44 +0100 Subject: [PATCH 22/82] Simplify stats images styles This way we don't need to manually define the width we think the image we insert in the `::before` pseudoclass is going to take. --- app/assets/images/stats_users.png | Bin 0 -> 3725 bytes app/assets/stylesheets/stats.scss | 19 +++++++++---------- app/helpers/stats_helper.rb | 4 +++- 3 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 app/assets/images/stats_users.png diff --git a/app/assets/images/stats_users.png b/app/assets/images/stats_users.png new file mode 100644 index 0000000000000000000000000000000000000000..76bb80606f80bd5764f676f0a0b87f89e12203a1 GIT binary patch literal 3725 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 literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/stats.scss b/app/assets/stylesheets/stats.scss index 27fb84873..6c2cc73c5 100644 --- a/app/assets/stylesheets/stats.scss +++ b/app/assets/stylesheets/stats.scss @@ -22,20 +22,14 @@ .number-with-info { display: inline-block; - margin-right: $line-height * 2; - &.participants { - padding-left: $line-height * 2.5; - position: relative; - - &::before { - left: 0; - position: absolute; - } + &::before { + display: inline-block; + margin-right: $line-height / 2; } &.total-participants::before { - content: image-url('stats_man.png'); + content: image-url('stats_users.png'); } &.male::before { @@ -46,6 +40,11 @@ content: image-url('stats_woman.png'); } + .content { + display: inline-block; + margin-right: $line-height * 2; + } + .number { display: block; font-size: rem-calc(60); diff --git a/app/helpers/stats_helper.rb b/app/helpers/stats_helper.rb index eef463a99..d321bce0b 100644 --- a/app/helpers/stats_helper.rb +++ b/app/helpers/stats_helper.rb @@ -37,7 +37,9 @@ module StatsHelper def number_with_info_tags(number, text, html_class: "") content_tag :p, class: "number-with-info #{html_class}".strip do - content_tag(:span, number, class: "number") + content_tag(:span, text, class: "info") + content_tag :span, class: "content" do + content_tag(:span, number, class: "number") + content_tag(:span, text, class: "info") + end end end end From 4d520a3a474d516fac1b46e2f1084f4c28fce8a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 19:20:07 +0100 Subject: [PATCH 23/82] Rename `age_groups` method The name was confusing because it seemed to return a list of age groups. --- app/models/concerns/statisticable.rb | 8 ++++-- app/views/budgets/stats/show.html.erb | 2 +- .../shared/stats/_participation.html.erb | 2 +- spec/models/budget/stats_spec.rb | 26 +++++++++---------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index 300b77387..0a041a851 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -55,7 +55,11 @@ module Statisticable [80, 84], [85, 89], [90, 300] - ].map do |start, finish| + ] + 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) @@ -90,7 +94,7 @@ module Statisticable def stats_methods %i[total_participants total_male_participants total_female_participants total_unknown_gender_or_age - male_percentage female_percentage age_groups] + male_percentage female_percentage participants_by_age] end def stats_cache(*method_names) diff --git a/app/views/budgets/stats/show.html.erb b/app/views/budgets/stats/show.html.erb index dad8ee5e9..e156d0d9b 100644 --- a/app/views/budgets/stats/show.html.erb +++ b/app/views/budgets/stats/show.html.erb @@ -140,7 +140,7 @@ - <% @stats[:age_groups].values.each do |group| %> + <% @stats[:participants_by_age].values.each do |group| %> <%= group[:range] %> diff --git a/app/views/shared/stats/_participation.html.erb b/app/views/shared/stats/_participation.html.erb index a806e2d0a..88faf3575 100644 --- a/app/views/shared/stats/_participation.html.erb +++ b/app/views/shared/stats/_participation.html.erb @@ -39,7 +39,7 @@ - <% stats[:age_groups].values.each do |group| %> + <% stats[:participants_by_age].values.each do |group| %> <%= group[:range] %> diff --git a/spec/models/budget/stats_spec.rb b/spec/models/budget/stats_spec.rb index 3ff9dfa9f..231c90675 100644 --- a/spec/models/budget/stats_spec.rb +++ b/spec/models/budget/stats_spec.rb @@ -148,21 +148,21 @@ describe Budget::Stats do end - context "#age_groups" do + context "#participants_by_age" do it "returns the age groups hash" do - expect(stats[:age_groups]["16 - 19"][:count]).to be 0 - expect(stats[:age_groups]["20 - 24"][:count]).to be 7 - expect(stats[:age_groups]["25 - 29"][:count]).to be 1 - expect(stats[:age_groups]["30 - 34"][:count]).to be 0 - expect(stats[:age_groups]["35 - 39"][:count]).to be 1 - expect(stats[:age_groups]["40 - 44"][:count]).to be 1 - expect(stats[:age_groups]["45 - 49"][:count]).to be 0 - expect(stats[:age_groups]["50 - 54"][:count]).to be 1 - expect(stats[:age_groups]["55 - 59"][:count]).to be 0 - expect(stats[:age_groups]["60 - 64"][:count]).to be 0 - expect(stats[:age_groups]["65 - 69"][:count]).to be 0 - expect(stats[:age_groups]["70 - 74"][:count]).to be 0 + expect(stats[:participants_by_age]["16 - 19"][:count]).to be 0 + expect(stats[:participants_by_age]["20 - 24"][:count]).to be 7 + expect(stats[:participants_by_age]["25 - 29"][:count]).to be 1 + expect(stats[:participants_by_age]["30 - 34"][:count]).to be 0 + expect(stats[:participants_by_age]["35 - 39"][:count]).to be 1 + expect(stats[:participants_by_age]["40 - 44"][:count]).to be 1 + expect(stats[:participants_by_age]["45 - 49"][:count]).to be 0 + expect(stats[:participants_by_age]["50 - 54"][:count]).to be 1 + 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 end end From 1975b6ae7ceb60f16c347edb6772aed3bf6dcba0 Mon Sep 17 00:00:00 2001 From: decabeza Date: Thu, 17 Jan 2019 13:28:54 +0100 Subject: [PATCH 24/82] Remove sticky on polls stats links --- app/views/polls/stats.html.erb | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index e09780af0..76d75df05 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -7,13 +7,7 @@
    From 70dbbbd1b99501a8a345522fd68064031fe84f73 Mon Sep 17 00:00:00 2001 From: decabeza Date: Thu, 17 Jan 2019 13:30:35 +0100 Subject: [PATCH 25/82] Add stack class to polls stats table This class improve tables layout on mobile screen sizes. --- app/assets/stylesheets/_consul_settings.scss | 3 +++ app/views/polls/stats.html.erb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/assets/stylesheets/_consul_settings.scss b/app/assets/stylesheets/_consul_settings.scss index 7ffd8ae4f..1275efcfc 100644 --- a/app/assets/stylesheets/_consul_settings.scss +++ b/app/assets/stylesheets/_consul_settings.scss @@ -119,3 +119,6 @@ $tab-content-border: $border; $orbit-bullet-diameter: 0.8rem; $pagination-radius: $global-radius; + +$show-header-for-stacked: true; + diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index 76d75df05..f77282b85 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -13,7 +13,7 @@
    <%= render "shared/stats/participation", stats: @stats %> - +
    From a552645e7f99948b7cb2a437339d0cdc27f8c501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 11 Jan 2019 21:37:10 +0100 Subject: [PATCH 26/82] Add tests to poll stats While we already had "one test to rule all stats", testing each method individually makes reading, adding and changing tests easier. Note we need to make all methods being tested public. We could also test them using methods like `stats.generate[:total_valid_votes]` instead of `stats.total_valid_votes`, but then the tests would be more difficult to read. --- app/models/concerns/statisticable.rb | 74 ++++++------- app/models/poll/stats.rb | 133 +++++++++++------------ spec/models/poll/stats_spec.rb | 154 +++++++++++++++++++++++++++ 3 files changed, 258 insertions(+), 103 deletions(-) diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index 0a041a851..cd24aed8e 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -12,32 +12,49 @@ module Statisticable self.class.stats_methods.map { |stat_name| [stat_name, send(stat_name)] }.to_h end + def total_male_participants + participants.where(gender: "male").count + end + + def total_female_participants + participants.where(gender: "female").count + end + + def total_unknown_gender_or_age + participants.where("gender IS NULL OR date_of_birth is NULL").uniq.count + end + + def male_percentage + calculate_percentage(total_male_participants, total_participants_with_gender) + end + + def female_percentage + calculate_percentage(total_female_participants, total_participants_with_gender) + 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 + end + private def total_participants_with_gender participants.where.not(gender: nil).distinct.count end - def total_male_participants - participants.where(gender: "male").count - end - - def total_female_participants - participants.where(gender: "female").count - end - - def total_unknown_gender_or_age - participants.where("gender IS NULL OR date_of_birth is NULL").uniq.count - end - - def male_percentage - calculate_percentage(total_male_participants, total_participants_with_gender) - end - - def female_percentage - calculate_percentage(total_female_participants, total_participants_with_gender) - end - def age_groups [[16, 19], [20, 24], @@ -58,23 +75,6 @@ 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 - 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 f8ad25560..21f9fba94 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -13,77 +13,78 @@ class Poll::Stats null_percentage_web null_percentage_booth total_null_percentage] end + def total_participants + total_participants_web + total_participants_booth + end + + %i[web booth].each do |channel| + define_method :"total_participants_#{channel}" do + send(:"total_#{channel}_valid") + + send(:"total_#{channel}_white") + + send(:"total_#{channel}_null") + end + + define_method :"total_participants_#{channel}_percentage" do + calculate_percentage(send(:"total_participants_#{channel}"), total_participants) + end + end + + def total_web_valid + voters.where(origin: "web").count - total_web_white + end + + def total_web_white + 0 + end + + def total_web_null + 0 + end + + def total_booth_valid + recounts.sum(:total_amount) + end + + def total_booth_white + recounts.sum(:white_amount) + end + + def total_booth_null + recounts.sum(:null_amount) + end + + def valid_percentage_web + calculate_percentage(total_web_valid, total_valid_votes) + end + + def white_percentage_web + calculate_percentage(total_web_white, total_white_votes) + end + + def null_percentage_web + calculate_percentage(total_web_null, total_null_votes) + end + + %i[valid white null].each do |type| + define_method :"#{type}_percentage_booth" do + calculate_percentage(send(:"total_booth_#{type}"), send(:"total_#{type}_votes")) + end + + define_method :"total_#{type}_votes" do + send(:"total_web_#{type}") + send(:"total_booth_#{type}") + end + + define_method :"total_#{type}_percentage" do + calculate_percentage(send(:"total_#{type}_votes"), total_participants) + end + end + private + def participants User.where(id: voters.pluck(:user_id)) end - def total_participants - total_participants_web + total_participants_booth - end - - %i[web booth].each do |channel| - define_method :"total_participants_#{channel}" do - send(:"total_#{channel}_valid") + - send(:"total_#{channel}_white") + - send(:"total_#{channel}_null") - end - - define_method :"total_participants_#{channel}_percentage" do - calculate_percentage(send(:"total_participants_#{channel}"), total_participants) - end - end - - def total_web_valid - voters.where(origin: "web").count - total_web_white - end - - def total_web_white - 0 - end - - def total_web_null - 0 - end - - def total_booth_valid - recounts.sum(:total_amount) - end - - def total_booth_white - recounts.sum(:white_amount) - end - - def total_booth_null - recounts.sum(:null_amount) - end - - def valid_percentage_web - calculate_percentage(total_web_valid, total_valid_votes) - end - - def white_percentage_web - calculate_percentage(total_web_white, total_white_votes) - end - - def null_percentage_web - calculate_percentage(total_web_null, total_null_votes) - end - - %i[valid white null].each do |type| - define_method :"#{type}_percentage_booth" do - calculate_percentage(send(:"total_booth_#{type}"), send(:"total_#{type}_votes")) - end - - define_method :"total_#{type}_votes" do - send(:"total_web_#{type}") + send(:"total_booth_#{type}") - end - - define_method :"total_#{type}_percentage" do - calculate_percentage(send(:"total_#{type}_votes"), total_participants) - end - end - def voters poll.voters end diff --git a/spec/models/poll/stats_spec.rb b/spec/models/poll/stats_spec.rb index b2130a370..5f6e345da 100644 --- a/spec/models/poll/stats_spec.rb +++ b/spec/models/poll/stats_spec.rb @@ -1,6 +1,160 @@ require "rails_helper" describe Poll::Stats do + let(:poll) { create(:poll) } + let(:stats) { Poll::Stats.new(poll) } + + describe "total participants" do + before { allow(stats).to receive(:total_web_white).and_return(1) } + + it "supports every channel" do + 3.times { create(:poll_voter, :from_web, poll: poll) } + create(:poll_recount, :from_booth, poll: poll, + total_amount: 8, white_amount: 4, null_amount: 1) + + expect(stats.total_participants_web).to eq(3) + expect(stats.total_participants_booth).to eq(13) + expect(stats.total_participants).to eq(16) + end + end + + describe "#total_participants_booth" do + it "uses recounts even if there are discrepancies when recounting" do + create(:poll_recount, :from_booth, poll: poll, total_amount: 1) + 2.times { create(:poll_voter, :from_booth, poll: poll) } + + expect(stats.total_participants_booth).to eq(1) + end + end + + describe "total participants percentage by channel" do + it "is relative to the total amount of participants" do + create(:poll_voter, :from_web, poll: poll) + create(:poll_recount, :from_booth, poll: poll, total_amount: 5) + + expect(stats.total_participants_web_percentage).to eq(16.667) + expect(stats.total_participants_booth_percentage).to eq(83.333) + end + end + + describe "#total_web_valid" do + before { allow(stats).to receive(:total_web_white).and_return(1) } + + it "returns only valid votes" do + 3.times { create(:poll_voter, :from_web, poll: poll) } + + expect(stats.total_web_valid).to eq(2) + end + end + + describe "#total_web_white" do + pending "Too complex to test" + end + + describe "#total_web_null" do + it "returns 0" do + expect(stats.total_web_null).to eq(0) + end + end + + describe "#total_booth_valid" do + it "sums the total amounts in the recounts" do + create(:poll_recount, :from_booth, poll: poll, total_amount: 3, white_amount: 1) + create(:poll_recount, :from_booth, poll: poll, total_amount: 4, null_amount: 2) + + expect(stats.total_booth_valid).to eq(7) + end + end + + describe "#total_booth_white" do + it "sums the white amounts in the recounts" do + create(:poll_recount, :from_booth, poll: poll, white_amount: 120, total_amount: 3) + create(:poll_recount, :from_booth, poll: poll, white_amount: 203, null_amount: 5) + + expect(stats.total_booth_white).to eq(323) + end + end + + describe "#total_booth_null" do + it "sums the null amounts in the recounts" do + create(:poll_recount, :from_booth, poll: poll, null_amount: 125, total_amount: 3) + create(:poll_recount, :from_booth, poll: poll, null_amount: 34, white_amount: 5) + + expect(stats.total_booth_null).to eq(159) + end + end + + describe "valid percentage by channel" do + it "is relative to the total amount of valid votes" do + create(:poll_recount, :from_booth, poll: poll, total_amount: 2) + create(:poll_voter, :from_web, poll: poll) + + expect(stats.valid_percentage_web).to eq(33.333) + expect(stats.valid_percentage_booth).to eq(66.667) + end + end + + describe "white percentage by channel" do + before { allow(stats).to receive(:total_web_white).and_return(10) } + + it "is relative to the total amount of white votes" do + create(:poll_recount, :from_booth, poll: poll, white_amount: 70) + + expect(stats.white_percentage_web).to eq(12.5) + expect(stats.white_percentage_booth).to eq(87.5) + end + end + + describe "null percentage by channel" do + it "only accepts null votes from booth" do + create(:poll_recount, :from_booth, poll: poll, null_amount: 70) + + expect(stats.null_percentage_web).to eq(0) + expect(stats.null_percentage_booth).to eq(100) + end + end + + describe "#total_valid_votes" do + it "counts valid votes from every channel" do + 2.times { create(:poll_voter, :from_web, poll: poll) } + create(:poll_recount, :from_booth, poll: poll, total_amount: 3, white_amount: 10) + create(:poll_recount, :from_booth, poll: poll, total_amount: 4, null_amount: 20) + + expect(stats.total_valid_votes).to eq(9) + end + end + + describe "#total_white_votes" do + before { allow(stats).to receive(:total_web_white).and_return(9) } + + it "counts white votes on every channel" do + create(:poll_recount, :from_booth, poll: poll, white_amount: 12) + + expect(stats.total_white_votes).to eq(21) + end + end + + describe "#total_null_votes" do + it "only accepts null votes from booth" do + create(:poll_recount, :from_booth, poll: poll, null_amount: 32) + + expect(stats.total_null_votes).to eq(32) + end + end + + describe "total percentage by type" do + before { allow(stats).to receive(:total_web_white).and_return(1) } + + it "is relative to the total amount of votes" do + 3.times { create(:poll_voter, :from_web, poll: poll) } + create(:poll_recount, :from_booth, poll: poll, + total_amount: 8, white_amount: 5, null_amount: 4) + + expect(stats.total_valid_percentage).to eq(50) + expect(stats.total_white_percentage).to eq(30) + expect(stats.total_null_percentage).to eq(20) + end + end describe "#generate" do it "generates the correct stats" do From 49f4a535697bc1609fefa32b22ea410c2b64aaaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 11 Jan 2019 21:44:32 +0100 Subject: [PATCH 27/82] Remove duplication in web percentage stats We didn't use metaprogramming from the start because the `null_percentage_web` method had a particular behaviour. However, the behaviour (due to a typo) didn't really matter because there are no null web votes, and so the `null_percentage_web` is always zero. --- app/models/poll/stats.rb | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index 21f9fba94..cc4e5318d 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -53,21 +53,11 @@ class Poll::Stats recounts.sum(:null_amount) end - def valid_percentage_web - calculate_percentage(total_web_valid, total_valid_votes) - end - - def white_percentage_web - calculate_percentage(total_web_white, total_white_votes) - end - - def null_percentage_web - calculate_percentage(total_web_null, total_null_votes) - end - %i[valid white null].each do |type| - define_method :"#{type}_percentage_booth" do - calculate_percentage(send(:"total_booth_#{type}"), send(:"total_#{type}_votes")) + %i[web booth].each do |channel| + define_method :"#{type}_percentage_#{channel}" do + calculate_percentage(send(:"total_#{channel}_#{type}"), send(:"total_#{type}_votes")) + end end define_method :"total_#{type}_votes" do From 90fe746d27c2e0fc9bbe8f7523cae1183e2cf07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 17:08:33 +0100 Subject: [PATCH 28/82] Add geozone stats to polls --- app/models/concerns/statisticable.rb | 24 ++++++++++++++-- app/views/shared/stats/_links.html.erb | 3 ++ .../shared/stats/_participation.html.erb | 24 ++++++++++++++++ config/locales/en/stats.yml | 3 ++ config/locales/es/stats.yml | 3 ++ spec/models/poll/stats_spec.rb | 28 +++++++++++++++++++ 6 files changed, 82 insertions(+), 3 deletions(-) diff --git a/app/models/concerns/statisticable.rb b/app/models/concerns/statisticable.rb index cd24aed8e..2d588a59a 100644 --- a/app/models/concerns/statisticable.rb +++ b/app/models/concerns/statisticable.rb @@ -49,6 +49,23 @@ module Statisticable end.to_h end + def participants_by_geozone + Geozone.all.order("name").map do |geozone| + count = participants.where(geozone: geozone).count + + [ + geozone.name, + { + total: { + count: count, + percentage: calculate_percentage(count, total_participants) + }, + percentage: calculate_percentage(count, geozone.users.count) + } + ] + end.to_h + end + private def total_participants_with_gender @@ -92,9 +109,10 @@ module Statisticable class_methods do def stats_methods - %i[total_participants total_male_participants - total_female_participants total_unknown_gender_or_age - male_percentage female_percentage participants_by_age] + %i[total_participants + total_male_participants total_female_participants total_unknown_gender_or_age + male_percentage female_percentage + participants_by_age participants_by_geozone] end def stats_cache(*method_names) diff --git a/app/views/shared/stats/_links.html.erb b/app/views/shared/stats/_links.html.erb index 7b7d6d7ac..581cce4b2 100644 --- a/app/views/shared/stats/_links.html.erb +++ b/app/views/shared/stats/_links.html.erb @@ -9,4 +9,7 @@
  • <%= link_to t("stats.by_age"), "#participants_by_age" %>
  • +
  • + <%= link_to t("stats.by_geozone"), "#participants_by_geozone" %> +
  • diff --git a/app/views/shared/stats/_participation.html.erb b/app/views/shared/stats/_participation.html.erb index 88faf3575..ed9119f2a 100644 --- a/app/views/shared/stats/_participation.html.erb +++ b/app/views/shared/stats/_participation.html.erb @@ -56,4 +56,28 @@
    <%= t("polls.show.stats.votes") %>
    + +
    +

    <%= t("stats.by_geozone") %>

    + + + + + + + + + + + + <% stats[:participants_by_geozone].each do |geozone, participants| %> + + + + + + <% end %> + +
    <%= t("stats.geozone") %><%= t("stats.total") %><%= t("stats.geozone_participation") %>
    <%= geozone %><%= "#{participants[:total][:count]} (#{number_to_stats_percentage(participants[:total][:percentage])})" %><%= number_to_stats_percentage(participants[:percentage]) %>
    +
    diff --git a/config/locales/en/stats.yml b/config/locales/en/stats.yml index 947e87b11..3cb6a3385 100644 --- a/config/locales/en/stats.yml +++ b/config/locales/en/stats.yml @@ -4,9 +4,12 @@ en: total_participants: "Participants" by_gender: "Participants by gender" by_age: "Participants by age" + by_geozone: "Participants by district" men_percentage: "%{percentage} Men" women_percentage: "%{percentage} Women" age: "Age" age_more_than: "%{start} years old and older" age_range: "%{start} - %{finish} years old" total: "Total" + geozone: "District" + geozone_participation: "% District population participation" diff --git a/config/locales/es/stats.yml b/config/locales/es/stats.yml index 926a024f8..2055060bf 100644 --- a/config/locales/es/stats.yml +++ b/config/locales/es/stats.yml @@ -4,9 +4,12 @@ es: total_participants: "Participantes" by_gender: "Participación por género" by_age: "Participación por grupos de edad" + by_geozone: "Participación por distritos" men_percentage: "%{percentage} Hombres" women_percentage: "%{percentage} Mujeres" age: "Edad" age_more_than: "De %{start} y más años" age_range: "De %{start} a %{finish} años" total: "Total" + geozone: "Distrito" + geozone_participation: "% Partipación población del distrito" diff --git a/spec/models/poll/stats_spec.rb b/spec/models/poll/stats_spec.rb index 5f6e345da..2b2fe6c7b 100644 --- a/spec/models/poll/stats_spec.rb +++ b/spec/models/poll/stats_spec.rb @@ -156,6 +156,34 @@ describe Poll::Stats do 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) } + + expect(stats.participants_by_geozone.keys).to eq %w[Eastasia Eurasia Oceania] + end + + it "calculates percentage relative to total participants" do + hobbiton = create(:geozone, name: "Hobbiton") + rivendel = create(:geozone, name: "Rivendel") + + 3.times { create :poll_voter, poll: poll, user: create(:user, :level_two, geozone: hobbiton) } + 2.times { create :poll_voter, poll: poll, user: create(:user, :level_two, geozone: rivendel) } + + expect(stats.participants_by_geozone["Hobbiton"][:total]).to eq(count: 3, percentage: 60.0) + expect(stats.participants_by_geozone["Rivendel"][:total]).to eq(count: 2, percentage: 40.0) + end + + it "calculates percentage relative to the geozone population" do + midgar = create(:geozone, name: "Midgar") + + create(:poll_voter, poll: poll, user: create(:user, :level_two, geozone: midgar)) + 2.times { create :user, :level_two, geozone: midgar } + + expect(stats.participants_by_geozone["Midgar"][:percentage]).to eq(33.333) + end + end + describe "#generate" do it "generates the correct stats" do poll = create(:poll) From a9bb3eba2655d1b89a30f9adcc1322f2ec79e617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 17:19:47 +0100 Subject: [PATCH 29/82] Add headings for advanced statistics --- app/views/polls/stats.html.erb | 151 ++++++++++++++++++--------------- config/locales/en/stats.yml | 3 + config/locales/es/stats.yml | 3 + 3 files changed, 89 insertions(+), 68 deletions(-) diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index f77282b85..db9dfd83d 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -8,79 +8,94 @@
    <%= render "shared/stats/participation", stats: @stats %> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    <%= t("polls.show.stats.votes") %><%= t("polls.show.stats.web") %><%= t("polls.show.stats.booth") %><%= t("polls.show.stats.total") %>
    <%= t("polls.show.stats.valid") %> - <%= @stats[:total_web_valid] %> - (<%= @stats[:valid_percentage_web].round(2) %>%) - - <%= @stats[:total_booth_valid] %> - (<%= @stats[:valid_percentage_booth].round(2) %>%) - - <%= @stats[:total_valid_votes] %> - (<%= @stats[:total_valid_percentage].round(2) %>%) -
    <%= t("polls.show.stats.white") %> - <%= @stats[:total_web_white] %> - (<%= @stats[:white_percentage_web].round(2) %>%) - - <%= @stats[:total_booth_white] %> - (<%= @stats[:white_percentage_booth].round(2) %>%) - <%= @stats[:total_white_votes] %> - (<%= @stats[:total_white_percentage].round(2) %>%) -
    <%= t("polls.show.stats.null_votes") %> - <%= @stats[:total_web_null] %> - (<%= @stats[:null_percentage_web].round(2) %>%) - - <%= @stats[:total_booth_null] %> - (<%= @stats[:null_percentage_booth].round(2) %>%) - - <%= @stats[:total_null_votes] %> - (<%= @stats[:total_null_percentage].round(2) %>%) -
    <%= t("polls.show.stats.total") %> - <%= @stats[:total_participants_web] %> - (<%= @stats[:total_participants_web_percentage].round(2) %>%) - - <%= @stats[:total_participants_booth] %> - (<%= @stats[:total_participants_booth_percentage].round(2) %>%) - <%= @stats[:total_participants] %>
    +
    +

    <%= t("stats.advanced") %>

    + +
    +

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

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    <%= t("polls.show.stats.votes") %><%= t("polls.show.stats.web") %><%= t("polls.show.stats.booth") %><%= t("polls.show.stats.total") %>
    <%= t("polls.show.stats.valid") %> + <%= @stats[:total_web_valid] %> + (<%= @stats[:valid_percentage_web].round(2) %>%) + + <%= @stats[:total_booth_valid] %> + (<%= @stats[:valid_percentage_booth].round(2) %>%) + + <%= @stats[:total_valid_votes] %> + (<%= @stats[:total_valid_percentage].round(2) %>%) +
    <%= t("polls.show.stats.white") %> + <%= @stats[:total_web_white] %> + (<%= @stats[:white_percentage_web].round(2) %>%) + + <%= @stats[:total_booth_white] %> + (<%= @stats[:white_percentage_booth].round(2) %>%) + <%= @stats[:total_white_votes] %> + (<%= @stats[:total_white_percentage].round(2) %>%) +
    <%= t("polls.show.stats.null_votes") %> + <%= @stats[:total_web_null] %> + (<%= @stats[:null_percentage_web].round(2) %>%) + + <%= @stats[:total_booth_null] %> + (<%= @stats[:null_percentage_booth].round(2) %>%) + + <%= @stats[:total_null_votes] %> + (<%= @stats[:total_null_percentage].round(2) %>%) +
    <%= t("polls.show.stats.total") %> + <%= @stats[:total_participants_web] %> + (<%= @stats[:total_participants_web_percentage].round(2) %>%) + + <%= @stats[:total_participants_booth] %> + (<%= @stats[:total_participants_booth_percentage].round(2) %>%) + <%= @stats[:total_participants] %>
    +
    +
    diff --git a/config/locales/en/stats.yml b/config/locales/en/stats.yml index 3cb6a3385..0b1915fd3 100644 --- a/config/locales/en/stats.yml +++ b/config/locales/en/stats.yml @@ -1,6 +1,7 @@ en: stats: title: "Participation data" + advanced: "Advanced statistics" total_participants: "Participants" by_gender: "Participants by gender" by_age: "Participants by age" @@ -13,3 +14,5 @@ en: total: "Total" geozone: "District" geozone_participation: "% District population participation" + polls: + vote_by_channel: "Vote type by channel" diff --git a/config/locales/es/stats.yml b/config/locales/es/stats.yml index 2055060bf..f8f76f211 100644 --- a/config/locales/es/stats.yml +++ b/config/locales/es/stats.yml @@ -1,6 +1,7 @@ es: stats: title: "Estadísticas de participación" + advanced: "Estadísticas avanzadas" total_participants: "Participantes" by_gender: "Participación por género" by_age: "Participación por grupos de edad" @@ -13,3 +14,5 @@ es: total: "Total" geozone: "Distrito" geozone_participation: "% Partipación población del distrito" + polls: + vote_by_channel: "Votos emitidos por medio" From 8f0bccf5b36ed0d55b0ca0b5b62a90038551237b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 17:27:21 +0100 Subject: [PATCH 30/82] Add mail votes stub to poll stats So far we don't know about implemenation details. --- app/models/poll/stats.rb | 26 ++++++++++--- app/views/polls/stats.html.erb | 70 +++++++++++++++++----------------- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/app/models/poll/stats.rb b/app/models/poll/stats.rb index cc4e5318d..191139d5a 100644 --- a/app/models/poll/stats.rb +++ b/app/models/poll/stats.rb @@ -2,22 +2,26 @@ class Poll::Stats include Statisticable alias_method :poll, :resource + CHANNELS = %i[web booth mail] + def self.stats_methods super + %i[total_valid_votes total_white_votes total_null_votes total_participants_web total_web_valid total_web_white total_web_null total_participants_booth total_booth_valid total_booth_white total_booth_null + total_participants_mail total_mail_valid total_mail_white total_mail_null total_participants_web_percentage total_participants_booth_percentage - valid_percentage_web valid_percentage_booth total_valid_percentage - white_percentage_web white_percentage_booth total_white_percentage - null_percentage_web null_percentage_booth total_null_percentage] + total_participants_mail_percentage + valid_percentage_web valid_percentage_booth valid_percentage_mail total_valid_percentage + white_percentage_web white_percentage_booth white_percentage_mail total_white_percentage + null_percentage_web null_percentage_booth null_percentage_mail total_null_percentage] end def total_participants total_participants_web + total_participants_booth end - %i[web booth].each do |channel| + CHANNELS.each do |channel| define_method :"total_participants_#{channel}" do send(:"total_#{channel}_valid") + send(:"total_#{channel}_white") + @@ -53,8 +57,20 @@ class Poll::Stats recounts.sum(:null_amount) end + def total_mail_valid + 0 # TODO + end + + def total_mail_white + 0 # TODO + end + + def total_mail_null + 0 # TODO + end + %i[valid white null].each do |type| - %i[web booth].each do |channel| + CHANNELS.each do |channel| define_method :"#{type}_percentage_#{channel}" do calculate_percentage(send(:"total_#{channel}_#{type}"), send(:"total_#{type}_votes")) end diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index db9dfd83d..a97e5ec43 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -30,51 +30,53 @@ <%= t("polls.show.stats.votes") %> - <%= t("polls.show.stats.web") %> - <%= t("polls.show.stats.booth") %> + <% Poll::Stats::CHANNELS.each do |channel| %> + <%= t("polls.show.stats.#{channel}") %> + <% end %> <%= t("polls.show.stats.total") %> <%= t("polls.show.stats.valid") %> - - <%= @stats[:total_web_valid] %> - (<%= @stats[:valid_percentage_web].round(2) %>%) - - - <%= @stats[:total_booth_valid] %> - (<%= @stats[:valid_percentage_booth].round(2) %>%) - + + <% Poll::Stats::CHANNELS.each do |channel| %> + + <%= @stats[:"total_#{channel}_valid"] %> + (<%= @stats[:"valid_percentage_#{channel}"].round(2) %>%) + + <% end %> + <%= @stats[:total_valid_votes] %> (<%= @stats[:total_valid_percentage].round(2) %>%) + <%= t("polls.show.stats.white") %> - - <%= @stats[:total_web_white] %> - (<%= @stats[:white_percentage_web].round(2) %>%) - - - <%= @stats[:total_booth_white] %> - (<%= @stats[:white_percentage_booth].round(2) %>%) - + + <% Poll::Stats::CHANNELS.each do |channel| %> + + <%= @stats[:"total_#{channel}_white"] %> + (<%= @stats[:"white_percentage_#{channel}"].round(2) %>%) + + <% end %> + <%= @stats[:total_white_votes] %> (<%= @stats[:total_white_percentage].round(2) %>%) <%= t("polls.show.stats.null_votes") %> - - <%= @stats[:total_web_null] %> - (<%= @stats[:null_percentage_web].round(2) %>%) - - - <%= @stats[:total_booth_null] %> - (<%= @stats[:null_percentage_booth].round(2) %>%) - + + <% Poll::Stats::CHANNELS.each do |channel| %> + + <%= @stats[:"total_#{channel}_null"] %> + (<%= @stats[:"null_percentage_#{channel}"].round(2) %>%) + + <% end %> + <%= @stats[:total_null_votes] %> (<%= @stats[:total_null_percentage].round(2) %>%) @@ -82,14 +84,14 @@ <%= t("polls.show.stats.total") %> - - <%= @stats[:total_participants_web] %> - (<%= @stats[:total_participants_web_percentage].round(2) %>%) - - - <%= @stats[:total_participants_booth] %> - (<%= @stats[:total_participants_booth_percentage].round(2) %>%) - + + <% Poll::Stats::CHANNELS.each do |channel| %> + + <%= @stats[:"total_participants_#{channel}"] %> + (<%= @stats[:"total_participants_#{channel}_percentage"].round(2) %>%) + + <% end %> + <%= @stats[:total_participants] %> From 553af8e95b054797d9ff74563ad64a10e87aafc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javi=20Mart=C3=ADn?= Date: Fri, 4 Jan 2019 17:41:23 +0100 Subject: [PATCH 31/82] Add poll stats by channel Note we currently don't have a way to get the votes by mail, so that section is always blank. --- app/assets/images/stats_booth.png | Bin 0 -> 3082 bytes app/assets/images/stats_mail.png | Bin 0 -> 2362 bytes app/assets/images/stats_web.png | Bin 0 -> 2911 bytes app/assets/stylesheets/stats.scss | 12 ++++++++++++ app/views/polls/stats.html.erb | 17 +++++++++++++++++ config/locales/en/stats.yml | 4 ++++ config/locales/es/stats.yml | 4 ++++ 7 files changed, 37 insertions(+) create mode 100644 app/assets/images/stats_booth.png create mode 100644 app/assets/images/stats_mail.png create mode 100644 app/assets/images/stats_web.png diff --git a/app/assets/images/stats_booth.png b/app/assets/images/stats_booth.png new file mode 100644 index 0000000000000000000000000000000000000000..98d218b1ab60a2e006b9a2e25b5ea0df2fd8100b GIT binary patch literal 3082 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/stats_web.png b/app/assets/images/stats_web.png new file mode 100644 index 0000000000000000000000000000000000000000..e16835b32419bfc64e7857a61e230ca167e73c09 GIT binary patch literal 2911 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 literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/stats.scss b/app/assets/stylesheets/stats.scss index 6c2cc73c5..2992e5d05 100644 --- a/app/assets/stylesheets/stats.scss +++ b/app/assets/stylesheets/stats.scss @@ -40,6 +40,18 @@ content: image-url('stats_woman.png'); } + &.web::before { + content: image-url('stats_web.png'); + } + + &.booth::before { + content: image-url('stats_booth.png'); + } + + &.mail::before { + content: image-url('stats_mail.png'); + } + .content { display: inline-block; margin-right: $line-height * 2; diff --git a/app/views/polls/stats.html.erb b/app/views/polls/stats.html.erb index a97e5ec43..216a1a2e8 100644 --- a/app/views/polls/stats.html.erb +++ b/app/views/polls/stats.html.erb @@ -11,6 +11,9 @@

    <%= link_to t("stats.advanced"), "#advanced_statistics" %>