<repo_name>brenes/lodel20n<file_sep>/config/initializers/decorators.rb
ActiveRecord::Base.class_eval do
  def decorator(format)
    @__decorators ||= {}
    @__decorators[format] ||= eval("Decorators::#{self.class.name}::#{format}.new(self)")
  end
end<file_sep>/db/migrate/20111120104353_create_resultados.rb
class CreateResultados < ActiveRecord::Migration
  def change
    create_table :resultados do |t|
      t.integer :escrutinio_id
      t.integer :partido_id

      t.timestamps
    end
  end
end
<file_sep>/db/migrate/20111120122701_add_fields_to_resultado.rb
class AddFieldsToResultado < ActiveRecord::Migration
  def change
    add_column :resultados, :escanos, :integer
    add_column :resultados, :total_votos, :integer
    add_column :resultados, :pct_votos, :integer
  end
end
<file_sep>/app/decorators/decorator.rb
module Decorators
  class Base
    def initialize(decorated)
      @decorated = decorated
    end

    def method_missing(s, *a)
      @decorated.send(s, *a)
    end
  end
end<file_sep>/app/models/escrutinio.rb
class Escrutinio < ActiveRecord::Base
  belongs_to :territorio
  has_many :resultados

  scope :final, where(:pct_escrutado => 100)

    # Los escrutinios tienen este formato
    # <escrutinio_sitio>
    #   <porciento_escrutado>100</porciento_escrutado>
    #   <nombre_sitio>Barbastro</nombre_sitio>
    #   <ts>0</ts>
    #   <tipo_sitio>5</tipo_sitio>
    #   <votos>
    #     <contabilizados><cantidad>9433</cantidad><porcentaje>77.5</porcentaje></contabilizados>
    #     <abstenciones><cantidad>2738</cantidad><porcentaje>22.5</porcentaje></abstenciones>
    #     <nulos><cantidad>70</cantidad><porcentaje>0.74</porcentaje></nulos>
    #     <blancos><cantidad>113</cantidad><porcentaje>1.21</porcentaje></blancos>
    #   </votos>
    #   <resultados>
    #     <numero_partidos>20</numero_partidos>
    #     <partido>
    #       <id_partido>226</id_partido>
    #       <nombre>PSOE</nombre>
    #       <electos>2</electos>
    #       <votos_numero>4398</votos_numero>
    #       <votos_porciento>46.97</votos_porciento>
    #     </partido>
    #   </resultados>
    # </escrutinio_sitio>
  def self.create_from_api territorio, escrutinio_xml
    
    hora = escrutinio_xml.xpath("/escrutinio_sitio/ts").first.content
    hora = (hora == 0) ? nil : Time.at(hora.to_i)

    escrutinio = Escrutinio.find_by_territorio_id_and_hora(territorio.id, hora)
    # si tenemos escrutinio de esta hora no lo repetimos
    return escrutinio unless escrutinio.nil?

    escrutinio = Escrutinio.create! :territorio_id => territorio.id, :hora => hora, 
      :pct_escrutado => escrutinio_xml.xpath("/escrutinio_sitio/porciento_escrutado").first.content,
      :total_contabilizados => escrutinio_xml.xpath("/escrutinio_sitio/votos/contabilizados/cantidad").first.content,
      :pct_contabilizados => escrutinio_xml.xpath("/escrutinio_sitio/votos/contabilizados/porcentaje").first.content,
      :total_abstenciones => escrutinio_xml.xpath("/escrutinio_sitio/votos/abstenciones/cantidad").first.content,
      :pct_abstenciones => escrutinio_xml.xpath("/escrutinio_sitio/votos/abstenciones/porcentaje").first.content,
      :total_nulos => escrutinio_xml.xpath("/escrutinio_sitio/votos/nulos/cantidad").first.content,
      :pct_nulos => escrutinio_xml.xpath("/escrutinio_sitio/votos/nulos/porcentaje").first.content,
      :total_blanco => escrutinio_xml.xpath("/escrutinio_sitio/votos/blancos/cantidad").first.content,
      :pct_blanco => escrutinio_xml.xpath("/escrutinio_sitio/votos/blancos/porcentaje").first.content

    escrutinio_xml.xpath("/escrutinio_sitio/resultados/partido").each do |resultado_xml|
      escrutinio.resultados << Resultado.create_from_api(resultado_xml)
    end
    escrutinio
  end

  def final?
    pct_escrutado == 100
  end

  def total_votos
    total_contabilizados + total_abstenciones + total_nulos
  end
