adds officer residence check and user voting

This commit is contained in:
rgarcia
2017-01-29 00:36:20 +01:00
parent 51be80eedc
commit 6c34599e1e
30 changed files with 570 additions and 89 deletions

View File

@@ -7,6 +7,6 @@ class Officing::BaseController < ApplicationController
skip_authorization_check
def verify_officer
raise CanCan::AccessDenied unless current_user.try(:poll_officer?) || current_user.try(:administrator?)
end
raise CanCan::AccessDenied unless current_user.try(:poll_officer?) || current_user.try(:administrator?)
end
end

View File

@@ -0,0 +1,21 @@
class Officing::ResidenceController < Officing::BaseController
def new
@residence = Officing::Residence.new
end
def create
@residence = Officing::Residence.new(residence_params)
if @residence.save
redirect_to new_officing_voter_path(id: @residence.user.id), notice: t("officing.residence.flash.create")
else
render :new
end
end
private
def residence_params
params.require(:residence).permit(:document_number, :document_type, :date_of_birth)
end
end

View File

@@ -1,6 +1,25 @@
class Officing::VotersController < Officing::BaseController
respond_to :html, :js
def new
@user = User.find(params[:id])
@polls = Poll.current # fix and use answerable_by(@user)
end
def create
@poll = Poll.find(voter_params[:poll_id])
@user = User.find(voter_params[:user_id])
@voter = Poll::Voter.new(document_type: @user.document_type,
document_number: @user.document_number,
user: @user,
poll: @poll)
@voter.save!
end
private
def voter_params
params.require(:voter).permit(:poll_id, :user_id)
end
end

View File

@@ -0,0 +1,107 @@
class Officing::Residence
include ActiveModel::Model
include ActiveModel::Dates
include ActiveModel::Validations::Callbacks
attr_accessor :user, :officer, :document_number, :document_type, :date_of_birth
before_validation :call_census_api
validates_presence_of :document_number
validates_presence_of :document_type
validates_presence_of :date_of_birth
validate :allowed_age
validate :residence_in_madrid
def initialize(attrs={})
self.date_of_birth = parse_date('date_of_birth', attrs)
attrs = remove_date('date_of_birth', attrs)
super
clean_document_number
end
def save
return false unless valid?
if user_exists?
self.user = find_user_by_document
else
user_params = {
document_number: document_number,
document_type: document_type,
geozone: self.geozone,
date_of_birth: date_of_birth.to_datetime,
gender: gender,
residence_verified_at: Time.current,
verified_at: Time.current,
erased_at: Time.current,
password: random_password,
terms_of_service: '1',
email: nil
}
self.user = User.create!(user_params)
end
end
def user_exists?
find_user_by_document.present?
end
def find_user_by_document
User.where(document_number: document_number,
document_type: document_type).first
end
def residence_in_madrid
return if errors.any?
unless residency_valid?
errors.add(:residence_in_madrid, false)
end
end
def allowed_age
return if errors[:date_of_birth].any?
unless allowed_age?
errors.add(:date_of_birth, I18n.t('verification.residence.new.error_not_allowed_age'))
end
end
def allowed_age?
self.date_of_birth <= User.minimum_required_age.years.ago
end
def geozone
Geozone.where(census_code: district_code).first
end
def district_code
@census_api_response.district_code
end
def gender
@census_api_response.gender
end
private
def call_census_api
@census_api_response = CensusApi.new.call(document_type, document_number)
end
def residency_valid?
@census_api_response.valid? &&
@census_api_response.date_of_birth == date_of_birth
end
def clean_document_number
self.document_number = self.document_number.gsub(/[^a-z0-9]+/i, "").upcase unless self.document_number.blank?
end
def random_password
(0...20).map { ('a'..'z').to_a[rand(26)] }.join
end
end

View File

@@ -44,6 +44,10 @@ class Poll < ActiveRecord::Base
current.joins(:geozones).where('geozone_restricted = ? or geozones.id = ?', false, user.geozone_id)
end
def votable_by?(user)
!document_has_voted?(user.document_number, user.document_type)
end
def document_has_voted?(document_number, document_type)
voters.where(document_number: document_number, document_type: document_type).exists?
end

View File

@@ -14,6 +14,6 @@ class Poll::Answer < ActiveRecord::Base
scope :by_question, -> (question_id) { where(question_id: question_id) }
def record_voter_participation
Poll::Voter.create_from_user(author, {poll_id: poll_id})
Poll::Voter.create!(user: author, poll: poll)
end
end

View File

