adds officer residence check and user voting
This commit is contained in:
@@ -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
|
||||
21
app/controllers/officing/residence_controller.rb
Normal file
21
app/controllers/officing/residence_controller.rb
Normal 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
|
||||
@@ -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
|
||||
107
app/models/officing/residence.rb
Normal file
107
app/models/officing/residence.rb
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 %>
|
||||
|
||||
14
app/views/officing/residence/_errors.html.erb
Normal file
14
app/views/officing/residence/_errors.html.erb
Normal 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">×</span>
|
||||
</button>
|
||||
<%= t("officing.residence.new.error_verifying_census") %>
|
||||
</div>
|
||||
|
||||
<% else %>
|
||||
<%= render "shared/errors",
|
||||
resource: @residence,
|
||||
message: t("officing.residence.new.form_errors") %>
|
||||
<% end %>
|
||||
26
app/views/officing/residence/new.html.erb
Normal file
26
app/views/officing/residence/new.html.erb
Normal 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>
|
||||
3
app/views/officing/voters/_already_voted.html.erb
Normal file
3
app/views/officing/voters/_already_voted.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="callout warning">
|
||||
<%= t("officing.voters.show.error_already_voted") %>
|
||||
</div>
|
||||
7
app/views/officing/voters/_can_vote.html.erb
Normal file
7
app/views/officing/voters/_can_vote.html.erb
Normal 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>
|
||||
3
app/views/officing/voters/_voted.html.erb
Normal file
3
app/views/officing/voters/_voted.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="callout success">
|
||||
<%= t("officing.voters.show.success") %>
|
||||
</div>
|
||||
1
app/views/officing/voters/create.js.erb
Normal file
1
app/views/officing/voters/create.js.erb
Normal file
@@ -0,0 +1 @@
|
||||
$("#<%= dom_id(@poll) %> #actions").html('<%= j render("voted") %>');
|
||||
@@ -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 %>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user