end
<file_sep>/app/decorators/territorio/html.rb
module Decorators::Territorio
  class Html < Decorators::Base

    def sidebar

    end
    
  end
end<file_sep>/app/controllers/territorios_controller.rb
class TerritoriosController < ApplicationController

  def index
    @territorio = Territorio.pais.first
    @escrutinio = @territorio.ultimo_escrutinio
    @escrutinio_html = Decorators::Escrutinio::Html.new(@escrutinio)
    @escrutinio_seo = Decorators::Escrutinio::Seo.new(@escrutinio_html)
    render :action => :show_pais
  end
    
  def show
    @territorio = Territorio.find(params[:id])
    @escrutinio = @territorio.ultimo_escrutinio
    @escrutinio_html = Decorators::Escrutinio::Html.new(@escrutinio)
    @escrutinio_seo = Decorators::Escrutinio::Seo.new(@escrutinio_html)
    render :action => :"show_#{@territorio.descripcion_tipo}"
  end

end
<file_sep>/db/migrate/20111120202536_add_pct_escrutado_to_escrutinio.rb
class AddPctEscrutadoToEscrutinio < ActiveRecord::Migration
  def change
    add_column :escrutinios, :pct_escrutado, :float
  end
end
<file_sep>/app/decorators/escrutinio/html.rb
module Decorators::Escrutinio
  class Html < Decorators::Base

    def titulo
      @decorated.final? ? "Escrutinio final" : "Escrutinio parcial <>(#{@decorated.pct_escrutado}%)</i>"
    end

    def fecha_actualizacion
      @decorated.final? ?
        "<p>Datos actualizados por última vez a las #{hora}</p>" :
        "<p>Datos definitivos</p>"  
    end

  end
end<file_sep>/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery

  Dir["app/decorators/*.rb"].each do |path| 
    require_dependency path 
  end 

  Dir["app/decorators/**/*.rb"].each do |path| 
    require_dependency path 
  end 
  I18n.locale = :es
end
<file_sep>/app/decorators/escrutinio/seo.rb
module Decorators::Escrutinio
    class Seo < Decorators::Base

      def header_seccion
        "<h2>#{@decorated.titulo}</h2>"
      end

    end