@@ -1,44 +1,53 @@
class Poll
class Voter < ActiveRecord::Base
belongs_to :poll
belongs_to :user
belongs_to :geozone
belongs_to :booth_assignment
validates :poll, presence: true
validates :poll_id, presence: true
validates :user_id, presence: true
validates :geozone_id, presence: true
validates :gender, presence: true
validates :age, presence: true
validates :document_number, presence: true, uniqueness: { scope: [:poll_id, :document_type], message: :has_voted }
def census_api_response
@census_api_response ||= CensusApi.new.call(document_type, document_number)
before_validation :set_demographic_info, :set_document_info
def set_demographic_info
return unless user.present?
self.gender = user.gender
self.age = user.age
self.geozone = user.geozone
end
def in_census?
census_api_response.valid?
end
def set_document_info
return unless user.present?
def fill_stats_fields
if in_census?
self.gender = census_api_response.gender
self.geozone_id = Geozone.select(:id).where(census_code: census_api_response.district_code).first.try(:id)
self.age = voter_age(census_api_response.date_of_birth)
end
end
def self.create_from_user(user, options = {})
poll_id = options[:poll_id]
booth_assignment_id = options[:booth_assignment_id]
Voter.create(
document_type: user.document_type,
document_number: user.document_number,
poll_id: poll_id,
booth_assignment_id: booth_assignment_id,
gender: user.gender,
geozone_id: user.geozone_id,
age: user.age
)
self.document_type = user.document_type
self.document_number = user.document_number
end
private
def in_census?
census_api_response.valid?
end
def census_api_response
@census_api_response ||= CensusApi.new.call(document_type, document_number)
end
def fill_stats_fields
if in_census?
self.gender = census_api_response.gender
self.geozone_id = Geozone.select(:id).where(census_code: census_api_response.district_code).first.try(:id)
self.age = voter_age(census_api_response.date_of_birth)
end
end
def voter_age(dob)
if dob.blank?
nil

View File

@@ -2,7 +2,7 @@
<ul id="officing_menu">
<li <%= "class=active" if controller_name == "voters" %>>
<%= link_to new_officing_poll_voter_path(Poll.last) do %>
<%= link_to new_officing_residence_path do %>
<span class="icon-user"></span>
<%= t("officing.menu.voters") %>
<% end %>

View File

@@ -0,0 +1,14 @@
<% if @residence.errors[:residence_in_madrid].present? %>
<div id="error_explanation" data-alert class="callout alert" data-closable>
<button class="close-button" aria-label="<%= t("application.close") %>" type="button" data-close>
<span aria-hidden="true">&times;</span>
</button>
<%= t("officing.residence.new.error_verifying_census") %>
</div>
<% else %>
<%= render "shared/errors",
resource: @residence,
message: t("officing.residence.new.form_errors") %>
<% end %>

View File

@@ -0,0 +1,26 @@
<h2><%= t("officing.residence.new.title") %></h2>
<div class="row">
<div class="small-12 medium-6 column">
<%= form_for @residence, as: "residence", url: officing_residence_path do |f| %>
<%= render "errors" %>
<%= f.label t("officing.residence.new.document_type_label") %>
<%= f.select :document_type, document_types, prompt: "", label: false %>
<%= f.text_field :document_number,
placeholder: t("officing.residence.new.document_number") %>
<div class="date-of-birth small-12 medium-6 clear">
<%= f.label t("verification.residence.new.date_of_birth") %>
<%= f.date_select :date_of_birth,
prompt: true,
start_year: 1900, end_year: 16.years.ago.year,
label: false %>
</div>
<input type="submit" value="<%= t("officing.residence.new.submit") %>" class="button">
<% end %>
</div>
</div>

View File

@@ -0,0 +1,3 @@
<div class="callout warning">
<%= t("officing.voters.show.error_already_voted") %>
</div>

View File

@@ -0,0 +1,7 @@
<div class="small-12 medium-6 large-4">
<%= form_for @user, as: :voter, url: officing_voters_path, method: :post, remote: true do |f| %>
<%= f.hidden_field :poll_id, value: poll.id %>
<%= f.hidden_field :user_id, value: @user.id %>
<%= f.submit t("officing.voters.show.submit"), class: "button success expanded" %>
<% end %>
</div>

View File

@@ -0,0 +1,3 @@
<div class="callout success">
<%= t("officing.voters.show.success") %>
</div>

View File

@@ -0,0 +1 @@
$("#<%= dom_id(@poll) %> #actions").html('<%= j render("voted") %>');

View File

@@ -1,20 +1,15 @@
<h2><%= t("officing.voters.new.title") %></h2>
<div class="row">
<div class="small-12 medium-6 column">
<form>
<% @polls.each do |poll| %>
<div id="<%= dom_id(poll) %>">
<div><%= poll.name %></div>
<label><%= t("officing.voters.new.document_type_label") %></label>
<select>
<option><%= t("officing.voters.new.document_type.spanish_id") %></option>
<option><%= t("officing.voters.new.document_type.residence_card") %></option>
<option><%= t("officing.voters.new.document_type.passport") %></option>
</select>
<label><%= t("officing.voters.new.document_number") %></label>
<input type="text" placeholder="<%= t("officing.voters.new.document_number") %>">
<input type="submit" value="<%= t("officing.voters.new.submit") %>" class="button">
</form>
<div id="actions">
<% if poll.votable_by?(@user) %>
<%= render "can_vote", poll: poll %>
<% else %>
<%= render "already_voted" %>
<% end %>
</div>
</div>
</div>
<% end %>

View File

@@ -1,30 +0,0 @@
<h2><%= t("officing.voters.show.title") %></h2>
<!-- IF CAN'T VOTE -->
<div class="callout alert">
<%= t("officing.voters.show.error_verifying_census") %>
</div>
<!-- IF ALREADY VOTED -->
<div class="callout warning">
<%= t("officing.voters.show.error_already_voted") %>
</div>
<!-- IF CAN VOTE -->
<div class="callout success">
<%= t("officing.voters.show.can_participate") %>
</div>
<div class="small-12 medium-6 large-4">
<form>
<input type="submit" value="<%= t("officing.voters.show.submit") %>" class="button success expanded">
</form>
</div>
<div class="callout success">
<%= t("officing.voters.show.success") %>
</div>