From 06bda41744182c248feaff794e928fd098f51781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Camilo=20Gonz=C3=A1lez?= Date: Wed, 1 Nov 2023 12:23:49 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=A5=9A=20Procesar=201.7=20en=20doble=20co?= =?UTF-8?q?lumnas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aplicaciones/procesador/fuente/aplicacion.ts | 30 +-- .../procesador/fuente/indicadores/01-07.ts | 241 +++++++++++++++--- .../procesador/fuente/limpieza/lugar.ts | 13 + 3 files changed, 239 insertions(+), 45 deletions(-) diff --git a/aplicaciones/procesador/fuente/aplicacion.ts b/aplicaciones/procesador/fuente/aplicacion.ts index 0335ec8..da2cc57 100644 --- a/aplicaciones/procesador/fuente/aplicacion.ts +++ b/aplicaciones/procesador/fuente/aplicacion.ts @@ -1,26 +1,24 @@ import VariableSingular from './modulos/VariableSingular'; +import Indicador17 from '@/indicadores/01-07'; async function inicio() { - const ya11 = new VariableSingular('tacued', true, 'porcentaje'); - await ya11.procesar('1.1: salud - acueducto', 'YA1_1.1', 'Sheet1', 'ya1-1'); + // const ya11 = new VariableSingular('tacued', true, 'porcentaje'); + // await ya11.procesar('1.1: salud - acueducto', 'YA1_1.1', 'Sheet1', 'ya1-1'); - const ya12 = new VariableSingular('talcan', true, 'porcentaje'); - await ya12.procesar('1.2: salud - alcantarillado', 'YA1_1.2', 'Sheet1', 'ya1-2'); + // const ya12 = new VariableSingular('talcan', true, 'porcentaje'); + // await ya12.procesar('1.2: salud - alcantarillado', 'YA1_1.2', 'Sheet1', 'ya1-2'); - const ya13 = new VariableSingular('mortalidad_menores_5', false, 'porcentaje'); - await ya13.procesar('1.3: mortalidad menores', 'YA1_1.3', 'Sheet1', 'ya1-3'); + // const ya13 = new VariableSingular('tasa_mortalidad_infantil', false, 'porcentaje'); + // await ya13.procesar('1.3: mortalidad menores', 'YA1_1.3', 'Sheet1', 'ya1-3'); - const ya15 = new VariableSingular('controles_prenatales', true, 'porcentaje'); - await ya15.procesar('1.5: controles prenatales', 'YA1_1.5', 'Sheet1', 'ya1-5'); + // const ya15 = new VariableSingular('controles_prenatales', true, 'porcentaje'); + // await ya15.procesar('1.5: controles prenatales', 'YA1_1.5', 'Sheet1', 'ya1-5'); - const ya18 = new VariableSingular('bajo_peso', false, 'porcentaje'); - await ya18.procesar('1.8: bajo peso', 'YA1_1.8', 'Sheet1', 'ya1-8'); - // await ya1_1(); - // await ya1_2(); - // await ya1_4(); - // await ya1_6(); - // await ya2_5(); - // await ya3_1(); + const ya17 = new Indicador17(false, 'porcentaje'); + await ya17.procesar('1.7: desnutricion', 'YA1_1.7', 'Sheet1', 'ya1-7'); + + // const ya18 = new VariableSingular('bajo_peso', false, 'porcentaje'); + // await ya18.procesar('1.8: bajo peso', 'YA1_1.8', 'Sheet1', 'ya1-8'); } inicio(); diff --git a/aplicaciones/procesador/fuente/indicadores/01-07.ts b/aplicaciones/procesador/fuente/indicadores/01-07.ts index 31c4935..b07202c 100644 --- a/aplicaciones/procesador/fuente/indicadores/01-07.ts +++ b/aplicaciones/procesador/fuente/indicadores/01-07.ts @@ -1,43 +1,226 @@ -import { limpiarDepartamento, limpiarMunicipio } from '@/limpieza/lugar'; -import type { Departamento, Errata, Municipio } from '@/tipos'; -import { guardarJSON } from '@/utilidades/ayudas'; +import { extraerPartesLugar, limpiarDepartamento } from '@/limpieza/lugar'; +import type { + Departamento, + Errata, + EstructurasMatematicas, + Municipio, + RespuestaNacional, + RespuestaPorcentaje, +} from '@/tipos'; +import { guardarJSON, redondearDecimal } from '@/utilidades/ayudas'; import maquinaXlsx from '@/utilidades/maquinaXlsx'; -/** - * Los datos no tienen denominador - */ -export type VariablesYa1_7 = { - codmpio: number; - ano: number; - naci_bajopeso: number; + +type Variables = { + codmpio_num: string; + num_2015: number; + num_2017: number; + num_2018: number; + num_2019: number; + codmpio_den: string; + den_2015: number; + den_2017: number; + den_2018: number; + den_2019: number; + den_2020: number; }; -type Respuesta = { - [año: string]: [lugar: string, porcentaje: number][]; +type DatosLugar = { + codigoMun: string; + codigoDep: string; + nombreMun: string; + codCompletoMun: string; + datos: { [año: string]: number }; }; -const errata: { fila: number; error: string }[] = []; -export default async () => { - const datosMunicipios: Respuesta = {}; - await maquinaXlsx('1.7: salud - bajo peso al nacer', 'YA1_1.7', 'Sheet1', procesador); - guardarJSON(datosMunicipios, 'ya1-7-mun'); - guardarJSON(errata, 'Errata YA1.7_dep'); +const años = ['2015', '2017', '2018', '2019', '2020']; +export default class { + datosNacionales: RespuestaNacional; + datosDepartamentos: RespuestaPorcentaje; + datosMunicipios: RespuestaPorcentaje; + errata: { fila: number; error: string }[]; + datosNum: DatosLugar[]; + datosDen: DatosLugar[]; + + constructor(ascendente: boolean, estructura: EstructurasMatematicas) { + this.datosNacionales = { ascendente, estructura, datos: {} }; + this.datosNum = []; + this.datosDen = []; + this.errata = []; + this.datosMunicipios = {}; + this.datosDepartamentos = {}; + + años.forEach((año) => { + this.datosNacionales.datos[año] = 0; + this.datosDepartamentos[año] = []; + this.datosMunicipios[año] = []; + }); + } + + async procesar(nombreReferencia: string, nombreArchivo: string, hoja: string, nombreParaArchivo: string) { + await maquinaXlsx(nombreReferencia, nombreArchivo, hoja, this.preprocesar); + guardarJSON(this.datosNum, 'prueba-num'); + guardarJSON(this.datosDen, 'prueba-den'); + this.procesarMunicipios(); + this.procesarDepartamentos(); + this.procesarNacional(); + guardarJSON(this.datosMunicipios, `${nombreParaArchivo}-mun`); + guardarJSON(this.datosDepartamentos, `${nombreParaArchivo}-dep`); + guardarJSON(this.datosNacionales, `${nombreParaArchivo}-nal`); + guardarJSON(this.errata, `Errata ${nombreParaArchivo}`); + } + + procesarFila(fila: Variables, numeroFila: number, datosLugar: DatosLugar, tipo: 'num' | 'den') { + Object.keys(fila).forEach((variable) => { + const partes = variable.split('_'); + + if (partes.length && partes.length === 2) { + if (partes[0] !== 'codmpio') { + const año = partes[1]; - function procesador(fila: VariablesYa1_7, numeroFila: number) { - const datos = limpiarMunicipio(fila.codmpio); + if (partes[0] === tipo) { + const valor = fila[variable as keyof Variables] as number; - if (datos.hasOwnProperty('error')) { - errata.push({ fila: numeroFila, error: (datos as Errata).mensaje }); - return; + if (!datosLugar.datos[año]) { + if (!isNaN(valor)) { + datosLugar.datos[año] = valor; + } else { + this.errata.push({ fila: numeroFila, error: `valor no es numerico en columna ${variable}: ${valor}` }); + } + } else { + this.errata.push({ + fila: numeroFila, + error: `ya existe valor de columna ${tipo}: ${JSON.stringify(fila)}`, + }); + } + } + } + } else { + this.errata.push({ + fila: numeroFila, + error: `variable mal escrita, debe estar sin espacios y separar partes con _, se ve así: ${variable}`, + }); + } + }); + } + + procesarBloque(fila: Variables, numeroFila: number, tipo: 'num' | 'den') { + const nombreLugar = fila[`codmpio_${tipo}`]; + + if (nombreLugar && nombreLugar !== 'Total general') { + const datosLugar = extraerPartesLugar(nombreLugar); + + if (datosLugar.hasOwnProperty('error')) { + this.errata.push({ fila: numeroFila, error: (datosLugar as Errata).mensaje }); + } else { + const [codigoMun, nombreMun, codigoDep, codCompletoMun] = datosLugar as Municipio; + const datos = tipo === 'num' ? this.datosNum : this.datosDen; + const lugar = datos.find((d) => d.codigoMun === codCompletoMun); + + if (!lugar) { + const lugarNuevo = { codigoMun, codigoDep, nombreMun, codCompletoMun, datos: {} }; + this.procesarFila(fila, numeroFila, lugarNuevo, tipo); + datos.push(lugarNuevo); + } else { + this.procesarFila(fila, numeroFila, lugar, tipo); + } + } + } else { + // } + } - const año = fila.ano; + preprocesar = (fila: Variables, numeroFila: number) => { + if (fila.codmpio_num) { + this.procesarBloque(fila, numeroFila, 'num'); + } - if (!datosMunicipios[año]) { - datosMunicipios[año] = []; + if (fila.codmpio_den) { + this.procesarBloque(fila, numeroFila, 'den'); } + }; - if (fila.naci_bajopeso) { - datosMunicipios[año].push([(datos as Municipio)[0], fila.naci_bajopeso]); + procesarMunicipios = () => { + this.datosNum.forEach((lugar) => { + const den = this.datosDen.find((lugarDen) => lugarDen.codCompletoMun === lugar.codCompletoMun); + + if (den) { + Object.keys(lugar.datos).forEach((año) => { + if (!this.datosMunicipios[año]) { + this.datosMunicipios[año] = []; + } + const tieneDen = den.datos.hasOwnProperty(año); + + if (tieneDen) { + const valorNum = lugar.datos[año]; + const valorDen = den.datos[año]; + + const { codCompletoMun } = lugar; + + this.datosMunicipios[año].push([codCompletoMun, redondearDecimal(valorNum / valorDen, 1, 2)]); + } else { + console.log(`no hay denominador para ${lugar.nombreMun} en el año ${año}`); + } + + this.datosMunicipios[año].sort(([a], [b]) => { + if (a < b) return -1; + else if (a > b) return 1; + return 0; + }); + }); + } else { + console.log(`no hay ningun registro en denonimador para ${lugar.nombreMun}`); + } + }); + }; + + procesarDepartamentos() { + for (const año in this.datosMunicipios) { + const datosAño = this.datosMunicipios[año]; + const deps: { [codDep: string]: number[] } = {}; + datosAño.forEach((municipio) => { + const codDepartamento = municipio[0].slice(0, 2); + if (!deps[codDepartamento]) { + deps[codDepartamento] = []; + } + deps[codDepartamento].push(municipio[1]); + }); + + for (const codDep in deps) { + const yaExiste = + this.datosDepartamentos[año] && this.datosDepartamentos[año].find(([codigo]) => codigo === codDep); + + if (yaExiste) { + // Ya existen datos departamentales, no hacer nada. + } else { + // No hay datos de este departamento en la tabla original, procesarlos con los datos que tenemos de los municipios. + const departamento = limpiarDepartamento(+codDep); + if (departamento.hasOwnProperty('error')) { + this.errata.push({ fila: Infinity, error: (departamento as Errata).mensaje }); + } else { + if (!this.datosDepartamentos[año]) { + this.datosDepartamentos[año] = []; + } + const suma = deps[codDep].reduce((valorAnterior, valorActual) => valorAnterior + valorActual, 0); + const porcentaje = redondearDecimal(suma / deps[codDep].length, 1, 2); + + this.datosDepartamentos[año].push([(departamento as Departamento)[0], porcentaje]); + } + } + } } } -}; + + procesarNacional() { + for (const año in this.datosDepartamentos) { + if (this.datosNacionales.datos[año]) { + // Ya existen datos a nivel nacional para este año + } else { + // No hay datos nacionales, sacarlos a partir de los datos departamentales. + const datosAño = this.datosDepartamentos[año]; + const suma = datosAño.reduce((depAnterior, valorActual) => ['', depAnterior[1] + valorActual[1]], ['', 0]); + + this.datosNacionales.datos[año] = redondearDecimal(suma[1] / datosAño.length, 1, 2); + } + } + } +} diff --git a/aplicaciones/procesador/fuente/limpieza/lugar.ts b/aplicaciones/procesador/fuente/limpieza/lugar.ts index a0600d7..c83d178 100644 --- a/aplicaciones/procesador/fuente/limpieza/lugar.ts +++ b/aplicaciones/procesador/fuente/limpieza/lugar.ts @@ -18,3 +18,16 @@ export const limpiarMunicipio = (codigo: number): Municipio | Errata => { if (mun) return mun; return { error: true, mensaje: `No existe municipio con código ${codigo}` }; }; + +export const extraerPartesLugar = (valor: string) => { + const partes = valor.split('-').map((parte) => parte.trim()); + const mun = municipios.datos.find((municipio) => municipio[3] === partes[0]); + + if (mun) { + return mun; + } else if (partes.length === 3 && partes[2] === 'No definido') { + return { error: true, mensaje: `Lugar indefinido` }; + } else { + return { error: true, mensaje: `No existe municipio con ${JSON.stringify(valor)}` }; + } +};