pasosdeJesus/sivel2_gen

View on GitHub
app/javascript/controllers/plotly_actos_controller.js

Summary

Maintainability
B
4 hrs
Test Coverage
import { Controller } from "@hotwired/stimulus"
// Calcula edad, edadactual y rango dada fecha de nacimiento o viceversa

import Msip__Motor from "../msip/motor"

var datosr = {} // Reorganiza datos de CSV en este diccionario cuyas
// llaves son fechas donde hay datos, el valor en cada fecha es
// un diccioneario indexado por las categorias de la variable y 
// el valor en cada una es la suma de cuenta por categoria
var datosrFechamin; // Fecha minima en CSV
var datosrFechamax; // Fecha máxima en CSV
var datosrEtiquetas = []  // Etiquetas o categorias posibles en CSV para la variable

// Las siguientes dependen de las selecciones del usuario en el filtro
var ejex = []  // Valores en eje x
var seriesEjey = {} // Series posibles en eje y indexadas por etiquetas

// DATOS PRESENTADOS COMO LOS REQUIERE PLOTLY
var trazosPresentados = [] // de plotly llenados por actualiarTrazosPresentados
var totalesEtiqueta = {} // Totales por etiqueta

// Conecta con data-controller="sivel2-gen--plotly-actos"
export default class extends Controller {

  static targets = [
    "presponsable",
    "fechaini",
    "fechafin"
  ]

  connect() {
    const self = this;

    console.log("Conexion de controlador de Plotly establecida");
    this.plotly_serietiempo_actos();
  }

  // Lee CSV, reorganiza los datos, prepara campos de filtro
  // Prepara series y presenta datos
  // Activa eventos ante cambios en campos de filtro que repintan lo necesario
  
  // Autores: Vladimir Támara y Luis Alejandro Cruz.
  // Con base en   https://plotly.com/javascript/line-charts/#styling-line-plot
  //
  // Desde javascript/packs/application.js se incluyó:
  // import hacer_serietiempo from '../serietiempo_plotly'
  // que operó dejando al final de este archivo
  // export default hacer_serietiempo
  //
  // <script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
  //
  
  // CSV:
  // fecha, presponsable, sexonac, ..., cuenta
  // 2020-03-01, PARAMILITARES, F, ...,5
  // Las gráficas analizaran solo una variable i.e columna e.g presponsable
  //   Serie de tiempo: Una linea por categoria de la variable analizada
  //      fecha vs. cuenta por categoria
  //    Barra: cuenta por categoria (en todo el tiempo)

  // Asigna opciones a un cuadro de selección múltiple inicialmente vacio
  // selector es selector del campo de selección múltiple
  // opciones es vector con las opciones a crear
  // seltodo indica si todas deben elegirse por omisión
  seleccionmAsignarOpciones(selector, opciones, seltodo = true) {
    for (var i = 0; i < opciones.length;  i++) {
      var opcionActual = document.createElement('option');
      if (seltodo) {
        opcionActual.selected = true;
      }
      opcionActual.text = opciones[i];
      selector.appendChild(opcionActual);
    }
    Msip__Motor.refrescarElementoTomSelect(selector)
  }


  // Recibe etiquetas por presentar y actualiza variable trazosPresentados
  // dejando justo las series de seriesEjey que corresponden a esas etiquetas
  actualizarTrazosPresentados(etiquetas) {
    var etiquetasP = []
    var i = 0
    trazosPresentados.forEach(function(t) {
      etiquetasP.push(t.name)
      i++;
    });
    var eo = etiquetas.sort()
    var epo = etiquetasP.sort()
    var c = 0
    var cp = 0
    var agregar = []
    var eliminar = []
    while (c < eo.length || cp < epo.length) {
      if (cp >= epo.length || eo[c] < epo[cp]) {
        agregar.push(eo[c])
        c++
      } else if (c >= eo.length || eo[c] > epo[cp]) {
        eliminar.push(epo[cp])
        cp++
      } else {
        c++
        cp++
      }
    }
    eliminar.forEach(function (e) {
      var indpe = trazosPresentados.find(t => t.name == e);
      trazosPresentados.splice(indpe, 1)
    })
    agregar.forEach(function (e) {
      trazosPresentados.push({
        x: ejex, 
        y: seriesEjey[e],
        stackgroup: 'uno',
        //type: 'scatter',
        //mode: 'lines',
        name: e,
        //line: {
        //color: colores[e],
        // width: 1
        //}
      })
    })
  
    var configuracion = {
      responsive: true, 
      displaylogo: false, 
      locale: 'es',
      showEditInChartStudio: true,
      plotlyServerURL: "https://chart-studio.plotly.com",
      height: 340, 
    }
  
    Plotly.newPlot('div_serietiempo', trazosPresentados, 
      {title: 'Serie de tiempo de actos', showlegend: false},
      configuracion);
  
    var valorBarras = []
    etiquetas.forEach(function (e) {
      valorBarras.push(totalesEtiqueta[e])
    })
    var barras = [{
      x: etiquetas,
      y: valorBarras,
      type: 'bar'
    }]
    Plotly.newPlot('div_barras', barras,
      {title: 'Actos por presunto responsable'},
      configuracion);
  }
 
