Skip to content
This repository has been archived by the owner on Nov 7, 2019. It is now read-only.

Como contribuir

rob edited this page Sep 2, 2014 · 2 revisions

¿Cómo agrego los datos de mis diputados?

Para poder tener tus diputados locales en la base de datos necesitamos tres cosas, como mínimo:

  1. La lista de distritos electorales locales y sus secciones federales correspondientes,
  2. La lista de comisiones del congreso local, y
  3. la lista de diputados locales

Puedes seguir esta guía, en la que asumimos que todo puede salir haciendo un crawler de la página de tu congreso. Si ya sabes lo que estás haciendo y tienes los datos en otro formato, igual lee esta página, y luego échate un clavado a los modelos.

Antes de empezar

Para poder correr algunos de los comandos, sería util dar de alta las carpetas de tu entidad. En bin/camaras podemos crear la siguiente estructura:

- camaras
    - diputados
    - senado
    - 09-Distrito-Federal
        - actor.rb
        - asistencias.rb
        - comisiones.rb
        - distritos.rb
        - endpoints.rb
        - lista.rb

dónde el nombre de la carpeta de nuestra entidad sea compuesto por: índice de la entidad-nombre de la entidad. El índice debe de estar espaciado por el caracter 0 para que se ordenen correctamente en la terminal. Estamos en 2014, un sort por lenguaje natural es demasiado pedir.

tucongreso.gob.mx

No, no es una dirección real. Pero si tu congreso tiene una página con los datos accesibles (no imágenes), de tus representantes, podemos usar las librerías que tenemos para ingestar los datos y mantenerlos actualizados sin necesidad de andarnos mandando .xls por correo. UGH.

Existe la posibilidad de que nos entreguen HTML hecho con las patas, o peor, frontpage. Prepárate para el reto.

IE testing time!

Endpoints.rb

Para poder crear un crawler, es necesario que creemos el archivo endpoints.rb, que debe de declarar un module con el nombre de tu entidad, algo así:

module Parser
    
    # tu módulo se debe llamar igual a la carpeta, reemplazando
    # guiones por espacios, sin incluir el índice de la entidad
    module DistritoFederal

        def self.endpoints
            {
                # Requerido: El dominio de tu congreso 
                base: 'http://tucongreso.entidad.gob.mx/',
                # Opcional
                lista: 'http://tucongreso.entidad.gob.mx/lista_de_diputados',
                # El URL para ver un actor
                # JAJAJA, un congreso con API
                actor: 'http://api.tucongreso.entidad.gob.mx/diputado/{{id}}.json',
                # El URL para ver la lista de comisiones
                lista_comisiones: 'http://tucongreso.entidad.gob.mx/lista_comisiones/{{id}' 
            }
        end

    end

end

Distritos Electorales

Cada distrito local está compuesto por una lista de secciones federales, las cuales ya se encuentran en la base de datos. Un distrito se ve así:

{
    "_id": "dl-9-12", // el id único de este distrito
    "tipo": "local", // El tipo de distrito, 
    "entidad": "entidad_numérica", // El índice numérico de la entidad, por ejemplo 9 para el DF
    "secciones": ["<id_seccion_federal>..."], // Un array de las secciones
}

El _id de cada distrito está compuesto por: tipo de distrito-entidad._id-distrito dónde:

  • tipo de distrito puede ser uno de [dl|df|sf], por ejemplo, "dl" para _D_iputado _L_ocal
  • entidad._id es el índice numérico del estado
  • distrito es el id de dicho distrito local como entero, nada de números romanos, por favor. Te estoy viendo, ALDF.

Por otro lado, secciones es una lista de ids de secciones electorales, correspondientes a aquellas del Marco Geográfico Nacional, que componen y delimitan la geografía del distrito.

Ingesta

Supongamos que nuestro instituto electoral local es bien chido y nos entrega un archivo JSON en el que vienen todos estos datos ya peinados. Anden, vayan a darle un abrazo a los trabajadores de la oficina de información pública de su IE, se lo merecen. Ya que hayan apapachado, así se podría ingestar dicha lista:

#!/usr/bin/env ruby
# encoding: utf-8

# Incluimos las librerías que usaremos
require_relative '../common.rb'

listaDistritos = JSON.parse(File.open('./data/distritos.json'), symbolize_keys: true)

#Asumiendo que éstos son un array del ejemplo anterior
listaDistritos.each do |distrito|
    Distrito.create!(distrito)
end

Comisiones

Es posible que las comisiones de tu congreso local estén disponibles desde la página del mismo, y en este caso podemos hacer un crawler, que se llamará comisiones.rb

Debemos ingestar las comisiones antes de agregar actores!

Asumiendo que los listados de comisiones son algo como:

<!--
    JAJAJA LA BUROCRACIA NO HACE HTML SEMÁNTICO 
    http://bukkit.rob.mx/laugh-cry.gif
-->
<ul id="comisiones">
    <li class="comision">
        <a href="/comision/1/comision-de-datos-abiertos">
            <span class="nombre">Comisión de datos Abiertos</span>
        </a>

        <p>Oficina: <span class="oficina">42, piso 6, edificio 9</span></p>

        <ul class="telefonos">
            <li class="telefono">
                <a class="numero" href="tel://55123456,101">55123456</a>
                 extensión <span class="extension">101</span>
            </li>
        </ul>
    </li>
