LabPLC/verificalo

View on GitHub
lib/vehicle_cdmx.rb

Summary

Maintainability
D
2 days
Test Coverage
# -*- coding: utf-8 -*-
require 'httparty'
require 'json'
require 'htmlentities'

class VehicleCDMX
  
  # constructor de clase
  
  def initialize (params)
    if self.validate(params) && self.extra(params)
      self.api
    end
  end
  
  # validacion de parametros
  
  def validate (params)
    unless params[:plate]
      @status = 'INVALID_PLATE'
      return false
    end
    params[:plate].gsub!(/[^0-9a-z ]/i, '')
    if params[:plate] !~ /\A[1-9a-z][0-9][0-9][a-z][a-z][a-z]\z/i
      @status = 'INVALID_PLATE'
      return false
    end
    @plate = params[:plate].upcase
    true
  end
  
  # parametros extra
  
  def extra (params)
    if params[:vin]
      @vin = params[:vin].upcase
    end
    if params[:registration]
      @registration = OpenStruct.new
      @registration.y = params[:registration][:year]
      @registration.m = params[:registration][:month]
      @registration.d = params[:registration][:day]
    end
    true
  end
  
  # consulta api
  
  def api
    begin
      url = 'http://datos.labplc.mx/movilidad/vehiculos/' + @plate + '.json'
      res = HTTParty.get(url)
      raise unless res.code == 200
    rescue
      @status = 'API_GET_ERROR'
      return false
    end
    begin
      res = JSON.parse(res.body)
      raise unless res['consulta']
      @api = res['consulta']
    rescue
      @status = 'API_JSON_ERROR'
      return false
    end
    unless @api['verificaciones'].is_a?(Array)
      if @api['verificaciones'] == 'intente_mas_tarde'
        @status = 'API_TRY_LATER'
      else
        @status = 'API_VERIFICACIONES_ERROR'
      end
      return false
    end
    unless @api['infracciones'].is_a?(Array)
      @status = 'API_INFRACCIONES_ERROR'
      return false
    end
    unless @api['tenencias'].is_a?(Hash)
      @status = 'API_TENENCIAS_ERROR'
      return false
    end
    @status = 'OK'
    true
  end
  
  # acesores basicos
  
  def error
    return false if @status == 'OK'
    @status
  end
  
  def plate
    return false if self.error
    @plate
  end
  
  def plate_last_digit
    return false if self.error
    /\d(?!.*\d)/.match(@plate)[0]
  end

  def plate_last_digit_str
    return false if self.error
    plate_last_digit_str = {
      '1' => 'uno', '2' => 'dos', '3' => 'tres', '4' => 'cuatro', 
      '5' => 'cinco', '6' => 'seis', '7' => 'siete', '8' => 'ocho',
      '9' => 'nueve', '0' => 'cero' }
    return plate_last_digit_str[self.plate_last_digit]
  end
  
  # acesores vin
  
  def user_vin?
    return true if @vin
    false
  end
  
  def user_vin
    return false unless self.user_vin?
    @vin
  end
  
  def user_vin= (v)
    @vin = v.upcase
  end
  
  def user_vin_valid?
    return false unless self.user_vin?
    if self.user_vin =~ /\A[a-z0-9]+\z/i
      return true
    end
    false
  end
  
  # acesores fecha de registro
  
  def registration_date?
    return true if @registration
    false
  end
  
  def registration_date
    return false unless self.registration_date?
    begin
      return Date.new(@registration.y.to_i,
                      @registration.m.to_i,
                      @registration.d.to_i)
    rescue
      return false
    end
    false
  end
  
  def registration_date= (s)
    date = Date.iso8601(s)
    @registration = OpenStruct.new
    @registration.y = date.year
    @registration.m = date.month
    @registration.d = date.day
  end
  
  def registration_date_valid?
    return false unless self.registration_date?
    return false unless self.registration_date
    return false if self.registration_date > Date.today
    true
  end
  
  def registration_date_str
    return false unless self.registration_date?
    I18n.localize(self.registration_date, :format => :long)
  end
  
  # verificaciones
  
  def verificaciones?
    unless @api['verificaciones'].is_a?(Array)
      return false
    end
    unless @api['verificaciones'].count > 0
      return false
    end
    true
  end
  
  def verificaciones
    return false unless self.verificaciones?
    if self.user_vin_valid?
      v = @api['verificaciones'].collect { |i| 
        i if i['vin'].upcase == self.user_vin
      }.compact
    else
      v = @api['verificaciones']
    end
    v.collect { |i| 
      i if i['vin'].upcase != 'DESCONOCIDO'
    }.compact.sort { |a, b| 
      _a = a['fecha_verificacion'].gsub(/-/, '') + a['hora_verificacion'].gsub(/:/, '')
      _b = b['fecha_verificacion'].gsub(/-/, '') + b['hora_verificacion'].gsub(/:/, '')
      _b <=> _a
    }
  end
  
  # diferente vin en verificaciones
  
  def verificaciones_vins
    return false unless self.verificaciones?
    if self.user_vin_valid?
      return [ self.user_vin ]
    else
      return @api['verificaciones'].collect { |i|
        i['vin'] if i['vin'].upcase != 'DESCONOCIDO'
      }.compact.uniq
    end
  end
  
  def verificaciones_vins?
    return false unless self.verificaciones_vins
    return true if self.verificaciones_vins.count > 1
    false
  end
  
  # verificaciones validas (sin canceladas)
  
  def verificaciones_valid
    return false unless self.verificaciones?
    self.verificaciones.collect { |i|
      i if i['cancelado'] == 'NO'
    }.compact
  end
  
  def verificaciones_valid?
    return false unless self.verificaciones_valid
    if self.verificaciones_valid.count > 0
      return true
    end
    false
  end
  
  # verificaciones aprobadas (validas - sin resultado igual a rechazo)
  
  def verificaciones_approved
    return false unless self.verificaciones_valid?
    self.verificaciones_valid.collect { |i|
      i if i['resultado'] != 'RECHAZO'
    }.compact      
  end
  
  def verificaciones_approved?
    return false unless self.verificaciones_valid?
    if self.verificaciones_approved.count > 0
      return true
    end
    false
  end
  
  # verificacion actual
  
  def verificacion_current
    return false unless self.verificaciones_valid?
    return self.verificaciones_valid[0]
  end
  
  def verificacion_current?
    return false unless self.verificacion_current
    true
  end
  
  def verificacion_current_vigency
    return false unless self.verificacion_current?
    if self.verificacion_current['equipo_gdf'] == '1'
      Date.parse(self.verificacion_current['vigencia'])
    else
      Date.parse(self.verificacion_current['vigencia']) - 1
    end
  end
  
  def verificacion_current_period
    return false unless self.verificacion_current?
    self.verificacion_current_vigency.beginning_of_month.prev_month
  end
  
  # ultima verificación aprobada
  
  def verificacion_last_approved
    return false unless self.verificaciones_approved?
    return self.verificaciones_approved[0]
  end
  
  def verificacion_last_approved?
    return false unless self.verificacion_last_approved
    true
  end
  
  # primera verficacion
  
  def verificacion_first_end
    return false unless self.registration_date_valid? 
    self.registration_date + 180
  end
  
  # estado verificacion
  
  def verificacion_ok?
    return false unless self.verificaciones_approved?
    return false if self.verificacion_expired?
    return false if self.verificacion_period?
    true
  end
  
  def verificacion_period?
    return false unless self.verificaciones_approved?
    return false if self.verificacion_expired?
    return true if self.verificacion_current_period <= Date.today
    false
  end
  
  def verificacion_expired?
    return false unless self.verificaciones_approved?
    return true if self.verificacion_current_vigency <= Date.today
    false
  end
  
  def verificacion_first_ok?
    return false unless self.registration_date_valid?
    return true if verificacion_first_end > Date.today
    false
  end
  
  def verificacion_first_expired?
    return false unless self.registration_date_valid?
    return true unless self.verificacion_first_ok?
    false
  end
  
  # strings verificacion
  
  def verificacion_current_expired_str
    return false unless self.verificacion_current?
    I18n.localize(self.verificacion_current_vigency, :format => :long)
  end

  def verificacion_current_vigency_str
    return false unless self.verificacion_current?
    I18n.localize(self.verificacion_current_vigency, :format => :long)
  end

  def verificacion_current_period_str
    return false unless self.verificaciones_approved?
    I18n.localize(self.verificacion_current_period, :format => :long)
  end

  def verificacion_current_brand_str
    return false unless self.verificacion_current?
    self.verificacion_current['marca'].gsub(/_/, ' ')
  end

  def verificacion_current_model_str
    return false unless self.verificacion_current?
    self.verificacion_current['submarca'].gsub(/_/, ' ')
  end

  def verificacion_current_year_str
    return false unless self.verificacion_current?
    self.verificacion_current['modelo']
  end

  def verificacion_current_vin_str
    return false unless self.verificacion_current?
    self.verificacion_current['vin']
  end

  def verificacion_last_approved_result
    return false unless self.verificacion_last_approved?
    self.verificacion_last_approved['resultado'].downcase
  end

  def verificacion_first_end_str
    return false unless self.registration_date_valid?
    I18n.localize(self.verificacion_first_end, :format => :long)
  end

  # historial de verificaciones

  def verificaciones_all
    return false unless self.verificaciones?
    self.verificaciones.collect { |v|
      r = OpenStruct.new
      r.date = I18n.localize(Date.parse(v['fecha_verificacion']), :format => :default)
      r.time = v['hora_verificacion'].gsub(/:\d\d$/, '')
      r.verificentro = v['verificentro']
      r.line = v['linea']
      r.fuel = v['combustible'].capitalize
      r.cert = v['certificado']
      r.cancel = v['cancelado'] == 'SI' ? true : false
      if v['equipo_gdf'] == '1'
        r.vigency = I18n.localize(Date.parse(v['vigencia']), :format => :default)
      else
        r.vigency = I18n.localize(Date.parse(v['vigencia']) - 1, :format => :default)
      end
      r.result = v['resultado']
      r.reject = v['casua_rechazo']
      r
    }
  end

  # estado hoy no circula

  def no_circula_cero?
    return false unless self.verificacion_last_approved?
    return false if self.verificacion_expired?
    return true if self.verificacion_last_approved['resultado'] =~ /cero/i
    false
  end

  def no_circula_uno?
    return false unless self.verificacion_last_approved?
    return false if self.verificacion_expired?
    return true if self.verificacion_last_approved['resultado'] =~ /uno/i
    false
  end

  def no_circula_dos?
    return false unless self.verificacion_last_approved?
    return false if self.verificacion_expired?
    return true if self.verificacion_last_approved['resultado'] =~ /dos/i
    false
  end

  def no_circula_never?
    return false if self.verificacion_last_approved?
    return true if self.verificacion_first_ok?
    false
  end
  
  def no_circula_expired?
    return true if self.verificacion_first_expired?
    return true if self.verificacion_expired?
    false
  end

  # no circula el proximo sabado?

  def no_circula_weekend?(date)
    return false if self.error
    plate_no_circula_weekend = {
      '1' => { '1' => true, '3' => true },
      '2' => { '2' => true, '4' => true },
      '3' => { '1' => true, '3' => true },
      '4' => { '2' => true, '4' => true },
      '5' => { '1' => true, '3' => true },
      '6' => { '2' => true, '4' => true },
      '7' => { '1' => true, '3' => true },
      '8' => { '2' => true, '4' => true },
      '9' => { '1' => true, '3' => true },
      '0' => { '2' => true, '4' => true },
    }
    if plate_no_circula_weekend[self.plate_last_digit.to_s][date.week_of_month.to_s]
      return true
    else
      return false
    end
  end

  # strings hoy no circula
  
  def no_circula_weekday_str
    return false if self.error
    plate_no_circula_weekday = { 
      '1' => 'jueves', '2' => 'jueves',
      '3' => 'miércoles', '4' => 'miércoles',
      '5' => 'lunes', '6' => 'lunes',
      '7' => 'martes', '8' => 'martes',
      '9' => 'viernes', '0' => 'viernes' }
    return plate_no_circula_weekday[self.plate_last_digit]
  end

  def no_circula_weekend_str
    return false if self.error
    plate_no_circula_weekend = { 
      '1' => 'primer y tercer', 
      '2' => 'segundo y cuarto',
      '3' => 'primer y tercer', 
      '4' => 'segundo y cuarto', 
      '5' => 'primer y tercer',
      '6' => 'segundo y cuarto', 
      '7' => 'primer y tercer',
      '8' => 'segundo y cuarto', 
      '9' => 'primer y tercer',
      '0' => 'segundo y cuarto' }
    return plate_no_circula_weekend[self.plate_last_digit] + ' sábado'
  end

  # infracciones

  def infracciones?
    unless @api['infracciones'].is_a?(Array)
      return false
    end
    unless @api['infracciones'].count > 0
      return false
    end
    true
  end

  def infracciones_unpaid
    return 0 unless self.infracciones?
    @api['infracciones'].count { |i| i['situacion'] != 'Pagada' }
  end

  def infracciones_all
    return false unless self.infracciones?
    unless @api['infracciones'].count > 0
      return false
    end
    @api['infracciones'].sort { |a, b| 
      _a = Date.strptime(a['fecha'], "%Y-%m-%d")
      _b = Date.strptime(b['fecha'], "%Y-%m-%d")
      _b <=> _a
    }.collect { |i|
      coder = HTMLEntities.new
      r = OpenStruct.new
      r.date = I18n.localize(Date.parse(i['fecha']), :format => :default)
      r.id = i['folio']
      r.cause = coder.decode(i['motivo'])
      r.basis = coder.decode(i['fundamento'])
      r.sanction = coder.decode(i['sancion'])
      r.status = coder.decode(i['situacion'])
      r
    }
  end

  # tenencias

  def tenencias?
    unless @api['tenencias'].is_a?(Hash)
      return false
    end
    true
  end

  def tenencias_unpaid
    return false unless self.tenencias?
    if @api['tenencias']['tieneadeudos'] == '1'
      return @api['tenencias']['adeudos'].split(/,/).count
    end
    return 0
  end
  
  def tenencias_unpaid_str
    return false unless self.tenencias?
    unpaid = @api['tenencias']['adeudos'].split(/,/)
    if unpaid.count < 1
      return false
    end
    if unpaid.count > 1
      return [unpaid[0...-1].join(', '), unpaid.last].join(' y ')
    else
      return unpaid[0]
    end
  end

  # adeudos

  def adeudos?
    return false unless self.tenencias? || self.infracciones?
    if self.tenencias_unpaid > 0 || self.infracciones_unpaid > 0
      return true
    end
    false
  end

end