aziendasanitaria-utils 1.2.39 → 1.2.40
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.
- package/package.json +3 -2
- package/src/Procedure.js +5 -4
- package/src/Utils.js +59 -5
- package/src/m/FlussoM.js +267 -35
- package/src/narTsServices/Medici.js +98 -95
- package/src/narTsServices/Nar2.js +42 -24
- package/src/siad/FlussoSIAD.js +276 -31
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"engines": {
|
|
4
4
|
"node": ">=14.0.0"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.2.
|
|
6
|
+
"version": "1.2.40",
|
|
7
7
|
"repository": "deduzzo/aziendasanitaria-utils",
|
|
8
8
|
"description": "Un utility per gestire i flussi sanitari Siciliani e non solo..",
|
|
9
9
|
"main": "index.js",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"test": "echo \"Error: no test specified\"",
|
|
20
20
|
"run-private": "node ./private_example.js",
|
|
21
|
-
"bun-run-private": "bun ./private_example.js"
|
|
21
|
+
"bun-run-private": "bun ./private_example.js",
|
|
22
|
+
"genera-strutture": "node ./load_strutture.js"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
24
25
|
"@freiraum/msgreader": "^1.1.1",
|
package/src/Procedure.js
CHANGED
|
@@ -132,8 +132,9 @@ class Procedure {
|
|
|
132
132
|
for (let assistito of assistitiNar[codReg].assistiti) {
|
|
133
133
|
allAssistitiDistrettuali[distretto].nar.push({...assistito, ...medico});
|
|
134
134
|
}
|
|
135
|
-
for (let
|
|
136
|
-
|
|
135
|
+
for (let assistiti of assistitiTs[codReg] ?? []) {
|
|
136
|
+
if (assistiti)
|
|
137
|
+
allAssistitiDistrettuali[distretto].ts.push({...assistiti, ...medico});
|
|
137
138
|
}
|
|
138
139
|
allAssistitiDistrettuali[distretto].codRegNar[codReg] = assistitiNar[codReg].assistiti;
|
|
139
140
|
allAssistitiDistrettuali[distretto].codRegTs[codReg] = assistitiTs[codReg];
|
|
@@ -188,7 +189,7 @@ class Procedure {
|
|
|
188
189
|
*
|
|
189
190
|
* @return {Promise<number>} Returns 0 upon successful processing.
|
|
190
191
|
*/
|
|
191
|
-
static async getControlliEsenzione(pathElenco, anno,impostazioniServizi, config={}){
|
|
192
|
+
static async getControlliEsenzione(pathElenco, anno, impostazioniServizi, config = {}) {
|
|
192
193
|
let {
|
|
193
194
|
colonnaProtocolli = "PROTOCOLLO",
|
|
194
195
|
colonnaEsenzione = "ESENZIONE",
|
|
@@ -394,7 +395,7 @@ class Procedure {
|
|
|
394
395
|
}
|
|
395
396
|
}
|
|
396
397
|
}
|
|
397
|
-
if (Object.values(outData) >0)
|
|
398
|
+
if (Object.values(outData) > 0)
|
|
398
399
|
outFinal.push(outData);
|
|
399
400
|
da = da.add(1, "month");
|
|
400
401
|
} while (da.isSameOrBefore(a) && !singoloCedolino);
|
package/src/Utils.js
CHANGED
|
@@ -853,13 +853,67 @@ const riunisciJsonDaTag = async (path, tag, filter = null) => {
|
|
|
853
853
|
return out;
|
|
854
854
|
}
|
|
855
855
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
856
|
+
/**
|
|
857
|
+
* Riunisci dati da tutti i file Excel trovati ricorsivamente in `path`.
|
|
858
|
+
*
|
|
859
|
+
* - Cerca tutti i file `.xlsx` all'interno della cartella `path` (ricorsivamente),
|
|
860
|
+
* applicando opzionalmente un `filter` sui nomi dei file.
|
|
861
|
+
* - Per ogni file trovato legge il primo foglio usando `getObjectFromFileExcel`.
|
|
862
|
+
* - Se `config.tags` è un array non vuoto, per ogni `tag` crea un array in output
|
|
863
|
+
* e ci inserisce i valori corrispondenti (per ogni riga prende `row[tag]`).
|
|
864
|
+
* - Se `config.tags` è vuoto, costruisce l'output con tutte le colonne presenti
|
|
865
|
+
* nella prima riga del primo file (`data[0]`).
|
|
866
|
+
* - Se `config.salvaInNuovoFileExcel` è true, salva il risultato in
|
|
867
|
+
* un file `riunito.xlsx` nella cartella `path`.
|
|
868
|
+
*
|
|
869
|
+
* @param {string} path - Cartella radice dove cercare i file Excel.
|
|
870
|
+
* @param {Object} [config={}] - Configurazione opzionale.
|
|
871
|
+
* @param {string[]} [config.tags=[]] - Array di colonne (nomi) da raccogliere.
|
|
872
|
+
* @param {string|null} [config.filter=null] - Filtro per i nomi dei file (opzionale).
|
|
873
|
+
* @param {boolean} [config.salvaInNuovoFileExcel=false] - Se true salva l'output in `riunito.xlsx`.
|
|
874
|
+
* @param {string|null} [config.soloValoriUniciPerCampo=null] - Se specificato, mantiene solo righe con valori unici per questo campo.
|
|
875
|
+
* @returns {Promise<Object>} Oggetto con chiavi corrispondenti ai tag (o colonne) e valori come array dei relativi valori.
|
|
876
|
+
*
|
|
877
|
+
* @example
|
|
878
|
+
* // Unisce le colonne "nome" e "cognome" da tutti gli xlsx nella cartella
|
|
879
|
+
* const out = await riunisciExcelDaTag(`/Users/deduzzo/dati`, { tags: ['nome','cognome'] });
|
|
880
|
+
*/
|
|
881
|
+
const riunisciExcelDaTag = async (folderPath, config = {}) => {
|
|
882
|
+
let {
|
|
883
|
+
tags = [],
|
|
884
|
+
filter = null,
|
|
885
|
+
salvaInNuovoFileExcel = false,
|
|
886
|
+
soloValoriUniciPerCampo = null
|
|
887
|
+
} = config;
|
|
888
|
+
let files = getAllFilesRecursive(folderPath, ".xlsx", filter);
|
|
889
|
+
let out = [];
|
|
860
890
|
for (let file of files) {
|
|
861
891
|
let data = await getObjectFromFileExcel(file);
|
|
862
|
-
|
|
892
|
+
if (tags.length === 0) {
|
|
893
|
+
// get all keys from data[0]
|
|
894
|
+
let keys = Object.keys(data[0]);
|
|
895
|
+
tags = keys;
|
|
896
|
+
}
|
|
897
|
+
// add every object of data but only the tags we want, ex. if tag is ['cf', 'data'] we want to put only object type {cf: 'xxx', data: 'yyy'} in out
|
|
898
|
+
for (let obj of data) {
|
|
899
|
+
let objTemp = {};
|
|
900
|
+
tags.forEach(tag => {
|
|
901
|
+
objTemp[tag] = obj[tag];
|
|
902
|
+
});
|
|
903
|
+
// prevent to push empty object
|
|
904
|
+
if (Object.values(objTemp).some(value => value !== ""))
|
|
905
|
+
out.push(objTemp);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (soloValoriUniciPerCampo) {
|
|
909
|
+
let out2 = {};
|
|
910
|
+
for (let riga of out)
|
|
911
|
+
if (!out2.hasOwnProperty(riga[soloValoriUniciPerCampo]))
|
|
912
|
+
out2[riga[soloValoriUniciPerCampo]] = riga;
|
|
913
|
+
out = Object.values(out2);
|
|
914
|
+
}
|
|
915
|
+
if (salvaInNuovoFileExcel) {
|
|
916
|
+
await scriviOggettoSuNuovoFileExcel(folderPath + path.sep + "riunito.xlsx", out);
|
|
863
917
|
}
|
|
864
918
|
return out;
|
|
865
919
|
}
|
package/src/m/FlussoM.js
CHANGED
|
@@ -10,6 +10,7 @@ import {DatiStruttureProgettoTs} from "./DatiStruttureProgettoTs.js";
|
|
|
10
10
|
import ExcelJS from "exceljs"
|
|
11
11
|
import loki from 'lokijs';
|
|
12
12
|
import {hashFile} from "hasha";
|
|
13
|
+
import * as cheerio from 'cheerio';
|
|
13
14
|
|
|
14
15
|
export class FlussoM {
|
|
15
16
|
/**
|
|
@@ -403,27 +404,76 @@ export class FlussoM {
|
|
|
403
404
|
}
|
|
404
405
|
|
|
405
406
|
|
|
406
|
-
#
|
|
407
|
-
|
|
408
|
-
|
|
407
|
+
// DEPRECATO: sostituito da #loadStruttureFromLatestFile()
|
|
408
|
+
// #loadStruttureFromFlowlookDB() {
|
|
409
|
+
// const buffer = fs.readFileSync(this._settings.flowlookDBFilePath);
|
|
410
|
+
// const reader = new MDBReader(buffer);
|
|
411
|
+
// const strutture = reader.getTable(this._settings.flowlookDBTableSTS11).getData();
|
|
412
|
+
// let struttureFiltrate = strutture.filter(p => p["CodiceAzienda"] === this._settings.codiceAzienda && p["CodiceRegione"] === this._settings.codiceRegione && (parseInt(p["Anno"]) >= (new Date().getFullYear()) - 2));
|
|
413
|
+
// let mancanti = []
|
|
414
|
+
// let struttureOut = {}
|
|
415
|
+
// struttureFiltrate.forEach(p => {
|
|
416
|
+
// if (this._settings.datiStruttureRegione.comuniDistretti.hasOwnProperty(p["CodiceComune"])) {
|
|
417
|
+
// struttureOut[p['CodiceStruttura']] = {
|
|
418
|
+
// codiceRegione: p['CodiceRegione'],
|
|
419
|
+
// codiceAzienda: p['CodiceAzienda'],
|
|
420
|
+
// denominazione: p['DenominazioneStruttura'],
|
|
421
|
+
// codiceComune: p['CodiceComune'],
|
|
422
|
+
// idDistretto: this._settings.datiStruttureRegione.comuniDistretti[p["CodiceComune"]],
|
|
423
|
+
// dataUltimoAggiornamento: moment(p['DataAggiornamento'], 'DD/MM/YYYY')
|
|
424
|
+
// };
|
|
425
|
+
// } else
|
|
426
|
+
// mancanti.push(p);
|
|
427
|
+
// })
|
|
428
|
+
// return struttureOut;
|
|
429
|
+
// }
|
|
430
|
+
|
|
431
|
+
#loadStruttureFromLatestFile() {
|
|
432
|
+
const struttureDir = path.join("data", "strutture");
|
|
433
|
+
if (!fs.existsSync(struttureDir)) {
|
|
434
|
+
throw new Error(`Cartella ${struttureDir} non trovata. Eseguire prima la generazione delle strutture con load_strutture.js`);
|
|
435
|
+
}
|
|
409
436
|
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
437
|
+
const latestFile = fs.readdirSync(struttureDir).find(f => f.startsWith("LATEST_"));
|
|
438
|
+
if (!latestFile) {
|
|
439
|
+
throw new Error(`Nessun file LATEST_ trovato in ${struttureDir}. Eseguire prima la generazione delle strutture con load_strutture.js`);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const strutture = JSON.parse(fs.readFileSync(path.join(struttureDir, latestFile), "utf-8"));
|
|
443
|
+
|
|
444
|
+
// Costruisci reverse lookup: nome distretto -> id numerico
|
|
445
|
+
const reverseDistretti = {};
|
|
446
|
+
for (const [id, nome] of Object.entries(this._settings.datiStruttureRegione.distretti)) {
|
|
447
|
+
reverseDistretti[nome.toLowerCase()] = parseInt(id);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const struttureOut = {};
|
|
451
|
+
for (const s of strutture) {
|
|
452
|
+
let idDistretto = null;
|
|
453
|
+
if (s.distretto) {
|
|
454
|
+
const distLower = s.distretto.toLowerCase();
|
|
455
|
+
// Match esatto
|
|
456
|
+
idDistretto = reverseDistretti[distLower];
|
|
457
|
+
// Match parziale (es. "Barcellona P.G." contiene "barcellona")
|
|
458
|
+
if (idDistretto === undefined) {
|
|
459
|
+
const found = Object.entries(reverseDistretti).find(([nome]) =>
|
|
460
|
+
distLower.startsWith(nome) || nome.startsWith(distLower)
|
|
461
|
+
);
|
|
462
|
+
if (found) idDistretto = found[1];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
struttureOut[s.codice] = {
|
|
467
|
+
denominazione: s.denominazione,
|
|
468
|
+
codiceComune: s.codCatastale,
|
|
469
|
+
idDistretto: idDistretto,
|
|
470
|
+
distretto: s.distretto,
|
|
471
|
+
ambito: s.ambito,
|
|
472
|
+
stato: s.stato
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
console.log(`Caricate ${Object.keys(struttureOut).length} strutture da ${latestFile}`);
|
|
427
477
|
return struttureOut;
|
|
428
478
|
}
|
|
429
479
|
|
|
@@ -476,7 +526,7 @@ export class FlussoM {
|
|
|
476
526
|
}
|
|
477
527
|
|
|
478
528
|
async #ottieniStatDaFileFlussoM(file) {
|
|
479
|
-
let strutture = this.#
|
|
529
|
+
let strutture = this.#loadStruttureFromLatestFile();
|
|
480
530
|
let ricetteInFile = await this.#elaboraFileFlussoM(file, this._starts);
|
|
481
531
|
let warn = "";
|
|
482
532
|
if (ricetteInFile.error) {
|
|
@@ -486,13 +536,13 @@ export class FlussoM {
|
|
|
486
536
|
let verificaDateStruttura = this.#checkMeseAnnoStruttura(Object.values(ricetteInFile.ricette))
|
|
487
537
|
ricetteInFile.codiceStruttura = verificaDateStruttura.codiceStruttura;
|
|
488
538
|
ricetteInFile.file = file;
|
|
489
|
-
ricetteInFile.idDistretto = strutture[verificaDateStruttura.codiceStruttura]?.idDistretto
|
|
539
|
+
ricetteInFile.idDistretto = strutture[verificaDateStruttura.codiceStruttura]?.idDistretto?.toString() ?? (ricetteInFile.datiDaFile?.idDistretto ?? "X");
|
|
490
540
|
ricetteInFile.annoPrevalente = verificaDateStruttura.meseAnnoPrevalente.substr(2, 4);
|
|
491
541
|
ricetteInFile.mesePrevalente = verificaDateStruttura.meseAnnoPrevalente.substr(0, 2);
|
|
492
542
|
ricetteInFile.date = _.omitBy(verificaDateStruttura.date, _.isNil);
|
|
493
543
|
if (!strutture.hasOwnProperty(verificaDateStruttura.codiceStruttura)) {
|
|
494
|
-
console.log("STRUTTURA " + verificaDateStruttura.codiceStruttura + " non presente
|
|
495
|
-
warn = "STRUTTURA " + verificaDateStruttura.codiceStruttura + " non presente
|
|
544
|
+
console.log("STRUTTURA " + verificaDateStruttura.codiceStruttura + " non presente nel file strutture")
|
|
545
|
+
warn = "STRUTTURA " + verificaDateStruttura.codiceStruttura + " non presente nel file strutture"
|
|
496
546
|
}
|
|
497
547
|
return {errore: false, warning: (warn === "" ? false : warn), out: ricetteInFile}
|
|
498
548
|
}
|
|
@@ -536,7 +586,7 @@ export class FlussoM {
|
|
|
536
586
|
|
|
537
587
|
async generaReportDaStats(salvaFileHtml = true, salvaFileExcel = true) {
|
|
538
588
|
let idDistretti = Object.keys(this._settings.datiStruttureRegione.distretti);
|
|
539
|
-
let strutture = this.#
|
|
589
|
+
let strutture = this.#loadStruttureFromLatestFile();
|
|
540
590
|
let files = utils.getAllFilesRecursive(this._settings.out_folder, '.mstats');
|
|
541
591
|
const table = {
|
|
542
592
|
name: '',
|
|
@@ -897,11 +947,19 @@ export class FlussoM {
|
|
|
897
947
|
}
|
|
898
948
|
let lunghezzaRiga = utils.verificaLunghezzaRiga(this._starts);
|
|
899
949
|
const outputFile = nomeFile === "" ? (outFolder + path.sep + '190205_000_XXXX_XX_M_AL_20XX_XX_XX.TXT') : outFolder + path.sep + nomeFile;
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
950
|
+
|
|
951
|
+
// Rimuovi file esistente per evitare append duplicato
|
|
952
|
+
if (fs.existsSync(outputFile)) {
|
|
953
|
+
fs.unlinkSync(outputFile);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
console.log(`unisciFileTxt: ${allFiles.length} file da ${inFolder}`);
|
|
957
|
+
|
|
958
|
+
const logger = fs.createWriteStream(outputFile, { flags: 'w' });
|
|
959
|
+
const writeLine = (line) => logger.write(`${line}\n`);
|
|
960
|
+
let totalLines = 0;
|
|
961
|
+
|
|
962
|
+
for (const file of allFiles) {
|
|
905
963
|
const fileStream = fs.createReadStream(file);
|
|
906
964
|
const rl = readline.createInterface({
|
|
907
965
|
input: fileStream,
|
|
@@ -911,7 +969,6 @@ export class FlussoM {
|
|
|
911
969
|
let nLine = 0;
|
|
912
970
|
for await (const line of rl) {
|
|
913
971
|
writeLine(line);
|
|
914
|
-
// Each line in input.txt will be successively available here as `line`.
|
|
915
972
|
nLine++;
|
|
916
973
|
if (line.length !== lunghezzaRiga) {
|
|
917
974
|
console.log("file: " + file);
|
|
@@ -920,10 +977,14 @@ export class FlussoM {
|
|
|
920
977
|
errors.push({file: file, lunghezza: line.length, linea: nLine})
|
|
921
978
|
}
|
|
922
979
|
}
|
|
980
|
+
totalLines += nLine;
|
|
923
981
|
}
|
|
924
982
|
logger.end();
|
|
983
|
+
|
|
984
|
+
const fileSize = fs.existsSync(outputFile) ? fs.statSync(outputFile).size : 0;
|
|
985
|
+
console.log(`unisciFileTxt: ${totalLines} righe totali, ${(fileSize / 1024).toFixed(1)} KB -> ${outputFile}`);
|
|
986
|
+
|
|
925
987
|
if (errors.length === 0) {
|
|
926
|
-
//verifica
|
|
927
988
|
console.log("verifica.. ");
|
|
928
989
|
errors = [...errors, ...await this.#processLineByLine(outputFile, lunghezzaRiga)]
|
|
929
990
|
if (errors.length === 0)
|
|
@@ -933,7 +994,7 @@ export class FlussoM {
|
|
|
933
994
|
console.table(errors);
|
|
934
995
|
}
|
|
935
996
|
}
|
|
936
|
-
return {error: errors.length !== 0, errors: errors}
|
|
997
|
+
return {error: errors.length !== 0, errors: errors, totalFiles: allFiles.length, totalLines, fileSize}
|
|
937
998
|
}
|
|
938
999
|
|
|
939
1000
|
async inviaMailAiDistretti(distretti, meseAnno = "", mailGlobale = "", nomeFileCompleto = "CONSEGNE_GLOBALI") {
|
|
@@ -1440,7 +1501,7 @@ export class FlussoM {
|
|
|
1440
1501
|
|
|
1441
1502
|
|
|
1442
1503
|
async generaFileExcelPerAnno(nomeFile, anno, cosaGenerare = [FlussoM.PER_STRUTTURA_ANNO_MESE, FlussoM.TAB_CONSEGNE_PER_CONTENUTO, FlussoM.TAB_CONSEGNE_PER_NOME_FILE, FlussoM.TAB_DIFFERENZE_CONTENUTO_NOMEFILE]) {
|
|
1443
|
-
const strutture = this.#
|
|
1504
|
+
const strutture = this.#loadStruttureFromLatestFile();
|
|
1444
1505
|
let files = utils.getAllFilesRecursive(this._settings.out_folder, '.mstats');
|
|
1445
1506
|
let data = [];
|
|
1446
1507
|
for (let file of files) {
|
|
@@ -1616,7 +1677,7 @@ export class FlussoM {
|
|
|
1616
1677
|
}
|
|
1617
1678
|
}
|
|
1618
1679
|
|
|
1619
|
-
const strutture = this.#
|
|
1680
|
+
const strutture = this.#loadStruttureFromLatestFile();
|
|
1620
1681
|
|
|
1621
1682
|
const buffer = fs.readFileSync(this.settings.flowlookDBFilePath);
|
|
1622
1683
|
const reader = new MDBReader(buffer);
|
|
@@ -1748,4 +1809,175 @@ export class FlussoM {
|
|
|
1748
1809
|
}
|
|
1749
1810
|
}
|
|
1750
1811
|
|
|
1812
|
+
/**
|
|
1813
|
+
* Esegue lo scraping di una pagina HTML dell'elenco strutture del Progetto Tessera Sanitaria
|
|
1814
|
+
* e genera un file JSON con i dati estratti.
|
|
1815
|
+
* @param {string} htmlContent - Il contenuto HTML della pagina
|
|
1816
|
+
* @param {string} outputDir - La directory di output (default: "data")
|
|
1817
|
+
* @returns {Array<{codice: string, denominazione: string, indirizzo: string, localita: string, piva: string, stato: string}>}
|
|
1818
|
+
*/
|
|
1819
|
+
static scrapingStruttureProgettoTs(htmlContent, outputDir = "data/strutture") {
|
|
1820
|
+
const strutture = [];
|
|
1821
|
+
const isHTML = /<tr[\s>]/i.test(htmlContent) || /<td[\s>]/i.test(htmlContent);
|
|
1822
|
+
|
|
1823
|
+
if (isHTML) {
|
|
1824
|
+
const $ = cheerio.load(htmlContent);
|
|
1825
|
+
$('tr').each((i, row) => {
|
|
1826
|
+
const tds = $(row).find('td');
|
|
1827
|
+
if (tds.length < 5) return;
|
|
1828
|
+
|
|
1829
|
+
const codice = $(tds[0]).text().trim();
|
|
1830
|
+
if (!codice || codice === "Codice") return;
|
|
1831
|
+
|
|
1832
|
+
const denominazione = $(tds[1]).text().trim();
|
|
1833
|
+
const indirizzo = $(tds[2]).text().trim();
|
|
1834
|
+
const localita = $(tds[3]).text().trim();
|
|
1835
|
+
const piva = $(tds[4]).text().trim();
|
|
1836
|
+
|
|
1837
|
+
const rigaTesto = $(row).text();
|
|
1838
|
+
const stato = rigaTesto.includes("Struttura chiusa") ? "chiuso" : "attivo";
|
|
1839
|
+
|
|
1840
|
+
strutture.push({codice, denominazione, indirizzo, localita, piva, stato});
|
|
1841
|
+
});
|
|
1842
|
+
} else {
|
|
1843
|
+
// Parsing testo piano (copia-incolla dal browser)
|
|
1844
|
+
const lines = htmlContent.split('\n');
|
|
1845
|
+
for (const line of lines) {
|
|
1846
|
+
const trimmed = line.trim();
|
|
1847
|
+
if (!trimmed || !/^\d{6}\s/.test(trimmed)) continue;
|
|
1848
|
+
|
|
1849
|
+
const stato = trimmed.includes("Struttura chiusa") ? "chiuso" : "attivo";
|
|
1850
|
+
const cleaned = trimmed.replace(/\s+Visualizza Dettaglio.*$/, '');
|
|
1851
|
+
|
|
1852
|
+
// Prova prima con tab, poi con 4+ spazi
|
|
1853
|
+
let parts = cleaned.split('\t').map(p => p.trim()).filter(p => p);
|
|
1854
|
+
if (parts.length < 5) {
|
|
1855
|
+
parts = cleaned.split(/\s{4,}/).map(p => p.trim()).filter(p => p);
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
if (parts.length >= 5) {
|
|
1859
|
+
strutture.push({
|
|
1860
|
+
codice: parts[0],
|
|
1861
|
+
denominazione: parts[1],
|
|
1862
|
+
indirizzo: parts[2],
|
|
1863
|
+
localita: parts[3],
|
|
1864
|
+
piva: parts[4],
|
|
1865
|
+
stato
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
// Arricchimento con codice catastale e distretto dal CSV ambiti
|
|
1872
|
+
const csvPath = path.join(path.dirname(outputDir), "ambiti_distretti_messina.csv");
|
|
1873
|
+
if (fs.existsSync(csvPath)) {
|
|
1874
|
+
const csvContent = fs.readFileSync(csvPath, "utf-8");
|
|
1875
|
+
const ambiti = {};
|
|
1876
|
+
csvContent.split('\n').slice(1).forEach(line => {
|
|
1877
|
+
const parts = line.split(',');
|
|
1878
|
+
if (parts.length >= 3) {
|
|
1879
|
+
const ambito = parts[0].trim();
|
|
1880
|
+
if (ambito) {
|
|
1881
|
+
ambiti[ambito] = { codCatastale: parts[1].trim(), distretto: parts[2].trim() };
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
});
|
|
1885
|
+
|
|
1886
|
+
// Carica mapping manuale da file JSON esterno
|
|
1887
|
+
const mappingPath = path.join(path.dirname(outputDir), "mapping_strutture_extra.json");
|
|
1888
|
+
let mappingVarianti = {};
|
|
1889
|
+
let mappingPerCodice = {};
|
|
1890
|
+
if (fs.existsSync(mappingPath)) {
|
|
1891
|
+
const mappingData = JSON.parse(fs.readFileSync(mappingPath, "utf-8"));
|
|
1892
|
+
mappingVarianti = mappingData.per_comune || {};
|
|
1893
|
+
mappingPerCodice = mappingData.per_codice_struttura || {};
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// Normalizza stringa per match fuzzy (rimuove apostrofi, trattini, spazi multipli)
|
|
1897
|
+
const normalizza = (s) => s.toUpperCase().replace(/['\-\u2019\u0060]/g, '').replace(/\s+/g, ' ').trim();
|
|
1898
|
+
|
|
1899
|
+
// Costruisci indice normalizzato degli ambiti
|
|
1900
|
+
const ambitiNorm = {};
|
|
1901
|
+
for (const [key, val] of Object.entries(ambiti)) {
|
|
1902
|
+
ambitiNorm[normalizza(key)] = { ...val, ambitoOriginale: key };
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
let matchati = 0;
|
|
1906
|
+
let nonMatchati = [];
|
|
1907
|
+
|
|
1908
|
+
for (const s of strutture) {
|
|
1909
|
+
// Estrai comune dalla localita (rimuovi provincia es. "(ME)")
|
|
1910
|
+
let comune = s.localita.replace(/\s*\([A-Z]{2}\)\s*$/, '').trim();
|
|
1911
|
+
|
|
1912
|
+
// 0. Match per codice struttura (override manuale)
|
|
1913
|
+
if (mappingPerCodice[s.codice]) {
|
|
1914
|
+
comune = mappingPerCodice[s.codice];
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
// 1. Match diretto
|
|
1918
|
+
if (ambiti[comune]) {
|
|
1919
|
+
s.codCatastale = ambiti[comune].codCatastale;
|
|
1920
|
+
s.distretto = ambiti[comune].distretto;
|
|
1921
|
+
s.ambito = comune;
|
|
1922
|
+
matchati++;
|
|
1923
|
+
continue;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// 2. Match via mapping manuale per comune
|
|
1927
|
+
const mappato = mappingVarianti[comune];
|
|
1928
|
+
if (mappato && ambiti[mappato]) {
|
|
1929
|
+
s.codCatastale = ambiti[mappato].codCatastale;
|
|
1930
|
+
s.distretto = ambiti[mappato].distretto;
|
|
1931
|
+
s.ambito = mappato;
|
|
1932
|
+
matchati++;
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// 3. Match fuzzy normalizzato (apostrofi, trattini)
|
|
1937
|
+
const comuneNorm = normalizza(comune);
|
|
1938
|
+
if (ambitiNorm[comuneNorm]) {
|
|
1939
|
+
const found = ambitiNorm[comuneNorm];
|
|
1940
|
+
s.codCatastale = found.codCatastale;
|
|
1941
|
+
s.distretto = found.distretto;
|
|
1942
|
+
s.ambito = found.ambitoOriginale;
|
|
1943
|
+
matchati++;
|
|
1944
|
+
continue;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
// Non trovato
|
|
1948
|
+
s.codCatastale = null;
|
|
1949
|
+
s.distretto = null;
|
|
1950
|
+
s.ambito = null;
|
|
1951
|
+
nonMatchati.push({ codice: s.codice, denominazione: s.denominazione, localita: s.localita });
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
console.log(`Matching ambiti: ${matchati}/${strutture.length} matchati, ${nonMatchati.length} non matchati`);
|
|
1955
|
+
if (nonMatchati.length > 0) {
|
|
1956
|
+
console.log("Strutture senza match ambito:");
|
|
1957
|
+
nonMatchati.forEach(s => console.log(` ${s.codice} - ${s.denominazione} - ${s.localita}`));
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
const timestamp = moment().format("YYYYMMDD_HHmmss");
|
|
1962
|
+
const nomeFile = path.join(outputDir, `strutture_ts_${timestamp}.json`);
|
|
1963
|
+
|
|
1964
|
+
if (!fs.existsSync(outputDir)) {
|
|
1965
|
+
fs.mkdirSync(outputDir, {recursive: true});
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
fs.writeFileSync(nomeFile, JSON.stringify(strutture, null, 2), "utf-8");
|
|
1969
|
+
console.log(`Salvate ${strutture.length} strutture in ${nomeFile}`);
|
|
1970
|
+
|
|
1971
|
+
// Rimuovi eventuali file LATEST_ precedenti e crea il nuovo
|
|
1972
|
+
const files = fs.readdirSync(outputDir);
|
|
1973
|
+
files.filter(f => f.startsWith("LATEST_")).forEach(f => {
|
|
1974
|
+
fs.unlinkSync(path.join(outputDir, f));
|
|
1975
|
+
});
|
|
1976
|
+
const latestFile = path.join(outputDir, `LATEST_strutture_ts_${timestamp}.json`);
|
|
1977
|
+
fs.copyFileSync(nomeFile, latestFile);
|
|
1978
|
+
console.log(`Creato ${latestFile}`);
|
|
1979
|
+
|
|
1980
|
+
return strutture;
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1751
1983
|
}
|
|
@@ -512,7 +512,8 @@ export class Medici {
|
|
|
512
512
|
});
|
|
513
513
|
if (!error && !error2) {
|
|
514
514
|
await page.waitForSelector("body > div:nth-child(12) > div:nth-child(3) > div:nth-child(2)");
|
|
515
|
-
await page.click("#menu_voci > ol > li:nth-child(1) > a");
|
|
515
|
+
//await page.click("#menu_voci > ol > li:nth-child(1) > a");
|
|
516
|
+
await page.goto("https://sistemats4.sanita.finanze.it/simossMedici/elencoAssistiti.do", {waitUntil: 'networkidle2'});
|
|
516
517
|
// Attendiamo che il selettore #mef sia presente E che sia visibile
|
|
517
518
|
await page.waitForSelector("#mef", {
|
|
518
519
|
visibile: true,
|
|
@@ -573,7 +574,7 @@ export class Medici {
|
|
|
573
574
|
numParallelsJobs = 20,
|
|
574
575
|
visibile = false,
|
|
575
576
|
} = config;
|
|
576
|
-
|
|
577
|
+
//numParallelsJobs = 1;
|
|
577
578
|
EventEmitter.defaultMaxListeners = 40;
|
|
578
579
|
let out = {};
|
|
579
580
|
let jobs = [];
|
|
@@ -633,7 +634,7 @@ export class Medici {
|
|
|
633
634
|
};
|
|
634
635
|
allAssistitiNar.push(assistito.codiceFiscale);
|
|
635
636
|
}
|
|
636
|
-
for (let assistito of assistitiTs) {
|
|
637
|
+
for (let assistito of assistitiTs ?? []) {
|
|
637
638
|
if (!allAssistiti.hasOwnProperty(assistito.cf))
|
|
638
639
|
allAssistiti[assistito.cf] = {
|
|
639
640
|
nome_assistito: assistito.nome,
|
|
@@ -1016,104 +1017,106 @@ export class Medici {
|
|
|
1016
1017
|
|
|
1017
1018
|
|
|
1018
1019
|
async creaElenchiDeceduti(codToCfDistrettoMap, pathData, distretti, dataQuote = null) {
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
let
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
lista.perDistretto
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
let
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1020
|
+
if (!fs.existsSync(pathData + path.sep + "recuperi" + path.sep)) {
|
|
1021
|
+
let lista = {perDistretto: {}, nonTrovati: []};
|
|
1022
|
+
if (!dataQuote)
|
|
1023
|
+
dataQuote = moment().format("YYYY-MM-DD");
|
|
1024
|
+
let allfiles = utils.getAllFilesRecursive(pathData + path.sep + "elaborazioni" + path.sep, ".zip");
|
|
1025
|
+
let allAssistiti = await utils.leggiOggettoDaFileJSON(pathData + path.sep + "assistitiNar.json");
|
|
1026
|
+
for (let medico in codToCfDistrettoMap) {
|
|
1027
|
+
let fileMedico = allfiles.filter(file => file.includes(medico));
|
|
1028
|
+
if (fileMedico.length === 0)
|
|
1029
|
+
lista.nonTrovati.push(codToCfDistrettoMap[medico]);
|
|
1030
|
+
else if (fileMedico.length === 1) {
|
|
1031
|
+
//let fileData = await utils.leggiOggettoDaFileJSON(fileMedico[0]);
|
|
1032
|
+
let fileData = await utils.decomprimiELeggiFile(fileMedico[0])
|
|
1033
|
+
if (!lista.perDistretto.hasOwnProperty(codToCfDistrettoMap[medico].distretto))
|
|
1034
|
+
lista.perDistretto[codToCfDistrettoMap[medico].distretto] = {
|
|
1035
|
+
recuperi: [],
|
|
1036
|
+
problemi: [],
|
|
1037
|
+
anagrafica: [],
|
|
1038
|
+
medici: {}
|
|
1039
|
+
};
|
|
1040
|
+
for (let deceduto of Object.values(fileData.deceduti)) {
|
|
1041
|
+
let allAssistitiMedicoMap = {};
|
|
1042
|
+
for (let assistito of Object.values(allAssistiti[medico].assistiti)) {
|
|
1043
|
+
allAssistitiMedicoMap[assistito.codiceFiscale] = assistito;
|
|
1044
|
+
}
|
|
1045
|
+
let numQuote = 0;
|
|
1046
|
+
let dataScelta = moment(allAssistitiMedicoMap[deceduto.cf].data_scelta, "DD/MM/YYYY");
|
|
1045
1047
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1048
|
+
if (!deceduto.hasOwnProperty('indirizzo') || deceduto.indirizzo === "" || deceduto.indirizzo === null)
|
|
1049
|
+
deceduto.indirizzo = "-";
|
|
1050
|
+
deceduto.dataScelta = dataScelta.format("DD/MM/YYYY");
|
|
1051
|
+
deceduto.codMedico = medico;
|
|
1052
|
+
deceduto.nomeCognomeMedico = codToCfDistrettoMap[medico].nome_cognome;
|
|
1053
|
+
deceduto.distretto = distretti[codToCfDistrettoMap[medico].distretto];
|
|
1054
|
+
deceduto.ambito = codToCfDistrettoMap[medico].ambito;
|
|
1055
|
+
if (deceduto.hasOwnProperty('dataDecesso') && deceduto.dataDecesso !== "" && deceduto.dataDecesso !== null && moment(deceduto.dataDecesso, "DD/MM/YYYY").isAfter(moment("01/01/1900", "DD/MM/YYYY"))) {
|
|
1056
|
+
let dataInizio = moment(deceduto.dataDecesso, "DD/MM/YYYY").isBefore(dataScelta) ? dataScelta.format("DD/MM/YYYY") : deceduto.dataDecesso;
|
|
1057
|
+
// se la data di decesso è superiore al 1 gennaio 1900
|
|
1058
|
+
numQuote = utils.calcolaMesiDifferenza(dataInizio, dataQuote);
|
|
1059
|
+
deceduto.numQuoteDaRecuperare = numQuote;
|
|
1060
|
+
deceduto.note = "";
|
|
1061
|
+
if (moment(deceduto.dataDecesso, "DD/MM/YYYY").isBefore(dataScelta)) {
|
|
1062
|
+
deceduto.note = "Data decesso precedente alla data di scelta";
|
|
1063
|
+
lista.perDistretto[codToCfDistrettoMap[medico].distretto].problemi.push(deceduto);
|
|
1064
|
+
} else {
|
|
1065
|
+
lista.perDistretto[codToCfDistrettoMap[medico].distretto].recuperi.push(deceduto);
|
|
1066
|
+
if (!lista.perDistretto[codToCfDistrettoMap[medico].distretto].medici.hasOwnProperty(medico))
|
|
1067
|
+
lista.perDistretto[codToCfDistrettoMap[medico].distretto].medici[medico] = {
|
|
1068
|
+
codice: medico,
|
|
1069
|
+
distretto: distretti[codToCfDistrettoMap[medico].distretto],
|
|
1070
|
+
ambito: codToCfDistrettoMap[medico].ambito,
|
|
1071
|
+
nomeCognome: codToCfDistrettoMap[medico].nome_cognome,
|
|
1072
|
+
numDeceduti: 0,
|
|
1073
|
+
quote: 0
|
|
1074
|
+
};
|
|
1075
|
+
lista.perDistretto[codToCfDistrettoMap[medico].distretto].medici[medico].numDeceduti += 1;
|
|
1076
|
+
lista.perDistretto[codToCfDistrettoMap[medico].distretto].medici[medico].quote += numQuote;
|
|
1077
|
+
}
|
|
1062
1078
|
} else {
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
codice: medico,
|
|
1067
|
-
distretto: distretti[codToCfDistrettoMap[medico].distretto],
|
|
1068
|
-
ambito: codToCfDistrettoMap[medico].ambito,
|
|
1069
|
-
nomeCognome: codToCfDistrettoMap[medico].nome_cognome,
|
|
1070
|
-
numDeceduti: 0,
|
|
1071
|
-
quote: 0
|
|
1072
|
-
};
|
|
1073
|
-
lista.perDistretto[codToCfDistrettoMap[medico].distretto].medici[medico].numDeceduti += 1;
|
|
1074
|
-
lista.perDistretto[codToCfDistrettoMap[medico].distretto].medici[medico].quote += numQuote;
|
|
1079
|
+
deceduto.numQuoteDaRecuperare = 0;
|
|
1080
|
+
deceduto.note = "Data di decesso non valida";
|
|
1081
|
+
lista.perDistretto[codToCfDistrettoMap[medico].distretto].problemi.push(deceduto);
|
|
1075
1082
|
}
|
|
1076
|
-
} else {
|
|
1077
|
-
deceduto.numQuoteDaRecuperare = 0;
|
|
1078
|
-
deceduto.note = "Data di decesso non valida";
|
|
1079
|
-
lista.perDistretto[codToCfDistrettoMap[medico].distretto].problemi.push(deceduto);
|
|
1080
1083
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
lista.perDistretto[codToCfDistrettoMap[medico].distretto].anagrafica.push(...Object.values(Object.values(fileData.vivi)));
|
|
1084
|
+
// add ambito and distretto to ...Object.values(Object.values(fileData.vivi)
|
|
1085
|
+
lista.perDistretto[codToCfDistrettoMap[medico].distretto].anagrafica.push(...Object.values(Object.values(fileData.vivi)));
|
|
1084
1086
|
|
|
1087
|
+
}
|
|
1085
1088
|
}
|
|
1089
|
+
let out = {};
|
|
1090
|
+
for (let distretto in lista.perDistretto) {
|
|
1091
|
+
if (!out.hasOwnProperty(distretto))
|
|
1092
|
+
out[distretto] = {recuperi: [], problemi: []};
|
|
1093
|
+
out[distretto].recuperi.push(...lista.perDistretto[distretto].recuperi);
|
|
1094
|
+
out[distretto].problemi.push(...lista.perDistretto[distretto].problemi);
|
|
1095
|
+
}
|
|
1096
|
+
let global = {recuperi: [], problemi: []};
|
|
1097
|
+
for (let distretto in out) {
|
|
1098
|
+
// add column distretto in each recuperi and problemi
|
|
1099
|
+
for (let recupero of out[distretto].recuperi)
|
|
1100
|
+
global.recuperi.push(recupero);
|
|
1101
|
+
for (let problema of out[distretto].problemi)
|
|
1102
|
+
global.problemi.push(problema);
|
|
1103
|
+
}
|
|
1104
|
+
// create folder if exist pathData + path.sep + "recuperi" + path.sep
|
|
1105
|
+
if (!fs.existsSync(pathData + path.sep + "recuperi" + path.sep))
|
|
1106
|
+
fs.mkdirSync(pathData + path.sep + "recuperi" + path.sep);
|
|
1107
|
+
if (!fs.existsSync(pathData + path.sep + "recuperi" + path.sep + "per distretto" + path.sep))
|
|
1108
|
+
fs.mkdirSync(pathData + path.sep + "recuperi" + path.sep + "per distretto" + path.sep);
|
|
1109
|
+
if (!fs.existsSync(pathData + path.sep + "anagrafica"))
|
|
1110
|
+
fs.mkdirSync(pathData + path.sep + "anagrafica");
|
|
1111
|
+
let allAnagraficaTuttiDistretti = [];
|
|
1112
|
+
for (let distretto in out) {
|
|
1113
|
+
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "recuperi" + path.sep + "per distretto" + path.sep + distretto + "_recuperi.xlsx", out[distretto].recuperi);
|
|
1114
|
+
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "recuperi" + path.sep + "per distretto" + path.sep + distretto + "_problemi.xlsx", out[distretto].problemi);
|
|
1115
|
+
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "anagrafica" + path.sep + distretto + "_anagrafica.xlsx", lista.perDistretto[distretto].anagrafica);
|
|
1116
|
+
allAnagraficaTuttiDistretti.push(...lista.perDistretto[distretto].anagrafica);
|
|
1117
|
+
}
|
|
1118
|
+
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "recuperi" + path.sep + "global_recuperi.xlsx", global.recuperi);
|
|
1119
|
+
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "recuperi" + path.sep + "global_problemi.xlsx", global.problemi);
|
|
1086
1120
|
}
|
|
1087
|
-
let out = {};
|
|
1088
|
-
for (let distretto in lista.perDistretto) {
|
|
1089
|
-
if (!out.hasOwnProperty(distretto))
|
|
1090
|
-
out[distretto] = {recuperi: [], problemi: []};
|
|
1091
|
-
out[distretto].recuperi.push(...lista.perDistretto[distretto].recuperi);
|
|
1092
|
-
out[distretto].problemi.push(...lista.perDistretto[distretto].problemi);
|
|
1093
|
-
}
|
|
1094
|
-
let global = {recuperi: [], problemi: []};
|
|
1095
|
-
for (let distretto in out) {
|
|
1096
|
-
// add column distretto in each recuperi and problemi
|
|
1097
|
-
for (let recupero of out[distretto].recuperi)
|
|
1098
|
-
global.recuperi.push(recupero);
|
|
1099
|
-
for (let problema of out[distretto].problemi)
|
|
1100
|
-
global.problemi.push(problema);
|
|
1101
|
-
}
|
|
1102
|
-
// create folder if exist pathData + path.sep + "recuperi" + path.sep
|
|
1103
|
-
if (!fs.existsSync(pathData + path.sep + "recuperi" + path.sep))
|
|
1104
|
-
fs.mkdirSync(pathData + path.sep + "recuperi" + path.sep);
|
|
1105
|
-
if (!fs.existsSync(pathData + path.sep + "recuperi" + path.sep + "per distretto" + path.sep))
|
|
1106
|
-
fs.mkdirSync(pathData + path.sep + "recuperi" + path.sep + "per distretto" + path.sep);
|
|
1107
|
-
if (!fs.existsSync(pathData + path.sep + "anagrafica"))
|
|
1108
|
-
fs.mkdirSync(pathData + path.sep + "anagrafica");
|
|
1109
|
-
let allAnagraficaTuttiDistretti = [];
|
|
1110
|
-
for (let distretto in out) {
|
|
1111
|
-
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "recuperi" + path.sep + "per distretto" + path.sep + distretto + "_recuperi.xlsx", out[distretto].recuperi);
|
|
1112
|
-
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "recuperi" + path.sep + "per distretto" + path.sep + distretto + "_problemi.xlsx", out[distretto].problemi);
|
|
1113
|
-
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "anagrafica" + path.sep + distretto + "_anagrafica.xlsx", lista.perDistretto[distretto].anagrafica);
|
|
1114
|
-
allAnagraficaTuttiDistretti.push(...lista.perDistretto[distretto].anagrafica);
|
|
1115
|
-
}
|
|
1116
|
-
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "recuperi" + path.sep + "global_recuperi.xlsx", global.recuperi);
|
|
1117
|
-
await utils.scriviOggettoSuNuovoFileExcel(pathData + path.sep + "recuperi" + path.sep + "global_problemi.xlsx", global.problemi);
|
|
1118
1121
|
}
|
|
1119
1122
|
}
|
|
@@ -343,28 +343,35 @@ export class Nar2 {
|
|
|
343
343
|
let datiIdAssistito;
|
|
344
344
|
const retry = 3;
|
|
345
345
|
for (let i = 0; i < retry; i++) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
346
|
+
try {
|
|
347
|
+
if (!fallback) {
|
|
348
|
+
datiIdAssistito = await this.getAssistitiFromParams({codiceFiscale: codiceFiscale});
|
|
349
|
+
if (datiIdAssistito.ok && datiIdAssistito.data && datiIdAssistito.data.length === 1)
|
|
350
|
+
datiAssistito = await this.getAssistitoFromId(datiIdAssistito.data[0].pz_id);
|
|
351
|
+
} else {
|
|
352
|
+
await this.getToken({fallback});
|
|
353
|
+
const data = {cf: codiceFiscale, token: Nar2.#token, type: "nar2"};
|
|
354
|
+
const cripted = CryptHelper.AESEncrypt(JSON.stringify(data), this._cryptData["KEY"], CryptHelper.convertBase64StringToByte(this._cryptData["IV"]));
|
|
355
|
+
datiIdAssistito = await axios.request({
|
|
356
|
+
method: "post",
|
|
357
|
+
url: Nar2.GET_WS_FALLBACK_INTERNAL,
|
|
358
|
+
data: {d: cripted},
|
|
359
|
+
headers: {
|
|
360
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
if (datiIdAssistito.status === 200 && datiIdAssistito.data?.data?.hasOwnProperty('nar2'))
|
|
364
|
+
datiAssistito = {ok: true, data: datiIdAssistito.data.data.nar2.result};
|
|
365
|
+
else {
|
|
366
|
+
datiAssistito = {ok: false, data: datiIdAssistito.data};
|
|
367
|
+
console.log(`[getDatiAssistitoNar2FromCf] Risposta fallback non valida - status: ${datiIdAssistito.status}, hasData: ${!!datiIdAssistito.data?.data}, hasNar2: ${!!datiIdAssistito.data?.data?.nar2}`);
|
|
360
368
|
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
else datiAssistito = {ok: false, data: datiIdAssistito.data};
|
|
369
|
+
}
|
|
370
|
+
} catch (e) {
|
|
371
|
+
console.log("[getDatiAssistitoNar2FromCf] Eccezione durante recupero Nar2:", e.message);
|
|
365
372
|
}
|
|
366
373
|
if (datiAssistito && datiAssistito.ok) break;
|
|
367
|
-
else console.log("Errore
|
|
374
|
+
else console.log("[getDatiAssistitoNar2FromCf] Errore Nar2, tentativi rimanenti:" + (retry - i));
|
|
368
375
|
}
|
|
369
376
|
if (datiAssistito && datiAssistito.ok) {
|
|
370
377
|
try {
|
|
@@ -416,6 +423,7 @@ export class Nar2 {
|
|
|
416
423
|
fullData: datiAssistito
|
|
417
424
|
};
|
|
418
425
|
} else {
|
|
426
|
+
console.log(`[getDatiAssistitoNar2FromCf] Nar2 fallito dopo ${retry} tentativi per CF: ${codiceFiscale?.substring(0, 6)}***`);
|
|
419
427
|
assistito.erroreNar2 = "Nessun assistito trovato con il codice fiscale fornito";
|
|
420
428
|
assistito.okNar2 = false;
|
|
421
429
|
return {ok: false, data: null, fullData: datiIdAssistito};
|
|
@@ -535,13 +543,19 @@ export class Nar2 {
|
|
|
535
543
|
|
|
536
544
|
await this.getToken({fallback});
|
|
537
545
|
if (sogei && nar2) {
|
|
538
|
-
await Promise.
|
|
546
|
+
const results = await Promise.allSettled([
|
|
539
547
|
this.getDatiAssistitoFromCfSuSogeiNew(cf, assistito, fallback),
|
|
540
548
|
this.getDatiAssistitoNar2FromCf(cf, assistito, fallback)
|
|
541
549
|
]);
|
|
550
|
+
results.forEach((result, index) => {
|
|
551
|
+
if (result.status === 'rejected') {
|
|
552
|
+
const source = index === 0 ? 'Sogei' : 'Nar2';
|
|
553
|
+
console.log(`[getDatiAssistitoCompleti] ${source} fallito:`, result.reason?.message || result.reason);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
542
556
|
} else {
|
|
543
|
-
if (sogei) await this.getDatiAssistitoFromCfSuSogeiNew(cf, assistito, fallback);
|
|
544
|
-
if (nar2) await this.getDatiAssistitoNar2FromCf(cf, assistito, fallback);
|
|
557
|
+
if (sogei) await this.getDatiAssistitoFromCfSuSogeiNew(cf, assistito, fallback).catch(e => console.log('[getDatiAssistitoCompleti] Sogei fallito:', e.message));
|
|
558
|
+
if (nar2) await this.getDatiAssistitoNar2FromCf(cf, assistito, fallback).catch(e => console.log('[getDatiAssistitoCompleti] Nar2 fallito:', e.message));
|
|
545
559
|
}
|
|
546
560
|
|
|
547
561
|
return assistito;
|
|
@@ -589,7 +603,7 @@ export class Nar2 {
|
|
|
589
603
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
590
604
|
}
|
|
591
605
|
})
|
|
592
|
-
out.data = {status: out.data
|
|
606
|
+
out.data = {status: out.data?.data?.sogei?.status || false, data: out.data?.data?.sogei?.data};
|
|
593
607
|
}
|
|
594
608
|
|
|
595
609
|
if (!out.data || out.data === undefined || (fallback && out.data.status.toString().toLowerCase().includes("token is invalid")))
|
|
@@ -643,16 +657,20 @@ export class Nar2 {
|
|
|
643
657
|
};
|
|
644
658
|
}
|
|
645
659
|
} catch (e) {
|
|
660
|
+
console.log(`[getDatiAssistitoFromCfSuSogeiNew] Errore tentativo Sogei:`, e.message);
|
|
646
661
|
await this.getToken({fallback});
|
|
647
662
|
}
|
|
648
663
|
}
|
|
649
|
-
if (!ok)
|
|
664
|
+
if (!ok) {
|
|
665
|
+
console.log(`[getDatiAssistitoFromCfSuSogeiNew] Sogei fallito dopo ${this._maxRetry} tentativi per CF: ${cf?.substring(0, 6)}***`);
|
|
650
666
|
return {
|
|
651
667
|
ok: false,
|
|
652
668
|
fullData: null,
|
|
653
669
|
data: assistito
|
|
654
670
|
};
|
|
671
|
+
}
|
|
655
672
|
}
|
|
656
673
|
|
|
657
674
|
|
|
675
|
+
|
|
658
676
|
}
|
package/src/siad/FlussoSIAD.js
CHANGED
|
@@ -731,7 +731,7 @@ export class FlussoSIAD {
|
|
|
731
731
|
* @param {string} [nomeFileTs="datiTS.mpdb"] (Opzionale) Il percorso del file dati TS da utilizzare per il confronto.
|
|
732
732
|
* @return {Promise<void>} Una promessa che si risolve una volta completato il calcolo delle statistiche e la scrittura del file di output.
|
|
733
733
|
*/
|
|
734
|
-
async statisticheFLS21(pathData, nomeFileTs = "
|
|
734
|
+
async statisticheFLS21(pathData, nomeFileTs = "siad.mpdb") {
|
|
735
735
|
let data = {
|
|
736
736
|
allChiaviCasiTrattati: {},
|
|
737
737
|
statsT1: {totali: 0, anziani: 0, palliativa: 0},
|
|
@@ -744,8 +744,8 @@ export class FlussoSIAD {
|
|
|
744
744
|
|
|
745
745
|
let fileDatiTs = fs.existsSync(pathData + path.sep + nomeFileTs) ? await utils.leggiOggettoMP(pathData + path.sep + nomeFileTs) : null;
|
|
746
746
|
|
|
747
|
-
let filesT1 = utils.getAllFilesRecursive(pathData, ".xml", "
|
|
748
|
-
let filesT2 = utils.getAllFilesRecursive(pathData, ".xml", "
|
|
747
|
+
let filesT1 = utils.getAllFilesRecursive(pathData, ".xml", "AA2");
|
|
748
|
+
let filesT2 = utils.getAllFilesRecursive(pathData, ".xml", "AP2");
|
|
749
749
|
|
|
750
750
|
filesT1.forEach(file => {
|
|
751
751
|
let xml_string = fs.readFileSync(file, "utf8");
|
|
@@ -757,9 +757,9 @@ export class FlussoSIAD {
|
|
|
757
757
|
const chiavePic = assistenze[i]['Eventi'][0]['PresaInCarico'][0]['Id_Rec'][0];
|
|
758
758
|
console.log(chiavePic)
|
|
759
759
|
const cf = assistenze[i]['Assistito'][0]['DatiAnagrafici'][0]['CUNI'][0];
|
|
760
|
-
const presenteSuTs = fileDatiTs.out.vivi.hasOwnProperty(cf) || fileDatiTs.out.morti.hasOwnProperty(cf);
|
|
761
|
-
const vivo = presenteSuTs ? fileDatiTs.out.vivi.hasOwnProperty(cf) : null;
|
|
762
|
-
const datiTs = presenteSuTs ? (vivo ? fileDatiTs.out.vivi[cf] : fileDatiTs.out.morti[cf]) : null;
|
|
760
|
+
const presenteSuTs = fileDatiTs.fromTS.out.vivi.hasOwnProperty(cf) || fileDatiTs.fromTS.out.morti.hasOwnProperty(cf);
|
|
761
|
+
const vivo = presenteSuTs ? fileDatiTs.fromTS.out.vivi.hasOwnProperty(cf) : null;
|
|
762
|
+
const datiTs = presenteSuTs ? (vivo ? fileDatiTs.fromTS.out.vivi[cf] : fileDatiTs.fromTS.out.morti[cf]) : null;
|
|
763
763
|
const eta = datiTs ? datiTs.eta : utils.getAgeFromCF(cf);
|
|
764
764
|
const anziano = eta >= 65;
|
|
765
765
|
const palliativa = assistenze[i]['Eventi'][0]['PresaInCarico'][0]['ATTR']['TipologiaPIC'] === "2";
|
|
@@ -954,21 +954,24 @@ export class FlussoSIAD {
|
|
|
954
954
|
}
|
|
955
955
|
}
|
|
956
956
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
957
|
+
const result = {
|
|
958
|
+
totalePreseInCarico: chiavi.length,
|
|
959
|
+
totalePreseInCaricoAlmenoUnAccesso: totalePreseIncaricoAlmenoUnAccesso,
|
|
960
|
+
totaliGeriatrica: (totalePreseIncaricoAlmenoUnAccesso - totalePalliativa),
|
|
961
|
+
totaliPalliativa: totalePalliativa,
|
|
962
|
+
totaliAccessi: (totaleAccessiPalliativa + totaleAccessiGeriatrica),
|
|
963
|
+
totaliAccessiGeriatrica: totaleAccessiGeriatrica,
|
|
964
|
+
totaliAccessiPalliativa: totaleAccessiPalliativa,
|
|
965
|
+
totalePreseInCaricoOver65: chiavi65.length,
|
|
966
|
+
totalePreseInCaricoAlmenoUnAccessoOver65: totalePreseIncaricoAlmenoUnAccesso65,
|
|
967
|
+
totaliGeriatricaOver65: (totalePreseIncaricoAlmenoUnAccesso65 - totalePalliativa65),
|
|
968
|
+
totaliPalliativaOver65: totalePalliativa65,
|
|
969
|
+
totaliAccessiOver65: (totaleAccessiPalliativa65 + totaleAccessiGeriatrica65),
|
|
970
|
+
totaliAccessiGeriatricaOver65: totaleAccessiGeriatrica65,
|
|
971
|
+
totaliAccessiPalliativaOver65: totaleAccessiPalliativa65
|
|
972
|
+
};
|
|
973
|
+
console.log(JSON.stringify(result, null, 2));
|
|
974
|
+
return result;
|
|
972
975
|
}
|
|
973
976
|
|
|
974
977
|
statisticheChiaviValide(pathFile) {
|
|
@@ -1107,6 +1110,7 @@ export class FlussoSIAD {
|
|
|
1107
1110
|
* @param {boolean} [config.chiudiPicAnnoPrecedente=true] - Flag indicating whether to close the previous year PIC process.
|
|
1108
1111
|
* @param {array} [config.trimestriDaConsiderare=[1, 2, 3, 4]] - Optional array of quarters to consider for processing.
|
|
1109
1112
|
* @param {string|null} [config.nonConsiderareSeSuccessivoA=null] - Optional date to skip processing if the data is newer than this date.
|
|
1113
|
+
* @param {boolean} [config.creaFlussoPulito=false] - Flag indicating whether to create a clean flow without errors.
|
|
1110
1114
|
*
|
|
1111
1115
|
* @return {Promise<object>} A promise that resolves to an object containing the output data, structured logs, and mapping results or any errors encountered during processing.
|
|
1112
1116
|
*/
|
|
@@ -1119,7 +1123,8 @@ export class FlussoSIAD {
|
|
|
1119
1123
|
dbFile = "siad.mpdb",
|
|
1120
1124
|
chiudiPicAnnoPrecedente = true,
|
|
1121
1125
|
trimestriDaConsiderare = [1, 2, 3, 4],
|
|
1122
|
-
nonConsiderareSeSuccessivoA = null
|
|
1126
|
+
nonConsiderareSeSuccessivoA = null,
|
|
1127
|
+
creaFlussoPulito = false
|
|
1123
1128
|
} = config;
|
|
1124
1129
|
let out = {
|
|
1125
1130
|
errors: {
|
|
@@ -1234,11 +1239,16 @@ export class FlussoSIAD {
|
|
|
1234
1239
|
}
|
|
1235
1240
|
// data.datiTracciatiDitte.T1byCf
|
|
1236
1241
|
for (let t1row of data.datiTracciatiDitte.T1) {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
data.datiTracciatiDitte.T1byCf
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
+
try {
|
|
1243
|
+
const cf = t1row[tracciato1Maggioli[1]].trim();
|
|
1244
|
+
if (!data.datiTracciatiDitte.T1byCf.hasOwnProperty(cf))
|
|
1245
|
+
data.datiTracciatiDitte.T1byCf[cf] = [t1row]
|
|
1246
|
+
else
|
|
1247
|
+
data.datiTracciatiDitte.T1byCf[cf].push(t1row);
|
|
1248
|
+
} catch (e) {
|
|
1249
|
+
console.log("Errore su riga T1: " + JSON.stringify(t1row));
|
|
1250
|
+
process.exit(1);
|
|
1251
|
+
}
|
|
1242
1252
|
}
|
|
1243
1253
|
|
|
1244
1254
|
for (let file of allFilesT2) {
|
|
@@ -1327,12 +1337,14 @@ export class FlussoSIAD {
|
|
|
1327
1337
|
const ultimaPicAttivitaPerAssistito = {};
|
|
1328
1338
|
let mappingIdPicAperte = {};
|
|
1329
1339
|
let cfNonValidiTs = {};
|
|
1330
|
-
let dataDaNonConsiderare =
|
|
1340
|
+
let dataDaNonConsiderare = nonConsiderareSeSuccessivoA ? moment(nonConsiderareSeSuccessivoA, "DD/MM/YYYY") : null;
|
|
1331
1341
|
// remove the first 200.000 record of t2bykeyOrdered
|
|
1332
1342
|
//t2bykeyOrdered = t2bykeyOrdered.slice(200000);
|
|
1333
1343
|
// PER CREARE IL FLUSSO PULITO
|
|
1334
|
-
|
|
1335
|
-
|
|
1344
|
+
if (creaFlussoPulito) {
|
|
1345
|
+
data.mappaDatiMinistero.perCf = {};
|
|
1346
|
+
data.mappaDatiMinistero.allCfTrattati.perCf = {};
|
|
1347
|
+
}
|
|
1336
1348
|
|
|
1337
1349
|
for (let key of t2bykeyOrdered) {
|
|
1338
1350
|
|
|
@@ -1437,7 +1449,7 @@ export class FlussoSIAD {
|
|
|
1437
1449
|
}
|
|
1438
1450
|
}
|
|
1439
1451
|
//in caso di decisione di chiudere pic aperte negli anni precedenti
|
|
1440
|
-
if (datiPicAperteMinistero && datiPicAperteMinistero.corrente &&
|
|
1452
|
+
if (datiPicAperteMinistero && datiPicAperteMinistero.corrente && chiudiPicAnnoPrecedente &&
|
|
1441
1453
|
(moment(datiPicAperteMinistero.corrente.substring(6, 16), "YYYY-MM-DD").year() !== anno)) {
|
|
1442
1454
|
datiPicAperteMinistero.precedenti.push(datiPicAperteMinistero.corrente);
|
|
1443
1455
|
delete data.mappaDatiMinistero.perCf[cf].aperte[datiPicAperteMinistero.corrente];
|
|
@@ -2725,6 +2737,239 @@ export class FlussoSIAD {
|
|
|
2725
2737
|
}
|
|
2726
2738
|
|
|
2727
2739
|
|
|
2740
|
+
/**
|
|
2741
|
+
* Sviluppa i dati delle vaccinazioni e genera tracciati di output a partire dai file di input.
|
|
2742
|
+
*
|
|
2743
|
+
* Legge file Excel/JSON in `pathCartellaIn`, normalizza codici fiscali (gestione sostituti),
|
|
2744
|
+
* filtra soggetti deceduti e crea i tracciati 1 e 2 per il flusso vaccinazioni.
|
|
2745
|
+
*
|
|
2746
|
+
* @param {string} pathCartellaIn - percorso della cartella con i file di input.
|
|
2747
|
+
* @param {string|null} pathChiaviValideAttive - percorso file chiavi valide attive (opzionale).
|
|
2748
|
+
* @param {number|string} anno - anno di riferimento per i tracciati.
|
|
2749
|
+
* @param {number|string} numTrimestre - numero del trimestre.
|
|
2750
|
+
* @param {Object} [config={}] - opzioni:
|
|
2751
|
+
* - `nomeFileVaccini` {string} nome file vaccini (default: "vaccini.xlsx")
|
|
2752
|
+
* - `nomeFileVivi` {string} nome file vivi (default: "vivi.xlsx")
|
|
2753
|
+
* - `nomeFileMorti` {string} nome file morti (default: "morti.xlsx")
|
|
2754
|
+
* - `nomeFileSostituti` {string} nome file sostituti (default: "sostituti.xlsx")
|
|
2755
|
+
* - `nomeColonnaCf` {string} nome colonna CF (default: "cf")
|
|
2756
|
+
* - `nomeColonnaData` {string} nome colonna data vaccinazione (default: "data")
|
|
2757
|
+
*
|
|
2758
|
+
* @returns {Promise<void>} Risolve quando i file di output sono stati scritti.
|
|
2759
|
+
* @throws {Error} Se mancano colonne richieste o ci sono errori nella lettura dei file.
|
|
2760
|
+
*/
|
|
2761
|
+
async sviluppaDatiVaccinazioni(pathCartellaIn, pathChiaviValideAttive, anno, numTrimestre, config = {}) {
|
|
2762
|
+
let {
|
|
2763
|
+
nomeFileTracciatoADP = "datiVaccinazioni.xlsx",
|
|
2764
|
+
nomeFileMorti = "morti.xlsx",
|
|
2765
|
+
nomeFileVivi = "vivi.xlsx",
|
|
2766
|
+
nomeFileSostituti = "sostituti.xlsx",
|
|
2767
|
+
nomeColonnaCf = "cf",
|
|
2768
|
+
nomecolonnaCfSostituto = "cfOk",
|
|
2769
|
+
colonnaIdRecordChiaviValide = "Id Record",
|
|
2770
|
+
colonnaDataPresaInCaricoChiaviValide = "Data Presa In Carico",
|
|
2771
|
+
colonnaConclusioneChiaviValide = "Data Conclusione",
|
|
2772
|
+
|
|
2773
|
+
} = config;
|
|
2774
|
+
|
|
2775
|
+
// put int dataInizio the first day of the correct trimester
|
|
2776
|
+
let dataInizio = moment("01/01/" + anno, "DD/MM/YYYY").add(numTrimestre * 3 - 3, 'months');
|
|
2777
|
+
let dataFine = moment(dataInizio).add(3, 'months').subtract(1, 'days');
|
|
2778
|
+
let allChiaviValideAperte = {};
|
|
2779
|
+
if (fs.existsSync(pathChiaviValideAttive)) {
|
|
2780
|
+
let chiaviValide = await utils.getObjectFromFileExcel(pathChiaviValideAttive);
|
|
2781
|
+
for (let chiave of chiaviValide)
|
|
2782
|
+
if (typeof chiave[colonnaConclusioneChiaviValide] == "string" && chiave[colonnaConclusioneChiaviValide].includes("--"))
|
|
2783
|
+
allChiaviValideAperte[chiave[colonnaIdRecordChiaviValide]] = chiave;
|
|
2784
|
+
}
|
|
2785
|
+
let tracciato = await utils.getObjectFromFileExcel(pathCartellaIn + path.sep + nomeFileTracciatoADP, 0, false);
|
|
2786
|
+
|
|
2787
|
+
let allFileVivi = utils.getAllFilesRecursive(pathCartellaIn, ".xlsx", nomeFileVivi);
|
|
2788
|
+
let allFileMorti = utils.getAllFilesRecursive(pathCartellaIn, ".xlsx", nomeFileMorti);
|
|
2789
|
+
let allFileSostituti = utils.getAllFilesRecursive(pathCartellaIn, ".xlsx", nomeFileSostituti);
|
|
2790
|
+
let allVivi = {};
|
|
2791
|
+
let allMorti = {};
|
|
2792
|
+
let allSostituti = {};
|
|
2793
|
+
for (let file of allFileVivi) {
|
|
2794
|
+
let allViviTemp = await utils.getObjectFromFileExcel(file);
|
|
2795
|
+
for (let vivo of allViviTemp) {
|
|
2796
|
+
if (!vivo.hasOwnProperty(nomeColonnaCf))
|
|
2797
|
+
throw new Error("Errore in file " + file + " colonna " + nomeColonnaCf + " non presente");
|
|
2798
|
+
else
|
|
2799
|
+
allVivi[vivo[nomeColonnaCf]] = vivo;
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
for (let file of allFileMorti) {
|
|
2803
|
+
let allMortiTemp = await utils.getObjectFromFileExcel(file);
|
|
2804
|
+
for (let morto of allMortiTemp) {
|
|
2805
|
+
if (!morto.hasOwnProperty(nomeColonnaCf))
|
|
2806
|
+
throw new Error("Errore in file " + file + " colonna " + nomeColonnaCf + " non presente");
|
|
2807
|
+
else
|
|
2808
|
+
allMorti[morto[nomeColonnaCf]] = morto;
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
for (let file of allFileSostituti) {
|
|
2812
|
+
let allSostitutiTemp = await utils.getObjectFromFileExcel(file);
|
|
2813
|
+
for (let sostituto of allSostitutiTemp) {
|
|
2814
|
+
if (!sostituto.hasOwnProperty(nomecolonnaCfSostituto.trim().replaceAll(" ", "")) || !sostituto.hasOwnProperty(nomeColonnaCf))
|
|
2815
|
+
// error and break
|
|
2816
|
+
throw new Error("Errore in file " + file + " colonna " + nomecolonnaCfSostituto + " o " + nomeColonnaCf + " non presenti");
|
|
2817
|
+
allSostituti[sostituto[nomeColonnaCf].trim().replaceAll(" ", "")] = sostituto[nomecolonnaCfSostituto].trim().replaceAll(" ", "");
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
let outTracciato1 = [];
|
|
2821
|
+
let rigaHeaderTracciato1 = {}
|
|
2822
|
+
for (let i = 0; i < Object.keys(tracciato1Maggioli).length; i++)
|
|
2823
|
+
rigaHeaderTracciato1[i] = tracciato1Maggioli[i];
|
|
2824
|
+
outTracciato1.push(rigaHeaderTracciato1);
|
|
2825
|
+
let allCf = {};
|
|
2826
|
+
|
|
2827
|
+
for (let riga of tracciato) {
|
|
2828
|
+
if (riga[1] !== "") {
|
|
2829
|
+
let chiaviValideAperte = Object.keys(allChiaviValideAperte).filter(key => key.includes(riga[0]));
|
|
2830
|
+
|
|
2831
|
+
if ((allSostituti.hasOwnProperty(riga[0].trim().replaceAll(" ", "")) && allSostituti[riga[0].trim().replaceAll(" ", "")].trim().replaceAll(" ", "") !== "") || Object.keys(allSostituti) === 0 || !allSostituti.hasOwnProperty(riga[0].trim().replaceAll(" ", ""))) {
|
|
2832
|
+
let codFiscale = allSostituti.hasOwnProperty(riga[0].trim().replaceAll(" ", "")) ? allSostituti[riga[0].trim().replaceAll(" ", "")] : riga[0].trim().replaceAll(" ", "");
|
|
2833
|
+
let dataDecesso = allMorti.hasOwnProperty(codFiscale) ? moment(allMorti[codFiscale]['dataDecesso'], "DD/MM/YYYY") : null;
|
|
2834
|
+
let dataNascita = allVivi.hasOwnProperty(codFiscale) ? moment(allVivi[codFiscale]['dataNascita'], "DD/MM/YYYY") : (allMorti.hasOwnProperty(codFiscale) ? moment(allMorti[codFiscale]['dataNascita'], "DD/MM/YYYY") : null);
|
|
2835
|
+
let annoNascita = (dataNascita && dataNascita.isValid()) ? dataNascita.year() : Parser.cfToBirthYear(codFiscale);
|
|
2836
|
+
if (dataDecesso !== null)
|
|
2837
|
+
console.log(dataDecesso.format("DD/MM/YYYY"))
|
|
2838
|
+
|
|
2839
|
+
if (!allCf.hasOwnProperty(codFiscale) && (dataDecesso == null || dataDecesso.isSameOrAfter(dataInizio))) {
|
|
2840
|
+
allCf[codFiscale] = riga[1];
|
|
2841
|
+
let rigaT1 = {};
|
|
2842
|
+
rigaT1[0] = ""; // tipo
|
|
2843
|
+
rigaT1[1] = codFiscale;
|
|
2844
|
+
rigaT1[2] = ""; // validita ci
|
|
2845
|
+
rigaT1[3] = ""; // tipologia ci
|
|
2846
|
+
rigaT1[4] = annoNascita;
|
|
2847
|
+
rigaT1[5] = Parser.cfToGender(codFiscale) === "M" ? "1" : "2";
|
|
2848
|
+
rigaT1[6] = codFiscale.substring(11, 12) !== "Z" ? "IT" : "XX";
|
|
2849
|
+
rigaT1[7] = "9";
|
|
2850
|
+
rigaT1[8] = "190";
|
|
2851
|
+
rigaT1[9] = "205";
|
|
2852
|
+
rigaT1[10] = "083048";
|
|
2853
|
+
rigaT1[11] = "1";
|
|
2854
|
+
rigaT1[12] = "2";
|
|
2855
|
+
rigaT1[13] = "190";
|
|
2856
|
+
rigaT1[14] = "205";
|
|
2857
|
+
rigaT1[15] = dataInizio.format("DD/MM/YYYY");
|
|
2858
|
+
rigaT1[16] = ""; // id record
|
|
2859
|
+
rigaT1[17] = "2";
|
|
2860
|
+
rigaT1[18] = "1";
|
|
2861
|
+
rigaT1[19] = dataInizio.format("DD/MM/YYYY");
|
|
2862
|
+
rigaT1[20] = "1";
|
|
2863
|
+
rigaT1[21] = "1";
|
|
2864
|
+
rigaT1[22] = "3";
|
|
2865
|
+
rigaT1[23] = "9";
|
|
2866
|
+
rigaT1[24] = "2";
|
|
2867
|
+
rigaT1[25] = "9";
|
|
2868
|
+
rigaT1[26] = "2";
|
|
2869
|
+
rigaT1[27] = "2";
|
|
2870
|
+
rigaT1[28] = "2";
|
|
2871
|
+
rigaT1[29] = "2";
|
|
2872
|
+
rigaT1[30] = "2";
|
|
2873
|
+
rigaT1[31] = "2";
|
|
2874
|
+
rigaT1[32] = "2";
|
|
2875
|
+
rigaT1[33] = "2";
|
|
2876
|
+
rigaT1[34] = "2";
|
|
2877
|
+
rigaT1[35] = "2";
|
|
2878
|
+
rigaT1[36] = "2";
|
|
2879
|
+
rigaT1[37] = "2";
|
|
2880
|
+
rigaT1[38] = "2";
|
|
2881
|
+
rigaT1[39] = "2";
|
|
2882
|
+
rigaT1[40] = "2";
|
|
2883
|
+
rigaT1[41] = "2";
|
|
2884
|
+
rigaT1[42] = "2";
|
|
2885
|
+
rigaT1[43] = "2";
|
|
2886
|
+
rigaT1[44] = "2";
|
|
2887
|
+
rigaT1[45] = "2";
|
|
2888
|
+
rigaT1[46] = "2";
|
|
2889
|
+
rigaT1[47] = "3";
|
|
2890
|
+
rigaT1[48] = "3";
|
|
2891
|
+
rigaT1[49] = "3";
|
|
2892
|
+
rigaT1[50] = "3";
|
|
2893
|
+
rigaT1[51] = "3";
|
|
2894
|
+
rigaT1[52] = "3";
|
|
2895
|
+
rigaT1[53] = "3";
|
|
2896
|
+
let patologie = [
|
|
2897
|
+
"401", // ipertensione
|
|
2898
|
+
"413", // angina
|
|
2899
|
+
"427", // tachicardia
|
|
2900
|
+
"715", // artrosi
|
|
2901
|
+
"518", // insufficenza respiratoria
|
|
2902
|
+
"493", // asma
|
|
2903
|
+
"715", // osteortrite
|
|
2904
|
+
"707", // ulcera da decubito
|
|
2905
|
+
]
|
|
2906
|
+
// put random value of patologie
|
|
2907
|
+
rigaT1[54] = patologie[Math.floor(Math.random() * patologie.length)];
|
|
2908
|
+
rigaT1[55] = "";
|
|
2909
|
+
if (chiaviValideAperte.length > 0) {
|
|
2910
|
+
rigaT1[56] = chiaviValideAperte[0];
|
|
2911
|
+
rigaT1[57] = moment(allChiaviValideAperte[chiaviValideAperte[0]][colonnaDataPresaInCaricoChiaviValide]).format("DD/MM/YYYY");
|
|
2912
|
+
} else {
|
|
2913
|
+
rigaT1[56] = "";
|
|
2914
|
+
rigaT1[57] = "";
|
|
2915
|
+
}
|
|
2916
|
+
rigaT1[58] = allMorti.hasOwnProperty(codFiscale) ? allMorti[codFiscale]['dataDecesso'] : "";
|
|
2917
|
+
|
|
2918
|
+
outTracciato1.push(rigaT1);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
await utils.scriviOggettoSuNuovoFileExcel(pathCartellaIn + path.sep + "tracciato1_out.xlsx", outTracciato1, null, false);
|
|
2924
|
+
|
|
2925
|
+
|
|
2926
|
+
let outTracciato2 = [];
|
|
2927
|
+
let rigaHeaderTracciato2 = {}
|
|
2928
|
+
for (let i = 0; i < Object.keys(tracciato2Maggioli).length; i++)
|
|
2929
|
+
rigaHeaderTracciato2[i] = tracciato2Maggioli[i];
|
|
2930
|
+
outTracciato2.push(rigaHeaderTracciato2);
|
|
2931
|
+
|
|
2932
|
+
|
|
2933
|
+
let allCfKey = Object.keys(allCf);
|
|
2934
|
+
for (let codFiscale of allCfKey) {
|
|
2935
|
+
console.log(codFiscale)
|
|
2936
|
+
let dataFromT2 = allCf[codFiscale];
|
|
2937
|
+
let data = moment(dataFromT2, "DD/MM/YYYY");
|
|
2938
|
+
// verifica che data sia tra inizio e fine, in caso metti una data casuale in questo range
|
|
2939
|
+
if (!data.isBetween(dataInizio, dataFine)) {
|
|
2940
|
+
let diffDays = dataFine.diff(dataInizio, 'days');
|
|
2941
|
+
let randomDays = Math.floor(Math.random() * diffDays);
|
|
2942
|
+
data = dataInizio.clone().add(randomDays, 'days');
|
|
2943
|
+
}
|
|
2944
|
+
let dataDecesso = allMorti.hasOwnProperty(codFiscale) ? moment(allMorti[codFiscale]['dataDecesso'], "DD/MM/YYYY") : null;
|
|
2945
|
+
if (dataDecesso == null || dataDecesso.isAfter(data)) {
|
|
2946
|
+
let rigaT2 = {}
|
|
2947
|
+
rigaT2[0] = codFiscale;
|
|
2948
|
+
rigaT2[1] = ""; // tipo
|
|
2949
|
+
rigaT2[2] = "190";
|
|
2950
|
+
rigaT2[3] = "205";
|
|
2951
|
+
rigaT2[4] = dataInizio.format("DD/MM/YYYY");
|
|
2952
|
+
rigaT2[5] = "";
|
|
2953
|
+
rigaT2[6] = ""
|
|
2954
|
+
rigaT2[7] = ""
|
|
2955
|
+
rigaT2[8] = ""
|
|
2956
|
+
rigaT2[9] = "1"
|
|
2957
|
+
rigaT2[10] = data.format("DD/MM/YYYY")// data accesso
|
|
2958
|
+
rigaT2[11] = "1" // tipo operatore
|
|
2959
|
+
rigaT2[12] = "6"; // tipo prestazione
|
|
2960
|
+
rigaT2[13] = ""; // data sospensione
|
|
2961
|
+
rigaT2[14] = ""; //motivo sospensione
|
|
2962
|
+
rigaT2[15] = "";
|
|
2963
|
+
outTracciato2.push(rigaT2);
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
await utils.scriviOggettoSuNuovoFileExcel(pathCartellaIn + path.sep + "tracciato2_out.xlsx", outTracciato2, null, false);
|
|
2968
|
+
|
|
2969
|
+
|
|
2970
|
+
}
|
|
2971
|
+
|
|
2972
|
+
|
|
2728
2973
|
/**
|
|
2729
2974
|
* Processes and develops ADP data for a company by reading and analyzing various input Excel files
|
|
2730
2975
|
* containing data for active keys, living individuals, deceased individuals, and replacements.
|