From 0ec3a8e8bf2ed9da59096602506c62e31d974cd5 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 19 Sep 2016 13:31:18 +0200 Subject: [PATCH 1/8] validates voter in census --- app/models/poll/voter.rb | 15 ++++++ config/locales/activerecord.en.yml | 4 ++ config/locales/activerecord.es.yml | 4 ++ .../20160914172016_create_poll_voters.rb | 9 ++++ db/schema.rb | 21 ++++++++- lib/census_api.rb | 18 +++++-- spec/factories.rb | 26 ++++++++++ spec/models/poll/voter_spec.rb | 47 +++++++++++++++++++ 8 files changed, 140 insertions(+), 4 deletions(-) create mode 100644 app/models/poll/voter.rb create mode 100644 db/migrate/20160914172016_create_poll_voters.rb create mode 100644 spec/models/poll/voter_spec.rb diff --git a/app/models/poll/voter.rb b/app/models/poll/voter.rb new file mode 100644 index 000000000..92df28443 --- /dev/null +++ b/app/models/poll/voter.rb @@ -0,0 +1,15 @@ +class Poll + class Voter < ActiveRecord::Base + belongs_to :booth + + validate :in_census + + def in_census + errors.add(:document_number, :not_in_census) unless census_api_response.valid? + end + + def census_api_response + CensusApi.new.call(document_type, document_number) + end + end +end \ No newline at end of file diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 07791e53c..933b0f572 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -82,6 +82,10 @@ en: attributes: max_per_day: invalid: "You have reached the maximum number of private messages per day" + poll/voter: + attributes: + document_number: + not_in_census: "Document not in census" proposal: attributes: tag_list: diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index d5b7f0005..38ceeabdd 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -82,6 +82,10 @@ es: attributes: max_per_day: invalid: "Has llegado al número máximo de mensajes privados por día" + poll/voter: + attributes: + document_number: + not_in_census: "Este documento no aparece en el censo" proposal: attributes: tag_list: diff --git a/db/migrate/20160914172016_create_poll_voters.rb b/db/migrate/20160914172016_create_poll_voters.rb new file mode 100644 index 000000000..df5bb4585 --- /dev/null +++ b/db/migrate/20160914172016_create_poll_voters.rb @@ -0,0 +1,9 @@ +class CreatePollVoters < ActiveRecord::Migration + def change + create_table :poll_voters do |t| + t.integer :booth_id + t.string :document_number + t.string :document_type + end + end +end diff --git a/db/schema.rb b/db/schema.rb index becde4f8a..8a928ca79 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160803154011) do +ActiveRecord::Schema.define(version: 20160914172535) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -270,6 +270,25 @@ ActiveRecord::Schema.define(version: 20160803154011) do add_index "organizations", ["user_id"], name: "index_organizations_on_user_id", using: :btree + create_table "poll_booths", force: :cascade do |t| + t.string "name" + t.integer "poll_id" + end + + create_table "poll_officers", force: :cascade do |t| + t.integer "user_id" + end + + create_table "poll_voters", force: :cascade do |t| + t.integer "booth_id" + t.string "document_number" + t.string "document_type" + end + + create_table "polls", force: :cascade do |t| + t.string "name" + end + create_table "proposal_notifications", force: :cascade do |t| t.string "title" t.text "body" diff --git a/lib/census_api.rb b/lib/census_api.rb index 678a748c6..d80f420e7 100644 --- a/lib/census_api.rb +++ b/lib/census_api.rb @@ -74,7 +74,7 @@ class CensusApi if end_point_available? client.call(:get_habita_datos, message: request(document_type, document_number)).body else - stubbed_response_body + stubbed_response(document_type, document_number) end end @@ -97,8 +97,20 @@ class CensusApi Rails.env.staging? || Rails.env.preproduction? || Rails.env.production? end - def stubbed_response_body - {get_habita_datos_response: {get_habita_datos_return: {hay_errores: false, datos_habitante: { item: {fecha_nacimiento_string: "31-12-1980", identificador_documento: "12345678Z", descripcion_sexo: "Varón" }}, datos_vivienda: {item: {codigo_postal: "28013", codigo_distrito: "01"}}}}} + def stubbed_response(document_type, document_number) + if document_number == "12345678Z" && document_type == "1" + stubbed_valid_response + else + stubbed_invalid_response + end + end + + def stubbed_valid_response + {get_habita_datos_response: {get_habita_datos_return: {datos_habitante: { item: {fecha_nacimiento_string: "31-12-1980", identificador_documento: "12345678Z", descripcion_sexo: "Varón" }}, datos_vivienda: {item: {codigo_postal: "28013", codigo_distrito: "01"}}}}} + end + + def stubbed_invalid_response + {get_habita_datos_response: {get_habita_datos_return: {datos_habitante: {}, datos_vivienda: {}}}} end def is_dni?(document_type) diff --git a/spec/factories.rb b/spec/factories.rb index cdcfe7381..adda53948 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -259,6 +259,32 @@ FactoryGirl.define do user end + factory :poll do + sequence(:name) { |n| "Poll #{n}" } + end + + factory :poll_officer, class: 'Poll::Officer' do + user + end + + factory :poll_booth, class: 'Poll::Booth' do + sequence(:name) { |n| "Booth #{n}" } + poll + end + + factory :poll_voter, class: 'Poll::Voter' do + association :booth, factory: :budget_booth + trait :valid_document do + document_type "1" + document_number "12345678Z" + end + + trait :invalid_document do + document_type "1" + document_number "99999999A" + end + end + factory :organization do user responsible_name "Johnny Utah" diff --git a/spec/models/poll/voter_spec.rb b/spec/models/poll/voter_spec.rb new file mode 100644 index 000000000..de4536243 --- /dev/null +++ b/spec/models/poll/voter_spec.rb @@ -0,0 +1,47 @@ +require 'rails_helper' + +describe :voter do + + let(:poll) { create(:poll) } + let(:booth) { create(:poll_booth, poll: poll) } + + describe "validations" do + + it "should be valid if in census and has not voted" do + voter = build(:poll_voter, :valid_document, booth: booth) + + expect(voter).to be_valid + end + + it "should not be valid if the user is not in the census" do + voter = build(:poll_voter, :invalid_document, booth: booth) + + expect(voter).to_not be_valid + expect(voter.errors.messages[:document_number]).to eq(["Document not in census"]) + end + + it "should not be valid if the user has already voted in the same booth" do + voter1 = create(:poll_voter, :valid_document, booth: booth) + voter2 = build(:poll_voter, :valid_document, booth: booth) + + expect(voter2).to_not be_valid + expect(voter2.errors.messages[:document_number]).to eq(["José García has already voted"]) + end + + it "should not be valid if the user has already voted in another booth" do + booth1 = create(:poll_booth, poll: poll) + booth2 = create(:poll_booth, poll: poll) + + voter1 = create(:poll_voter, :valid_document, booth: booth1) + voter2 = build(:poll_voter, :valid_document, booth: booth2) + + expect(voter2).to_not be_valid + expect(voter2.errors.messages[:document_number]).to eq(["José García has already voted"]) + end + + xit "should not be valid if the user has voted via web" do + pending "Implementation for voting via web" + end + + end +end \ No newline at end of file From 48d2aaa845543346ef01bcb8f6c0e70ffd0a6539 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 19 Sep 2016 18:22:35 +0200 Subject: [PATCH 2/8] validates user has not already voted --- app/models/poll.rb | 4 ++++ app/models/poll/booth.rb | 6 ++++++ app/models/poll/voter.rb | 11 +++++++++++ config/locales/activerecord.en.yml | 1 + config/locales/activerecord.es.yml | 1 + db/migrate/20160914172535_create_poll_booths.rb | 8 ++++++++ 6 files changed, 31 insertions(+) create mode 100644 app/models/poll.rb create mode 100644 app/models/poll/booth.rb create mode 100644 db/migrate/20160914172535_create_poll_booths.rb diff --git a/app/models/poll.rb b/app/models/poll.rb new file mode 100644 index 000000000..507c848aa --- /dev/null +++ b/app/models/poll.rb @@ -0,0 +1,4 @@ +class Poll < ActiveRecord::Base + has_many :booths + has_many :voters, through: :booths, class_name: "Poll::Voter" +end \ No newline at end of file diff --git a/app/models/poll/booth.rb b/app/models/poll/booth.rb new file mode 100644 index 000000000..a05f3d547 --- /dev/null +++ b/app/models/poll/booth.rb @@ -0,0 +1,6 @@ +class Poll + class Booth < ActiveRecord::Base + belongs_to :poll + has_many :voters + end +end \ No newline at end of file diff --git a/app/models/poll/voter.rb b/app/models/poll/voter.rb index 92df28443..353d593b3 100644 --- a/app/models/poll/voter.rb +++ b/app/models/poll/voter.rb @@ -1,15 +1,26 @@ class Poll class Voter < ActiveRecord::Base belongs_to :booth + delegate :poll, to: :booth validate :in_census + validate :has_not_voted def in_census errors.add(:document_number, :not_in_census) unless census_api_response.valid? end + def has_not_voted + errors.add(:document_number, :has_voted) if has_voted? + end + def census_api_response CensusApi.new.call(document_type, document_number) end + + def has_voted? + poll.voters.where(document_number: document_number, document_type: document_type).present? + end + end end \ No newline at end of file diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 933b0f572..252feb0b5 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -86,6 +86,7 @@ en: attributes: document_number: not_in_census: "Document not in census" + has_voted: "Has already voted" proposal: attributes: tag_list: diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index 38ceeabdd..18faf7900 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -86,6 +86,7 @@ es: attributes: document_number: not_in_census: "Este documento no aparece en el censo" + has_voted: "Ya ha votado" proposal: attributes: tag_list: diff --git a/db/migrate/20160914172535_create_poll_booths.rb b/db/migrate/20160914172535_create_poll_booths.rb new file mode 100644 index 000000000..a474aba49 --- /dev/null +++ b/db/migrate/20160914172535_create_poll_booths.rb @@ -0,0 +1,8 @@ +class CreatePollBooths < ActiveRecord::Migration + def change + create_table :poll_booths do |t| + t.string :name + t.integer :poll_id + end + end +end From 88e7af70d05d9d3490b94b41fd0dbba04c0159c3 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 19 Sep 2016 18:37:37 +0200 Subject: [PATCH 3/8] displays voter name if already voted --- app/models/poll/voter.rb | 8 ++++++-- config/locales/activerecord.en.yml | 2 +- config/locales/activerecord.es.yml | 2 +- lib/census_api.rb | 6 +++++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/models/poll/voter.rb b/app/models/poll/voter.rb index 353d593b3..936d8af3b 100644 --- a/app/models/poll/voter.rb +++ b/app/models/poll/voter.rb @@ -11,16 +11,20 @@ class Poll end def has_not_voted - errors.add(:document_number, :has_voted) if has_voted? + errors.add(:document_number, :has_voted, name: name) if has_voted? end def census_api_response - CensusApi.new.call(document_type, document_number) + @census ||= CensusApi.new.call(document_type, document_number) end def has_voted? poll.voters.where(document_number: document_number, document_type: document_type).present? end + def name + @census.name + end + end end \ No newline at end of file diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 252feb0b5..32f4ef78f 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -86,7 +86,7 @@ en: attributes: document_number: not_in_census: "Document not in census" - has_voted: "Has already voted" + has_voted: "%{name} has already voted" proposal: attributes: tag_list: diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index 18faf7900..a9047df7b 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -86,7 +86,7 @@ es: attributes: document_number: not_in_census: "Este documento no aparece en el censo" - has_voted: "Ya ha votado" + has_voted: "%{name} ya ha votado" proposal: attributes: tag_list: diff --git a/lib/census_api.rb b/lib/census_api.rb index d80f420e7..eadfc549e 100644 --- a/lib/census_api.rb +++ b/lib/census_api.rb @@ -61,6 +61,10 @@ class CensusApi end end + def name + "#{data[:datos_habitante][:item][:nombre]} #{data[:datos_habitante][:item][:apellido1]}" + end + private def data @@ -106,7 +110,7 @@ class CensusApi end def stubbed_valid_response - {get_habita_datos_response: {get_habita_datos_return: {datos_habitante: { item: {fecha_nacimiento_string: "31-12-1980", identificador_documento: "12345678Z", descripcion_sexo: "Varón" }}, datos_vivienda: {item: {codigo_postal: "28013", codigo_distrito: "01"}}}}} + {get_habita_datos_response: {get_habita_datos_return: {datos_habitante: { item: {fecha_nacimiento_string: "31-12-1980", identificador_documento: "12345678Z", descripcion_sexo: "Varón", nombre: "José", apellido1: "García" }}, datos_vivienda: {item: {codigo_postal: "28013", codigo_distrito: "01"}}}}} end def stubbed_invalid_response From 4c08058f80fdd710fa7050ede646d1bc2c0873db Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 19 Sep 2016 18:54:02 +0200 Subject: [PATCH 4/8] updates db schema --- db/schema.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 8a928ca79..e68f466ce 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -275,10 +275,6 @@ ActiveRecord::Schema.define(version: 20160914172535) do t.integer "poll_id" end - create_table "poll_officers", force: :cascade do |t| - t.integer "user_id" - end - create_table "poll_voters", force: :cascade do |t| t.integer "booth_id" t.string "document_number" From 55f4a6bbae019fdd8fd9f497b649e51ac14339c5 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 19 Sep 2016 18:55:16 +0200 Subject: [PATCH 5/8] removes officer factory --- spec/factories.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/factories.rb b/spec/factories.rb index adda53948..48162288d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -263,10 +263,6 @@ FactoryGirl.define do sequence(:name) { |n| "Poll #{n}" } end - factory :poll_officer, class: 'Poll::Officer' do - user - end - factory :poll_booth, class: 'Poll::Booth' do sequence(:name) { |n| "Booth #{n}" } poll From e0951a2f2d968e76f8d065eb4899cd703d12f110 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Mon, 19 Sep 2016 18:56:10 +0200 Subject: [PATCH 6/8] adds spacing to factories --- spec/factories.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/factories.rb b/spec/factories.rb index 48162288d..cea46a0bc 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -270,6 +270,7 @@ FactoryGirl.define do factory :poll_voter, class: 'Poll::Voter' do association :booth, factory: :budget_booth + trait :valid_document do document_type "1" document_number "12345678Z" From a6384af527126e61c972ce3fe791b7ee4259162e Mon Sep 17 00:00:00 2001 From: rgarcia Date: Tue, 20 Sep 2016 11:44:43 +0200 Subject: [PATCH 7/8] updates query to be more efficient --- app/models/poll/voter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/poll/voter.rb b/app/models/poll/voter.rb index 936d8af3b..a517c3f34 100644 --- a/app/models/poll/voter.rb +++ b/app/models/poll/voter.rb @@ -19,7 +19,7 @@ class Poll end def has_voted? - poll.voters.where(document_number: document_number, document_type: document_type).present? + poll.voters.where(document_number: document_number, document_type: document_type).exists? end def name From fd220bdf22ab0db91de0af7adae386b00bc59cd0 Mon Sep 17 00:00:00 2001 From: rgarcia Date: Tue, 20 Sep 2016 13:49:48 +0200 Subject: [PATCH 8/8] fixes specs --- spec/features/management/document_verifications_spec.rb | 4 ++-- spec/features/management/email_verifications_spec.rb | 4 ++-- spec/features/management/managed_users_spec.rb | 4 ++-- spec/features/management/users_spec.rb | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/features/management/document_verifications_spec.rb b/spec/features/management/document_verifications_spec.rb index 674349815..130630762 100644 --- a/spec/features/management/document_verifications_spec.rb +++ b/spec/features/management/document_verifications_spec.rb @@ -47,7 +47,7 @@ feature 'DocumentVerifications' do scenario 'Verifying a user which does exists in the census but not in the db redirects allows sending an email' do visit management_document_verifications_path - fill_in 'document_verification_document_number', with: '1234' + fill_in 'document_verification_document_number', with: '12345678Z' click_button 'Check' expect(page).to have_content "Please introduce the email used on the account" @@ -66,7 +66,7 @@ feature 'DocumentVerifications' do expect_any_instance_of(Verification::Management::Document).to receive(:under_sixteen?).and_return(true) visit management_document_verifications_path - fill_in 'document_verification_document_number', with: '1234' + fill_in 'document_verification_document_number', with: '12345678Z' click_button 'Check' expect(page).to have_content "You must be over 16 to verify your account." diff --git a/spec/features/management/email_verifications_spec.rb b/spec/features/management/email_verifications_spec.rb index 22987f317..d68d9bd5f 100644 --- a/spec/features/management/email_verifications_spec.rb +++ b/spec/features/management/email_verifications_spec.rb @@ -8,7 +8,7 @@ feature 'EmailVerifications' do user = create(:user) visit management_document_verifications_path - fill_in 'document_verification_document_number', with: '1234' + fill_in 'document_verification_document_number', with: '12345678Z' click_button 'Check' expect(page).to have_content "Please introduce the email used on the account" @@ -30,7 +30,7 @@ feature 'EmailVerifications' do expect(page).to_not have_link "Verify my account" expect(page).to have_content "Account verified" - expect(user.reload.document_number).to eq('1234') + expect(user.reload.document_number).to eq('12345678Z') expect(user).to be_level_three_verified end diff --git a/spec/features/management/managed_users_spec.rb b/spec/features/management/managed_users_spec.rb index 78010bc39..6ac04b137 100644 --- a/spec/features/management/managed_users_spec.rb +++ b/spec/features/management/managed_users_spec.rb @@ -57,7 +57,7 @@ feature 'Managed User' do user = create(:user) visit management_document_verifications_path - fill_in 'document_verification_document_number', with: '1234' + fill_in 'document_verification_document_number', with: '12345678Z' click_button 'Check' within(".account-info") do @@ -88,7 +88,7 @@ feature 'Managed User' do login_as_manager visit management_document_verifications_path - fill_in 'document_verification_document_number', with: '1234' + fill_in 'document_verification_document_number', with: '12345678Z' click_button 'Check' expect(page).to have_content "Please introduce the email used on the account" diff --git a/spec/features/management/users_spec.rb b/spec/features/management/users_spec.rb index 1a0618e60..694720cc0 100644 --- a/spec/features/management/users_spec.rb +++ b/spec/features/management/users_spec.rb @@ -9,7 +9,7 @@ feature 'Users' do scenario 'Create a level 3 user from scratch' do visit management_document_verifications_path - fill_in 'document_verification_document_number', with: '1234' + fill_in 'document_verification_document_number', with: '12345678Z' click_button 'Check' expect(page).to have_content "Please introduce the email used on the account" @@ -45,10 +45,10 @@ feature 'Users' do end scenario 'Delete a level 2 user account from document verification page', :js do - level_2_user = create(:user, :level_two, document_number: 13579) + level_2_user = create(:user, :level_two, document_number: "12345678Z") visit management_document_verifications_path - fill_in 'document_verification_document_number', with: '13579' + fill_in 'document_verification_document_number', with: '12345678Z' click_button 'Check' expect(page).to_not have_content "This user account is already verified." @@ -62,7 +62,7 @@ feature 'Users' do expect(level_2_user.reload.erase_reason).to eq "Deleted by manager: manager_user_#{Manager.last.user_id}" visit management_document_verifications_path - fill_in 'document_verification_document_number', with: '13579' + fill_in 'document_verification_document_number', with: '12345678Z' click_button 'Check' expect(page).to have_content "no user account associated to it"