utn-cli 2.1.18 → 2.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,370 +0,0 @@
1
- const { ejecutarConsulta, ejecutarConsultaSIGU } = require('../db.js');
2
-
3
- module.exports = {
4
-
5
- async generarFirmaHTML(Identificador, FechaDeLaFirma) {
6
- const ReporteHTML = require('./ReporteHTML.js');
7
- return await ReporteHTML.generarFirmaHTML(Identificador, FechaDeLaFirma);
8
- },
9
-
10
- GenerarReporteHTMLRegistrosVerticales(ElementosParaLaTabla, ParametrosExcluidos = [], ParametrosExtra = [], MostrarEncabezado = true) {
11
- const ReporteHTML = require('./ReporteHTML.js');
12
- return ReporteHTML.GenerarReporteHTMLRegistrosVerticales(ElementosParaLaTabla, ParametrosExcluidos, ParametrosExtra, MostrarEncabezado);
13
- },
14
-
15
- GenerarReporteHTMLEncabezado(InformacionDeLaDerecha, titulares, marcaDeAgua = '') {
16
- const ReporteHTML = require('./ReporteHTML.js');
17
- return ReporteHTML.GenerarReporteHTMLEncabezado(InformacionDeLaDerecha, titulares, marcaDeAgua);
18
- },
19
-
20
- GenerarReporteHTMLFecha() {
21
- const ReporteHTML = require('./ReporteHTML.js');
22
- return ReporteHTML.GenerarReporteHTMLFecha();
23
- },
24
-
25
- GenerarReporteHTMLTablas(ElementosParaLaTabla, ParametrosExcluidos = [], ParametrosExtra = []) {
26
- const ReporteHTML = require('./ReporteHTML.js');
27
- return ReporteHTML.GenerarReporteHTMLTablas(ElementosParaLaTabla, ParametrosExcluidos, ParametrosExtra);
28
- },
29
-
30
- GenerarReporteHTMLPie() {
31
- const ReporteHTML = require('./ReporteHTML.js');
32
- return ReporteHTML.GenerarReporteHTMLPie();
33
- },
34
-
35
- JSONAHTML(input, title = 'Reporte') {
36
- let obj = input;
37
- if (typeof input === 'string') {
38
- try { obj = JSON.parse(input); }
39
- catch (e) { /* no es JSON, lo tratamos como texto primitivo */ }
40
- }
41
-
42
- const escapeHtml = (s) =>
43
- String(s)
44
- .replace(/&/g, '&')
45
- .replace(/</g, '&lt;')
46
- .replace(/>/g, '&gt;')
47
- .replace(/"/g, '&quot;')
48
- .replace(/'/g, '&#39;');
49
-
50
- const isPrimitive = v => v === null || v === undefined || typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean';
51
-
52
- const seen = new WeakSet();
53
-
54
- function renderValue(v) {
55
- if (v === null || v === undefined || v === '') return '—';
56
- if (isPrimitive(v)) return escapeHtml(String(v));
57
- if (Array.isArray(v)) return renderArray(v);
58
- return renderObject(v);
59
- }
60
-
61
- function renderObject(o) {
62
- if (seen.has(o)) return '<em>(referencia circular)</em>';
63
- seen.add(o);
64
- const keys = Object.keys(o);
65
- if (keys.length === 0) {
66
- seen.delete(o);
67
- return '—';
68
- }
69
- let rows = '';
70
- for (const k of keys) {
71
- rows += `
72
- <tr>
73
- <th>${escapeHtml(k)}</th>
74
- <td>${renderValue(o[k])}</td>
75
- </tr>
76
- `;
77
- }
78
- seen.delete(o);
79
- return `<table border='1'><tbody>${rows}</tbody></table>`;
80
- }
81
-
82
- function renderArray(arr) {
83
- if (arr.length === 0) return '—';
84
- if (arr.every(isPrimitive)) {
85
- return `<div>${arr.map(x => `<span>${escapeHtml(String(x))}</span>`).join(' ')}</div>`;
86
- }
87
- if (arr.every(it => typeof it === 'object' && it !== null && !Array.isArray(it))) {
88
- const columns = Array.from(new Set(arr.flatMap(item => Object.keys(item))));
89
- const header = columns.map(c => `<th>${escapeHtml(c)}</th>`).join('');
90
- const body = arr.map(item => {
91
- const cells = columns.map(c => `<td>${item.hasOwnProperty(c) ? renderValue(item[c]) : ''}</td>`).join('');
92
- return `<tr>${cells}</tr>`;
93
- }).join('');
94
- return `<table border='1'><thead><tr>${header}</tr></thead><tbody>${body}</tbody></table>`;
95
- }
96
- return `<div>${arr.map(it => `<div>${renderValue(it)}</div>`).join('')}</div>`;
97
- }
98
-
99
- let content = '';
100
- if (typeof obj === 'object' && obj !== null && !Array.isArray(obj)) {
101
- const topKeys = Object.keys(obj);
102
- for (const k of topKeys) {
103
- content += `
104
- <section>
105
- <h2>${escapeHtml(k)}</h2>
106
- <div>${renderValue(obj[k])}</div>
107
- </section>
108
- `;
109
- }
110
- } else {
111
- content = `<section><div>${renderValue(obj)}</div></section>`;
112
- }
113
-
114
- return `<h1>${escapeHtml(title)}</h1>${content}`;
115
- },
116
-
117
- convertirACSV(ArregloDeJSON) {
118
- if (!ArregloDeJSON || ArregloDeJSON.length === 0) return '';
119
- const intentarParsearJSON = (valor) => {
120
- if (typeof valor === 'object' && valor !== null && !Array.isArray(valor)) return valor;
121
- if (typeof valor === 'string' && valor.trim().startsWith('{')) {
122
- try {
123
- const objeto = JSON.parse(valor);
124
- if (typeof objeto === 'object' && objeto !== null && !Array.isArray(objeto)) return objeto;
125
- } catch (e) { }
126
- }
127
- return null;
128
- };
129
- const columnasOriginales = Object.keys(ArregloDeJSON[0]);
130
- const mapaColumnasJSON = {};
131
- columnasOriginales.forEach(col => {
132
- const todasLasLlaves = new Set();
133
- let esJSON = false;
134
- ArregloDeJSON.forEach(fila => {
135
- const obj = intentarParsearJSON(fila[col]);
136
- if (obj) {
137
- esJSON = true;
138
- Object.keys(obj).forEach(k => todasLasLlaves.add(k));
139
- }
140
- });
141
- if (esJSON) mapaColumnasJSON[col] = Array.from(todasLasLlaves);
142
- });
143
-
144
- const encabezadosFinales = [];
145
- columnasOriginales.forEach(col => {
146
- if (mapaColumnasJSON[col]) {
147
- mapaColumnasJSON[col].forEach(llave => encabezadosFinales.push(`${col}_${llave}`));
148
- } else {
149
- encabezadosFinales.push(col);
150
- }
151
- });
152
-
153
- const filasCSV = ArregloDeJSON.map(fila => {
154
- const valoresFila = [];
155
- columnasOriginales.forEach(col => {
156
- if (mapaColumnasJSON[col]) {
157
- const obj = intentarParsearJSON(fila[col]) || {};
158
- mapaColumnasJSON[col].forEach(llave => {
159
- let valor = obj[llave];
160
- if (valor === undefined || valor === null || valor === 'null') valor = '';
161
- valoresFila.push(JSON.stringify(String(valor)).replace(/,/g, ' '));
162
- });
163
- } else {
164
- let valor = fila[col];
165
- if (valor === null || valor === 'null') valor = '';
166
- valoresFila.push(JSON.stringify(String(valor)).replace(/,/g, ' '));
167
- }
168
- });
169
- return valoresFila.join(',');
170
- });
171
- return [encabezadosFinales.join(','), ...filasCSV].join('\n');
172
- },
173
-
174
- convertirACSVConDetalle(ArregloDeJSON, ColumnaConDetalle) {
175
- if (!ArregloDeJSON || ArregloDeJSON.length === 0) return '';
176
- const ejemploConDetalle = ArregloDeJSON.find(e => Array.isArray(e[ColumnaConDetalle]) && e[ColumnaConDetalle].length > 0);
177
- const camposDetalle = ejemploConDetalle ? Object.keys(ejemploConDetalle[ColumnaConDetalle][0]) : [];
178
- const columnasBase = Object.keys(ArregloDeJSON[0]).filter(key => key !== ColumnaConDetalle);
179
- const encabezados = [...columnasBase, ...camposDetalle];
180
- const filas = ArregloDeJSON.flatMap(fila => {
181
- const detalles = Array.isArray(fila[ColumnaConDetalle]) ? fila[ColumnaConDetalle] : [];
182
- if (detalles.length === 0) {
183
- return [
184
- encabezados.map(col => {
185
- const valor = fila[col];
186
- return formatearValor(valor);
187
- }).join(',')
188
- ];
189
- }
190
- return detalles.map(detalle => {
191
- return encabezados.map(col => {
192
- if (camposDetalle.includes(col)) {
193
- return formatearValor(detalle[col]);
194
- } else {
195
- return formatearValor(fila[col]);
196
- }
197
- }).join(',');
198
- });
199
- });
200
- return [encabezados.join(','), ...filas].join('\n');
201
- function formatearValor(valor) {
202
- if (valor === null || valor === 'null' || typeof valor === 'undefined') {
203
- return '""';
204
- }
205
- return `"${String(valor).replace(/"/g, '""')}"`;
206
- }
207
- },
208
-
209
- jsonATabla(Datos) {
210
- if (!Array.isArray(Datos) || Datos.length === 0) {
211
- return 'El arreglo está vacío o no es válido.';
212
- }
213
- const columnas = Object.keys(Datos[0]);
214
- const anchos = columnas.map(columna => {
215
- return Math.max(
216
- columna.length,
217
- ...Datos.map(row => (row[columna] ? row[columna].toString().length : 0))
218
- );
219
- });
220
- const formatearFila = (fila) => '| ' + fila.map((valor, index) => (valor || '').toString().padEnd(anchos[index])).join(' | ') + ' |';
221
- const separador = '+-' + anchos.map(ancho => '-'.repeat(ancho)).join('-+-') + '-+';
222
- const encabezado = formatearFila(columnas);
223
- const filas = Datos.map(row => formatearFila(columnas.map(col => row[col] || '')));
224
- return [separador, encabezado, separador, ...filas, separador].join('\n');
225
- },
226
-
227
- async DatosParaReporteCSV() {
228
- return this.convertirACSV(await this.DatosParaGraficoDeBarras());
229
- },
230
-
231
- async DatosParaGraficoDeBarras() {
232
- return await ejecutarConsultaSIGU("SELECT MONTHNAME(`FechaNacimiento`) AS `EjeHorizontal`, `Sexo` AS `Etiqueta`, COUNT(*) AS `Total` FROM `SIGU`.`SIGU_Personas` WHERE MONTH(`FechaNacimiento`) > 0 AND `Sexo` <> '' GROUP BY MONTHNAME(`FechaNacimiento`), `Sexo` ORDER BY MONTH(`FechaNacimiento`)");
233
- },
234
-
235
- async DatosParaGraficoDePie() {
236
- return await ejecutarConsultaSIGU("SELECT `Tipo` AS `Etiqueta`, COUNT(*) AS `Total` FROM `SIGU`.`SIGU_ModulosV2` GROUP BY `Tipo`");
237
- },
238
-
239
- async generarObjetoInfoDeUnReportePDF() {
240
- const UUID = await ejecutarConsulta("SELECT UUID() AS `Dato`");
241
- const PalabrasClave = {
242
- "Módulo": this.NombreCanonicoDelModulo,
243
- "UUID": UUID[0]['Dato']
244
- };
245
- return {
246
- Title: 'Nombre del reporte, como por ejemplo: Boleta de Vacaciones',
247
- Author: 'Universidad Técnica Nacional',
248
- Subject: 'Reporte PDF',
249
- Keywords: JSON.stringify(PalabrasClave),
250
- Creator: 'SIGU',
251
- Producer: 'SIGU'
252
- };
253
- },
254
-
255
- agregarMarcaDeAgua(doc, texto = 'Borrador', opciones = {}) {
256
- const {
257
- color = '#CCCCCC',
258
- opacity = 0.3,
259
- } = opciones;
260
-
261
- const { width, height } = doc.page;
262
-
263
- doc.save();
264
- doc
265
- .font('Times-Roman', 220)
266
- .fillColor(color)
267
- .opacity(opacity)
268
- .rotate(-55, { origin: [width / 2, height / 2] })
269
- .text(
270
- texto,
271
- -width * 0.2,
272
- height * 0.4,
273
- {
274
- align: 'center',
275
- valign: 'center',
276
- width: width * 1.5,
277
- }
278
- );
279
- doc.restore();
280
- },
281
-
282
- agregarTablaElegante(doc, datos, opciones = {}) {
283
- const { x = 50, y = 50, columnWidth = 100, rowHeight = 20, headerHeight = 25, fontSize = 10 } = opciones;
284
-
285
- doc.fontSize(fontSize);
286
-
287
- const encabezados = Object.keys(datos[0]);
288
-
289
- encabezados.forEach((encabezado, i) => {
290
- const posX = x + i * columnWidth;
291
- doc
292
- .rect(posX, y, columnWidth, headerHeight)
293
- .fillAndStroke('#d3d3d3', '#000')
294
- .fillColor('#000')
295
- .text(encabezado, posX + 5, y + 5, { width: columnWidth - 10, align: 'left' });
296
- });
297
-
298
- datos.forEach((fila, filaIndex) => {
299
- const filaY = y + headerHeight + filaIndex * rowHeight;
300
- encabezados.forEach((columna, colIndex) => {
301
- const posX = x + colIndex * columnWidth;
302
- const texto = fila[columna] !== undefined ? fila[columna].toString() : '';
303
- doc
304
- .rect(posX, filaY, columnWidth, rowHeight)
305
- .stroke()
306
- .fillColor('#000')
307
- .text(texto, posX + 5, filaY + 5, { width: columnWidth - 10, align: 'left' });
308
- });
309
- });
310
-
311
- return doc;
312
- },
313
-
314
- async reportePDFDeEjemplo(Respuesta) {
315
- Respuesta.setHeader('Content-Type', 'application/pdf');
316
- Respuesta.setHeader('Content-Disposition', 'inline; filename="reporte.pdf"');
317
- const PDFDocument = require('pdfkit');
318
-
319
- const opciones = {
320
- font: 'Courier',
321
- size: 'LETTER',
322
- info: await this.generarObjetoInfoDeUnReportePDF()
323
- };
324
- var doc = new PDFDocument(opciones);
325
- doc.pipe(Respuesta);
326
-
327
- this.agregarMarcaDeAgua(doc, 'Borrador', {
328
- fontSize: 80,
329
- opacity: 0.2,
330
- color: '#FF0000',
331
- });
332
-
333
- doc.fontSize(25).text('Here is some vector graphics...', 100, 80);
334
-
335
- doc.save()
336
- .moveTo(100, 150)
337
- .lineTo(100, 250)
338
- .lineTo(200, 250)
339
- .fill('#FF3300');
340
-
341
- doc.circle(280, 200, 50).fill('#6600FF');
342
-
343
- doc.scale(0.6)
344
- .translate(470, 130)
345
- .path('M 250,75 L 323,301 131,161 369,161 177,301 z')
346
- .fill('red', 'even-odd')
347
- .restore();
348
-
349
- doc.text('And here is some wrapped text...', 100, 300)
350
- .font('Times-Roman', 13)
351
- .moveDown()
352
- .text("lorem", {
353
- width: 412,
354
- align: 'justify',
355
- indent: 30,
356
- columns: 2,
357
- height: 300,
358
- ellipsis: true
359
- });
360
-
361
- const datos = [
362
- { Nombre: 'Juan', Edad: 28, Ciudad: 'San José', LugarDeTrabajo: 'UTN' },
363
- { Nombre: 'Ana', Edad: 34 },
364
- { Nombre: 'Luis', Edad: 25, Ciudad: 'Alajuela' },
365
- ];
366
- this.agregarTablaElegante(doc, datos, { x: 0, y: 400, columnWidth: 150, fontSize: 12 });
367
- doc.end();
368
- },
369
-
370
- };
@@ -1,105 +0,0 @@
1
- const { ejecutarConsultaSIGU } = require('../db.js');
2
-
3
- module.exports = {
4
-
5
- async crearTareaProgramada(Tarea) {
6
- const os = require('node:os');
7
- return await ejecutarConsultaSIGU("INSERT INTO `SIGU`.`SIGU_ModulosTareasProgramadas` VALUES\
8
- (?, ?, 'Ejecutada', NOW(4), ?) ON DUPLICATE KEY UPDATE `NombreDelEquipo` = ?"
9
- , [this.NombreDelRepositorioDelBackend, Tarea.name, os.hostname(), os.hostname()]);
10
- },
11
-
12
- async pseudoEjecutarTareaProgramada(Tarea) {
13
- const os = require('node:os');
14
- const Resultado = await ejecutarConsultaSIGU("UPDATE `SIGU`.`SIGU_ModulosTareasProgramadas` SET `Estado` = 'Procesando'\
15
- , `NombreDelEquipo` = ?, `FechaYHoraDeLaUltimaEjecucion` = NOW(4)\
16
- WHERE `Repositorio` = ? AND `TareaProgramada` = ? AND `Estado` IN ('Ejecutada', 'Cancelada', 'Fallida')"
17
- , [os.hostname(), this.NombreDelRepositorioDelBackend, Tarea]);
18
- return Resultado['affectedRows'];
19
- },
20
-
21
- async finalizarTareaProgramada(Tarea) {
22
- const os = require('node:os');
23
- const Resultado = await ejecutarConsultaSIGU("UPDATE `SIGU`.`SIGU_ModulosTareasProgramadas` SET `Estado` = 'Ejecutada'\
24
- , `NombreDelEquipo` = ?, `FechaYHoraDeLaUltimaEjecucion` = NOW(4)\
25
- WHERE `Repositorio` = ? AND `TareaProgramada` = ? AND `Estado` IN ('Procesando')"
26
- , [os.hostname(), this.NombreDelRepositorioDelBackend, Tarea]);
27
- return Resultado['affectedRows'];
28
- },
29
-
30
- async ejecucionCadaXMinutos(callback, intervaloMinutos) {
31
- while (true) {
32
- try {
33
- await this.crearTareaProgramada(callback);
34
- break;
35
- } catch (error) {
36
- console.error('Error al ejecutar crearTareaProgramada:', error);
37
- console.log('Reintentando en 5 segundos...');
38
- await new Promise(resolve => setTimeout(resolve, 5000));
39
- }
40
- }
41
- const intervaloMilisegundos = intervaloMinutos * 60 * 1000;
42
- while (true) {
43
- const inicio = Date.now();
44
- try {
45
- await callback();
46
- } catch (error) {
47
- console.error(`Error en la función "${callback.name}":`, error?.message || error);
48
- }
49
- const duracion = Date.now() - inicio;
50
- const tiempoRestante = intervaloMilisegundos - duracion;
51
- if (tiempoRestante > 0) {
52
- await new Promise(resolve => setTimeout(resolve, tiempoRestante));
53
- }
54
- }
55
- },
56
-
57
- async ejecutarEnHoraEspecifica(hora, minuto, segundo, callback) {
58
- while (true) {
59
- try {
60
- await this.crearTareaProgramada(callback);
61
- break;
62
- } catch (error) {
63
- console.error('Error al ejecutar crearTareaProgramada:', error);
64
- console.log('Reintentando en 5 segundos...');
65
- await new Promise(resolve => setTimeout(resolve, 5000));
66
- }
67
- }
68
- function programarEjecucion() {
69
- const ahora = new Date();
70
- const proximaEjecucion = new Date(ahora);
71
- proximaEjecucion.setHours(hora, minuto, segundo, 0);
72
- let tiempoEspera = proximaEjecucion - ahora;
73
- if (tiempoEspera < 0) {
74
- proximaEjecucion.setDate(proximaEjecucion.getDate() + 1);
75
- tiempoEspera = proximaEjecucion - ahora;
76
- }
77
- console.log(`Se ha programado a '${callback.name}' para ejecución a las ${proximaEjecucion.toLocaleTimeString()}`);
78
- setTimeout(() => {
79
- callback();
80
- programarEjecucion();
81
- }, tiempoEspera);
82
- }
83
- programarEjecucion();
84
- },
85
-
86
- async ejecucionDiferida(callback) {
87
- const stackLines = new Error().stack.split('\n');
88
- const lineaCaller = stackLines[2] ?? '';
89
- const match = lineaCaller.match(/\((.+?):\d+:\d+\)/) ?? lineaCaller.match(/at (.+?):\d+:\d+/);
90
- const archivo = match ? require('path').basename(match[1]) : null;
91
- while (true) {
92
- try {
93
- this._archivoFuenteActual = archivo;
94
- await callback();
95
- break;
96
- } catch (error) {
97
- console.error(`Error en la función "${callback.name}": `, error.message);
98
- console.log('Reintentando en 5 segundos...');
99
- await new Promise(resolve => setTimeout(resolve, 5000));
100
- }
101
- }
102
- this._archivoFuenteActual = null;
103
- },
104
-
105
- };