</ul>

Entonces sí, en este archivo podríamos hacer algo como:

#!/usr/bin/env ruby
# encoding: utf-8
module Parser

  module DistritoFederal

    class Comision
      attr_accessor :requests

      def initialize
        ## la lista de páginas para hacer requests de endpoints[:lista_comisiones]
        @requests = [{id: 1}, {id: 1}]
      end

      def parse(data, request)
        dom = Nokogiri::HTML(data)
        dom.at_css('.comision').each do |comision|

          link = comision.at_css('a')
          href = link.attr('href')

          fkey = Parser::DistritoFederal.endpoints[:base]+href
          nombre = link.at_css('.nombre').text.squish

          telefonos = comision.at_css('.telefono')

          comision = {
            camara: 'local',
            entidad: 9,
            meta: {
              # el url que puede visitar un humano para ver esta comisión
              fkey: fkey,
              lastCrawl: Time.now
            },
            nombre: nombre,
            oficina: comision.at_css('.oficina').text,
            telefonos: telefonos.map {|t|
              {
                numero: t.at_css('.numero').text,
                extension: t.at_css('.extension').text
              }
            }
          }

          #por cada comision parseada, `yield`eamos el valor de la misma
          yield comision

        end #/at_css
      end # parse

    end # Comision
  end # DistritoFederal
end # Parser

Lo más probable es que su HTML sea un potente purgante de los intestinos. Jajaja. HTML SEMÁNTICO

Actores

En el lingo de representantes.pati.to, cada representante es un "actor", como "actor político".

Lista de Actores (lista.rb)

Ya que tenemos las comisiones, ahora podemos ingestar nuestros diputados locales, empezando por la lista de los mismos:

<ul id="diputados">
    <li class="diputado">
        <a href="/ver_diputado/1">Doña Diputada Local (Dto XLII)</a>
    </li>
</ul>
# encoding: utf-8

module Parser

  module DistritoFederal

    class Lista

      def initialize
        @ids = []
        #mod es el módulo actual, en este caso Parser::DistritoFederal
        request(mod.endpoints[:lista]) do |data|
          doc = Nokogiri::HTML(data)
          doc.encoding = 'utf-8'
          @ids = doc.css('.diputado a').map { |link|
            #el nombre de la variable corresponde a aquel que usaremos en endpoints[:actor]
            {id_del_diputado: link.attr('href').gsub(/\D/, '')}
          }
        end
      end

      def to_a
        @ids
      end

      def count
        @ids.count
      end

    end # Lista
  end # DistritoFederal
end # Parser

Representantes

Con la lista, podemos recorrer la lista de diputados e irlos ingestando, a la vez que los relacionamos a sus comisiones. Asumamos que tienen un API y regresan JSON.

# encoding utf-8

module Parser
  
  module DistritoFederal

    # OPCIONAL
    def self.test
      # método si quieres hacer pruebas con ./crawl 09-Distrito-Federal:actores
    end

    class Actor

      def initialize
        # si debes hacer requests antes o inicializar madres, por ejemplo
        request('http://tucongreso.entidad.gob.mx/permiso-de-ddos?pretty=please') do |data|
          Log.info "Ya pedimos permiso de DDOSear su server :)"
          @tokenDDoS = data
        end

        @entidad = 9
      end

      # Extraer los datos de un actor
      # 
      # este método se ejecutará una vez por diputado
      # 
      # @param data [String] el body del request
      # @param request [Hash], los datos del request
      def parse(data, request)

        # porque luego las librerías de ruby deciden parsear los datos solitas
        info = JSON.parse(data, symbolize_keys: true) rescue data

        actor = {
          meta: {
            fkey: request[:url]+@tokenDDoS,
            lastCrawl: Time.now
          },
          camara: "local",
          distrito: "dl-#{@entidad}-#{info[:distrito_local]}",
          entidad: @entidad,
          tipo_distrito: "local",

          nombre: info[:nombre],
          partido: info[:partido], # pri,pan,prd,pvem,pt,mc,panal,nil
          # 1 para batos, 2 para morras, hay más géneros, pero no se como ponerlos :(
          genero: info[:genero]=='bato' ? 1 : 2,
          eleccion: info[:eleccion] # mayoría relativa ó representación proporcional
          suplente: info[:suplente][:nombre],
          curul: info[:calienta_el_asiento_numero], # o un string en caso de que tenga
          cabecera: info[:municipio], # la cabecera de este distrito

          puestos: info[:comisiones].map {|comision|
            {
              puesto: comision[:puesto],
              # Acá relacionamos un actor a su comisión
              comision: Comision.findOne("meta.fkey" => info[:url])
            }
          },

          correo: info[:correo],
          telefonos: [
            {
              #ambos como string, para no perder ceros al principio...
              numero: info[:telefonos][0][:numero].to_s,
              extension: info[:telefonos][0][:numero][:extension].to_s
            }
          ],
          links: [
            {servicio: "twitter", url: info[:twitter]}
          ],
          # corriendo bin/imagenes podemos convertir este URL a una imagen en la DB
          imagen: info[:imagen],

        }

        # Y regresamos el actor ya parseado
        return actor
      end

    end # Actor
  end # DistritoFederal
end # Parser