  // Retorna opciones elegidas en un campo de selección múltiple
  seleccionmOpcionesElegidas(selector) {
    var res = [];
    var opciones = selector && selector.options;
    var op;
  
    for (var i = 0, t = opciones.length; i < t; i++) {
      op = opciones[i];
  
      if (op.selected) {
        res.push(op.value || op.text);
      }
    }
    return res;
  }


  actualizarGraficaEtiquetas(){
    var opelegidas = this.seleccionmOpcionesElegidas(this.presponsableTarget)
    this.actualizarTrazosPresentados(opelegidas);
  }
 
  actualizarGraficaFechas() {
    var opelegidas = this.seleccionmOpcionesElegidas(this.presponsableTarget)
    var fechaini = this.fechainiTarget.value
    var fechafin = this.fechafinTarget.value
    this.recalcularSeriesPresentadas(fechaini, fechafin, opelegidas);
  }

  procesar_datos(filas, variable) {
    var dicEtiquetas = {}
    filas.forEach(function(r) {
      dicEtiquetas[r[variable]] = 0
      if (typeof datosr[r.fecha] == 'undefined') {
        datosr[r.fecha] = {}
        if (typeof datosrFechamin == 'undefined' || r.fecha < datosrFechamin) {
          datosrFechamin = r.fecha
        }
        if (typeof datosrFechamax == 'undefined' || r.fecha > datosrFechamax) {
          datosrFechamax = r.fecha
        }
      }
      if (typeof datosr[r.fecha][r[variable]] == 'undefined') {
        datosr[r.fecha][r[variable]] = +r.cuenta
      } else {
        datosr[r.fecha][r[variable]] += +r.cuenta
      }
    })

    datosrEtiquetas = Object.keys(dicEtiquetas)
    this.seleccionmAsignarOpciones(this.presponsableTarget, datosrEtiquetas, true);

    function saneaFechaEstandar(fecha) {
      if (/^[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?$/.test(fecha)) {
        return fecha
      } else {
        return "";
      }
    }
    var fechaminLoc = saneaFechaEstandar(datosrFechamin)
    this.fechainiTarget.value = saneaFechaEstandar(datosrFechamin)
    this.fechafinTarget.value = saneaFechaEstandar(datosrFechamax)
    /*var colores = {
       'F': 'rgb(219, 64, 82)',
      'M': 'rgb(64, 219, 82)',
      'S': 'rgb(64, 82, 219)'
    } */

    this.actualizarGraficaFechas()
  }

  // Recibe fecha inicia, fecha final y etiquetas a presentar
  // actualiza datos presentados para que corresponda a ese rango
  // y etiquetas
  recalcularSeriesPresentadas(fechaini, fechafin, etiquetas) {
    totalesEtiqueta = {}
    ejex= []
    seriesEjey = {}
    trazosPresentados = [] 
    Object.keys(datosr).sort().forEach(function(fecha) {
      if (fecha >= fechaini & fecha <= fechafin) {
        ejex.push(fecha)
        datosrEtiquetas.forEach(function(e) {
          if (typeof datosr[fecha][e] == 'undefined') {
            datosr[fecha][e] = 0
          }
          if (typeof seriesEjey[e] == 'undefined') {
            seriesEjey[e] = []
          }
          seriesEjey[e].push(datosr[fecha][e])
          if (typeof totalesEtiqueta[e] == 'undefined') {
            totalesEtiqueta[e] = datosr[fecha][e]
          } else {
            totalesEtiqueta[e] += datosr[fecha][e]
          }
        })
      }
    })
    this.actualizarTrazosPresentados(etiquetas) 
  }




  plotly_serietiempo_actos() {
    const self = this;
    const ruta_relativa = window.location.pathname.split("/")[1]
    Plotly.d3.csv("/" + ruta_relativa + "/" + ruta_relativa + "/csv/actos_individuales.csv", function(err, datos) { 
      if (err) {
        console.error("Error al cargar el archivo CSV:", err);
      } else {
        self.procesar_datos(datos, 'presponsable') 
      }
    });
  };

}