end<file_sep>/lib/tasks/api.rake
namespace :elpais do

  desc "Tarea para descargar toda la estructura de territorios"
  task :descarga_territorios => :environment do

    VCR.use_cassette("elpais_estructura_#{Settings["year"]}", :record => :new_episodes) do
      
      Territorio.delete_all

      # Creamos el territorio del pais
      pais = Territorio.find_by_nombre("España")
      pais ||= Territorio.create! :nombre => "España", :tipo_api => Territorio.TIPOS_API[:pais]

      # Visitamos la página de El Pais y recorremos l a lista de comunidades
      Nokogiri::HTML(Net::HTTP.get(URI.parse(pais.pais_url))).xpath("//div[@id='otrasCCAAs']/ul/li/a/@href").each do |id_comunidad|


        str_id_comunidad =  id_comunidad.value.gsub("/", "")

        uri_comunidad = URI.parse("http://rsl00.epimg.net/elecciones/#{Settings["year"]}/generales/congreso/#{str_id_comunidad}/index.xml2")

        resultado_comunidad = Nokogiri::XML(Net::HTTP.get uri_comunidad)

 
        comunidad = Territorio.create_from_api str_id_comunidad, pais, resultado_comunidad

        puts "#{str_id_comunidad} " + comunidad.nombre

        contenido = Nokogiri::HTML(Net::HTTP.get(URI.parse(comunidad.pais_url)))

        contenido.xpath("//div[@id='otrasCircunscripciones']/ul/li/a/@href").each do |id_provincia|
          
          str_id_provincia =  id_provincia.value.gsub("/", "").gsub("\.html", "")

          uri_provincia = URI.parse("http://rsl00.epimg.net/elecciones/#{Settings["year"]}/generales/congreso/#{str_id_comunidad}/#{str_id_provincia}.xml2")

          resultado_provincia = Nokogiri::XML(Net::HTTP.get uri_provincia)        
        
          provincia = Territorio.create_from_api str_id_provincia, comunidad, resultado_provincia

          puts "---  #{str_id_provincia} " + provincia.nombre

          Nokogiri::HTML(Net::HTTP.get(URI.parse(provincia.pais_url))).xpath("//div[@id='listadoMunicipios']/ul/li/a/@href").each do |id_municipio|            

            str_id_municipio =  id_municipio.value.gsub(/^.*\//, "").gsub("\.html", "")
            
            uri_municipio = URI.parse("http://rsl00.epimg.net/elecciones/#{Settings["year"]}/generales/congreso/#{str_id_comunidad}/#{str_id_provincia}/#{str_id_municipio}.xml2")

            resultado_municipio = Nokogiri::XML(Net::HTTP.get uri_municipio)
            
            unless resultado_municipio.xpath("/escrutinio_sitio/nombre_sitio").first.nil?

              municipio = Territorio.create_from_api str_id_municipio, provincia, resultado_municipio

              puts "------  #{str_id_municipio} " + municipio.nombre
            end
          end

        end
      end
    end


  end

  desc "Tarea para descargar el escrutinio de los territorios en su estado actual"
  task :escrutinio => :environment do

    # Tarda tanto que podemos dejarlo en bucle sin problema
    while true
      Territorio.interesantes.each do |t|
        t.consultar_escrutinio
      end
    end

  end

end<file_sep>/app/models/resultado.rb
class Resultado < ActiveRecord::Base
  belongs_to :escrutinio
  belongs_to :partido


  # Los resultados tienen este formato
  #       <id_partido>226</id_partido>
  #       <nombre>PSOE</nombre>
  #       <electos>2</electos>
  #       <votos_numero>4398</votos_numero>
  #       <votos_porciento>46.97</votos_porciento>
  def self.create_from_api resultado_xml

    id_partido = resultado_xml.xpath("id_partido").first.content

    partido = Partido.find_or_create_by_id_api(id_partido, :nombre => resultado_xml.xpath("nombre").first.content)

    Resultado.create! :partido_id => partido.id, 
      :escanos => resultado_xml.xpath("electos").first.nil? ? nil : resultado_xml.xpath("electos").first.content.to_i,
      :total_votos => resultado_xml.xpath("votos_numero").first.content.to_i,
      :pct_votos => resultado_xml.xpath("votos_porciento").first.content.to_f      

  end

end
<file_sep>/app/models/partido.rb
class Partido < ActiveRecord::Base
  has_many :resultados
end
<file_sep>/app/decorators/territorio/js.rb
module Decorators::Territorio
  class Js < Decorators::Base

    def distribucion_partidos
      escrutinio = @decorated.ultimo_escrutinio

      "var data = #{escrutinio.resultados.map{|r| {:id => r.partido.id, :name => r.pct_votos > 0.5 ? r.partido.nombre : "", :pct => 1 + r.pct_votos*(100-escrutinio.resultados.count)/100}}.to_json};

        var w = 600,                        //width
        h = 450,                            //height
        r = 200,                            //radius
        center = 225;
        color = d3.scale.category20c();     //builtin range of colors

        var vis = d3.select(\"#graphic\")
          .append(\"svg:svg\")              //create the SVG element inside the <body>
          .data([data])                   //associate our data with the document
            .attr(\"width\", w)           //set the width and height of our visualization (these will be attributes of the <svg> tag
            .attr(\"height\", h)
          .append(\"svg:g\")                //make a group to hold our pie chart
            .attr(\"transform\", \"translate(\" + center + \",\" + center + \")\")    //move the center of the pie chart from 0, 0 to radius, radius

        var arc = d3.svg.arc()              //this will create <path> elements for us using arc data
          .outerRadius(r);

    var pie = d3.layout.pie()           //this will create arc data for us given a list of values
        .value(function(d) { return d.pct; });    //we must tell it out to access the value of each element in our data array

    var arcs = vis.selectAll(\"g.slice\")     //this selects all <g> elements with class slice (there aren't any yet)
        .data(pie)                          //associate the generated pie data (an array of arcs, each having startAngle, endAngle and value properties) 
        .enter()                            //this will create <g> elements for every \"extra\" data element that should be associated with a selection. The result is creating a <g> for every object in the data array
            .append(\"svg:g\")                //create a group to hold each slice (we will have a <path> and a <text> element associated with each slice)
                .attr(\"class\", \"slice\");    //allow us to style things in the slices (like text)

        arcs.append(\"svg:path\")
                .attr('id', function(d, i) { 
                  return 'party-'+d.data.id;
                   } )
                .attr(\"fill\", function(d, i) { return color(i); } ) //set the color for each slice to be chosen from the color function defined above
                .attr(\"d\", arc)                                    //this creates the actual SVG path using the associated data (pie) with the arc drawing function
                .on(\"mouseover\", function() {
                  $('.party').removeClass('active');
                  $('.'+this.id).addClass('active');

                  d3.select(this).transition()
                  .attr(\"transform\", function(d) {
                   angle = d.startAngle + (d.endAngle - d.startAngle)/2
                   distance = 20
                   return \"translate(\" + distance*Math.sin(angle) + \", \" + -1*distance*Math.cos(angle) + \")\";    
                  })
                  .delay(0)
                  .duration(1000);

                })                            
                .on(\"mouseout\", function() {
                  d3.select(this).transition()
                  .attr(\"transform\", function(d) {
                   angle = d.startAngle + (d.endAngle - d.startAngle)/2
                   distance = 20
                   return \"translate(0,0)\";    
                  })
                  .delay(1000)
                  .duration(1000)
                  });;
    

        arcs.append(\"svg:text\")                                     //add a label to each slice
                .attr(\"transform\", function(d) {                    //set the label's origin to the center of the arc
                //we have to make sure to set these before calling arc.centroid
                d.innerRadius = 0;
                d.outerRadius = r;
                return \"translate(\" + arc.centroid(d) + \")\";        //this gives us a pair of coordinates like [50, 50]
            })
            .attr(\"text-anchor\", \"middle\")                          //center the text on it's origin
            .text(function(d, i) { return data[i].name; });        //get the label from our original data array
"
    end

    def historico_partidos 
      @decorated.escrutinios.map do |escrutinio|
        "var data = #{escrutinio.resultados.map{|r| {:name => r.partido.nombre, :pct => r.pct_votos}}.to_json};

        var c = d3.scale.linear().domain([0,100]).range([\"rgb(50%, 0, 0)\", \"rgb(100%, 0, 0)\"]).interpolate(d3.interpolateRgb)
        var pct_acumulado = 100
        var vis = d3.select(\"#graphic\")
          .append(\"div\")
          .attr(\"width\", \"100%\")

        vis.append(\"span\")
            .text(\"#{escrutinio.hora}: #{escrutinio.pct_escrutado}%\")

        vis.selectAll(\"div\")
            .data(data)
            .enter().append(\"div\")
            .style(\"width\", function(d) { return d.pct+'%' })
            .style(\"clear\", function(d, i) { return (i == 0) ? \"left\" : \"none\" })
            .style(\"background-color\", function(d) { pct_acumulado -= d.pct; return  c(pct_acumulado + d.pct) })
            .text(function(d) { return d.name + '(' + d.pct+'%)'});"
      end.join("\n")
    end
    
  end
end<file_sep>/app/models/territorio.rb
class Territorio < ActiveRecord::Base
  has_many :escrutinios
  belongs_to :padre, :class_name => "Territorio", :foreign_key => "territorio_padre_id"
  has_many :hijos, :class_name => "Territorio", :foreign_key => "territorio_padre_id"

  @@TIPOS_API = {:pais => 1, :comunidad => 2, :provincia => 3, :municipio => 5}
  cattr_reader :TIPOS_API

  @@TIPOS_API.each do |name, id|
    scope name.to_sym, where(:tipo_api => id)
  end

  scope :interesantes,  where("tipo_api = #{@@TIPOS_API[:pais]} OR tipo_api = #{@@TIPOS_API[:comunidad]} OR tipo_api = #{@@TIPOS_API[:provincia]}")
  def self.create_from_api id, padre, xml
    num_a_elegir =  xml.xpath("/escrutinio_sitio/num_a_elegir").first
    tipo = xml.xpath("/escrutinio_sitio/tipo_sitio").first.content
    territorio = Territorio.find_by_id_api_and_tipo_api_and_territorio_padre_id(id, tipo, padre.id) || Territorio.create!(:nombre => xml.xpath("/escrutinio_sitio/nombre_sitio").first.content, 
      :tipo_api => tipo, 
      :id_api => id, 
      :escanos => (num_a_elegir.nil? ? nil  : num_a_elegir.content), 
      :padre => padre)
  end

  def descripcion_tipo
    @@TIPOS_API.invert[tipo_api]
  end

  def api_url 
    send :"api_url_#{descripcion_tipo}"
  end

  def pais_url 
    send :"pais_url_#{descripcion_tipo}"
  end

  def consultar_escrutinio  
    escrutinio_xml = Nokogiri::XML(Net::HTTP.get(URI.parse(api_url)))
    escrutinios << Escrutinio.create_from_api(self, escrutinio_xml)
  end

  def ultimo_escrutinio
    escrutinios.final.first || escrutinios.first(:order => "hora DESC")
  end

  protected

  def api_url_pais
    "http://rsl00.epimg.net/elecciones/#{Settings["year"]}/generales/congreso/index.xml2"
  end

  def pais_url_pais
    "http://resultados.elpais.com/elecciones/#{Settings["year"]}/generales/congreso/"
  end

  def api_url_comunidad
    "http://rsl00.epimg.net/elecciones/#{Settings["year"]}/generales/congreso/#{id_api}/index.xml2"
  end

  def pais_url_comunidad
    "http://resultados.elpais.com/elecciones/#{Settings["year"]}/generales/congreso/#{id_api}/"
  end

  def api_url_provincia
    "http://rsl00.epimg.net/elecciones/#{Settings["year"]}/generales/congreso/#{padre.id_api}/#{id_api}.xml2"
  end

  def pais_url_provincia
    "http://resultados.elpais.com/elecciones/#{Settings["year"]}/generales/congreso/#{padre.id_api}/#{id_api}.html"
  end

  def api_url_municipio
    "http://rsl00.epimg.net/elecciones/#{Settings["year"]}/generales/congreso/#{padre.padre.id_api}/#{padre.id_api}/#{id_api}.xml2"
  end

  def pais_url_municipio
    "http://resultados.elpais.com/elecciones/#{Settings["year"]}/generales/congreso/#{padre.padre.id_api}/#{padre.id_api}/#{id_api}.html"
  end
end
<file_sep>/db/migrate/20111120122429_add_fields_to_territorio.rb
class AddFieldsToTerritorio < ActiveRecord::Migration
  def change
    add_column :territorios, :tipo_api, :integer
    add_column :territorios, :id_api, :string
    add_column :territorios, :escanos, :integer
  end
end
<file_sep>/db/migrate/20111120122618_add_fields_to_escrutinio.rb
class AddFieldsToEscrutinio < ActiveRecord::Migration
  def change
    add_column :escrutinios, :total_contabilizados, :integer
    add_column :escrutinios, :total_abstenciones, :integer
    add_column :escrutinios, :total_nulos, :integer
    add_column :escrutinios, :total_blanco, :integer
    add_column :escrutinios, :pct_contabilizados, :double
    add_column :escrutinios, :pct_abstenciones, :double
    add_column :escrutinios, :pct_nulos, :double
    add_column :escrutinios, :pct_blanco, :double
  end
end
<file_sep>/config/initializers/vcr.rb
VCR.config do |c|
  c.allow_http_connections_when_no_cassette = true
  c.cassette_library_dir = 'db/vcr'
  c.stub_with :webmock # or :fakeweb
end<file_sep>/db/migrate/20111120122757_add_fields_to_partido.rb
class AddFieldsToPartido < ActiveRecord::Migration
  def change
    add_column :partidos, :id_api, :integer
  end
end
