meurio/verdade-ou-consequencia

View on GitHub
app/models/candidate.rb

Summary

Maintainability
B
6 hrs
Test Coverage
class Candidate < ActiveRecord::Base
  attr_accessible :born_at, :male, :name, :nickname, :number, :party_id, :party, :email, :mobile_phone, :bio, :finished_at, :group_id, :short_url, :politician, :occupation, :scholarity, :city_id, :cpf, :electoral_title

  validates :nickname, :email, :number, :party_id, :cpf, :born_at, :city_id, :presence => true
  validate :verify_tse_data

  belongs_to :party
  belongs_to :city
  
  has_many :answers, :as => :responder
  has_many :users
  before_create { self.token = Digest::SHA1.hexdigest("#{Time.now.to_s}#{self.number}") }
 
  scope :by_age,          ->(start, end_at) { where(['EXTRACT(year from current_date) - EXTRACT(year from born_at) 
                                                      BETWEEN ? AND ?', start, end_at]) }
  scope :by_gender,       ->(*genders)      { where(male: genders)            } 
  scope :by_scholarity,   ->(*scholarities) { where(scholarity: scholarities) }
  scope :by_reelection,   ->(*politicians)  { where(politician: politicians)  }

  scope :finished, where('finished_at IS NOT NULL')

  before_save :corrige_dados
  before_create :corrige_dados

  def self.assign_next_group candidate
    if candidate && candidate.group_id.nil?
      if Candidate.where(:group_id => 1).count <= Candidate.where(:group_id => 2).count
        candidate.update_attribute :group_id, 1
      else
        candidate.update_attribute :group_id, 2
      end
    end
  end

  def self.match_for_user user_id, options = {:party_id => nil, :union_id => nil}
    candidates = 
      Candidate.
      select("candidates.id, parties.symbol as symbol, name, nickname, round(sum((
             CASE 
             WHEN answers.short_answer = ua.short_answer 
              THEN 100 
             WHEN ua.short_answer IS NULL 
              THEN NULL
             WHEN answers.short_answer IS NULL
              THEN NULL ELSE 0 END)::numeric * ua.weight)::numeric / (CASE sum(ua.weight) WHEN 0 THEN 1 ELSE sum(ua.weight) END)) as score").
      joins(:answers).
      joins(:party).
      joins("LEFT JOIN answers ua ON ua.question_id = answers.question_id AND ua.responder_type = 'User'").
      where("ua.responder_id = ?", user_id)
    if options[:party_id] then candidates = candidates.where(:party_id => options[:party_id]) end
    if options[:union_id] then candidates = candidates.where("parties.union_id = ?", options[:union_id]) end
    connection.select_all(candidates.order("score DESC").group("candidates.name, candidates.nickname, candidates.id, symbol"))
  end

  def party_union
    u = Union.joins(:parties).where("parties.id = #{party.id} and unions.city_id = #{city_id}")
    (u.count > 0)?u[0]:nil
  end

  def gang
    if party_union
      result = party_union.parties.sort{|a,b| a.score-b.score}.slice(0,6).map{|p| select_one_of_tse_worsts p.id}
    else
      result = [ select_one_of_tse_worsts(self.party_id) ]
    end
    result.select{|v|v != nil}
  end

  def verify_tse_data
      # registros = TseData.where("cpf = ? and \"number\" = ? and city_id = ? and born_at = ?", cpf, number, city_id, born_at )
      _cpf = cpf.gsub /\D/, ''
      registros = TseData.where("cpf = ? and born_at = ?", _cpf, born_at )
      errors.add(:cpf, "Dados passados não correspondem aos dados fornecidos pelo TSE") if (registros == [])
  end

  def rank
      return 4;
  end
        
  def picture
      (User.find id).picture
  end

  def vote_intension
    User.where("candidate_id = #{id}").count
  end

  def email_ativo
    e = email ? email : ( User.find id ).email
    e.strip.downcase
  end

  def self.verify_merge_fields
      campos = api_client.lists(list_id).merge_fields.retrieve
      [
        {:name => "NOME", :tipo => "text"},
        {:name => "NOME_URNA", :tipo => "text"},
        {:name => "NUMERO", :tipo => "number"},
        {:name => "PARTIDO", :tipo => "text"},
        {:name => "CIDADE", :tipo => "text"},
        {:name => "UF", :tipo => "text"}
      ].each{ |c|
        cmp = campos.select{|cp| cp['tag'] == c[:name]}
        if cmp.count == 0
          api_client.lists(list_id).merge_fields.create body: {
            name: c[:name],
            tag: c[:name],
            type: c[:tipo]
          }
        end
      }
  end

  def self.subscribe_all
    lista_segmentos = segment_list
    cands = Candidate.where("nickname is not null and party_id is not null and city_id is not null").
      each{ |c|
        alvo = ''
        if c.city.name == 'São Paulo' and c.city.state == 'SP'
          alvo = :sampa
        elsif c.city.name == 'Rio de Janeiro' and c.city.state == 'RJ'
          alvo = :rio
        elsif c.city.name == 'Porto Alegre' and c.city.state == 'RS'
          alvo = :poa
        elsif c.city.name == 'Recife' and c.city.state == 'PE'
          alvo = :recife
        else
          alvo = :brasil
        end
        begin
          api_client.lists(list_id).members(Digest::MD5.hexdigest(c.email_ativo)).upsert(
            body: {
              email_address: c.email_ativo,
              status: "subscribed",
              merge_fields: {
                CIDADE: c.city ? "#{c.city.name}" : '',
                UF: c.city ? "#{c.city.state}" : '',
                FNAME: c.nickname,
                NOME_URNA: c.nickname,
                NUMERO: c.number,
                PARTIDO: c.party ? c.party.symbol : ''
              }              
            })
          api_client.lists(list_id).segments(lista_segmentos[alvo]).members.create body:{email_address: c.email_ativo, status: :subscribed}
        rescue Exception => e
          logger.error(e)
        end
      }

  end

  private

  def self.segment_list
    r = api_client.lists(list_id).segments.retrieve['segments']
    
    hash_segmentos = {
      sampa: insert_or_identify_segment(r, /^c_001/ , 'c_001 - Lista de candidatos de SÃO PAULO'),
      rio: insert_or_identify_segment(r, /^c_002/ , 'c_002 - Lista de candidatos de RIO DE JANEIRO'),
      poa: insert_or_identify_segment(r, /^c_003/ , 'c_003 - Lista de candidatos de POA (PORTO ALEGRE)'),
      recife: insert_or_identify_segment(r, /^c_004/ , 'c_004 - Lista de candidatos de RECIFE'),
      brasil: insert_or_identify_segment(r, /^c_005/ , 'c_005 - Lista de candidatos de RESTANTE DO BRASIL')
    }
  end

  def self.insert_or_identify_segment segmentos, match, titulo
    id = ''
    ss =  segmentos.select{|s| s['name'].match(match) }
    if ss.count == 0
      begin
        id = api_client.lists(list_id).segments.create(body: { name: titulo, static_segment: [] })['id']
      rescue Exception => e
        logger.error(e)
      end
    else
      id = ss[0]['id']
    end
    id
  end

  def self.api_client
    Gibbon::Request.new
  end

  def self.list_id
     # "568b99bd67"
     # ENV['MAILCHIMP_LIST_ID']    
     "56203dedd8"
  end

  def select_one_of_tse_worsts party_id_to_search
    tse = TseData.joins(:party)
      .where("tse_data.city_id = #{city_id} and tse_data.male='true' and (party_id = #{party_id_to_search})")
      .order("(100 - extract(year from age(tse_data.born_at))) * parties.score, parties.score")
      .limit(15)

    tse[Random.rand(tse.size)] if tse != []
  end

  def corrige_dados
    self.mobile_phone.gsub! /\D/, '' if self.mobile_phone != nil
    self.cpf.gsub! /\D/, '' if self.cpf != nil
    self.electoral_title.gsub! /\D/, '' if self.electoral_title != nil
  end
end