Audit legacy formazione - securehub / 10.200.0.251¶
Data audit: 2026-09-02
Modalita: sola lettura via SSH su 10.200.0.251.
Host e stack¶
- Host raggiunto:
10.200.0.251 - Hostname remoto:
docker01-nxt-sense.it - Applicazione legacy individuata:
/home/securehub/gestione - Stack: Flask / Flask-AppBuilder legacy
- Database: MySQL 5.7 container
mysql57, schemagestione - Storage documenti: path assoluti sotto
/home/securehub/gestione/app/static/archive/...
Non sono state fatte modifiche, migrazioni, restart o scritture sul server legacy.
Numeri principali¶
Conteggi database legacy:
corsi: 654date_corsi: 910dipendente_corso: 4.695dipendenti: 3.476documenti: 4.983allegati: 4.775categorie_corsi: 59luoghi_corsi: 2aule: 71
Documenti formazione:
- documenti collegati a corso: 4.539
- documenti con nome/titolo attestato: 871
- documenti corso-discente con nome/titolo/categoria certificato: 3.713
- coppie corso-discente con almeno un documento certificato: 3.713
- iscrizioni senza documento certificato collegato: 979
- documenti con nome/titolo registro: 782
- allegati PDF: 1.074
- documenti con path assoluto
/home/securehub/...: 4.724 - documenti senza corso ma con dipendente: 0
Nota conteggio attestati: il valore 871 e un conteggio prudente basato solo sulla parola
attestat in titolo o nome file. Nel legacy molti documenti sono classificati e titolati
come Certificato ...; usando categoria/titolo/nome con certificat risultano 3.713
documenti certificato collegati a corso e dipendente. Le 4.695 righe di
dipendente_corso sono iscrizioni, non attestati prodotti.
Tabelle principali¶
corsi¶
Campi rilevanti:
idnomedescrizionecodiceCodice_corsocertificate_titlecertificate_subtitledurata_oreattivotipocategoria_idnumero_massimo_partecipantidata_certificatoonline_id
Note:
codiceeCodice_corsosono entrambi presenti e univoci.tipoe enum legacy:IN_PRESENZA,VIDEOCONFERENZA,E_LEARNING,ESTERNO,NON_GESTITO.- Non esiste
tenant_key. - Non esiste un campo esplicito
multi_societa. online_idesiste ma al momento risulta non popolato sui corsi controllati.
date_corsi¶
Campi rilevanti:
idcorso_iddataorario_inizio_mattinaorario_fine_mattinaorario_inizio_pomeriggioorario_fine_pomeriggio
Note:
- Gli orari sono
datetime, nontime. - Le fasce mattina/pomeriggio sono compatibili con il modello SafeOps attuale, ma vanno convertite a
time. - Query qualita: 0 date con coppie orarie incomplete.
- Query qualita: 2 date con anno anomalo (
206o2206).
dipendente_corso¶
Campi rilevanti:
idcorso_iddipendente_id
Note:
- Non e presente
esito. - Non sono presenti campi firma, presenza, completamento o scadenza attestato.
- Lo stato va ricostruito da documenti/attestati/registri e non da questa tabella.
- Query qualita: 8 coppie corso/dipendente duplicate.
documenti¶
Campi rilevanti:
idtitolopercorso_filenome_originaleuid_unicodata_creazionedocente_idcliente_idcategoria_iddipendente_idcorso_iddati_extraowner_idnote
Note:
- Registro e attestati sono entrambi documenti.
- Attestato tipico:
documenti.corso_id+documenti.dipendente_id. - Registro tipico:
documenti.corso_idvalorizzato edipendente_idnullo. categoria_id = 23corrisponde aRegistro Presenze.- Le categorie certificate sono categorie documentali dedicate.
allegati¶
Campi rilevanti:
idtitolouidpercorso_filefile_typeupload_datenome_originalethumbnail_pathdocumento_idtipofile_hash
Note:
file_hashe gia disponibile e utile per deduplica/migrazione.- Gli allegati puntano a
documenti.
categorie_corsi¶
Campi rilevanti:
idnomedescrizionecodicefolderparent_iddurata_orescadenza_annicategoria_documentale_idonline_id
Note:
- 29 categorie hanno
scadenza_anni > 0. categoria_documentale_ide il ponte utile verso categorie documentali/attestati.- Alcuni nomi hanno encoding sporco, da normalizzare in migrazione.
Route e view legacy¶
Menu formazione legacy individuati in app/views/__init__.py:
CorsoModelViewcomeCorsiDocenteModelViewcomeDocenti e DocumentiCategoriaCorsiViewcomeCategorie CorsiAulaModelViewcomeAuleLuogoCorsoModelViewcomeLuoghi CorsiDataCorsoModelViewcomeDate CorsiAddDipendenteCorsoViewcomeGestione Dipendenti nei CorsiCalendarioCorsiViewcomeCalendario CorsiPartecipanteModelViewcomeCorsistiMansioniModelViewcomeMansioni
View/file specifici:
app/views/corsi_documenti_view.pyapp/views/add_dipendenti_corsi.pyapp/models/corso.pyapp/models/datacorso.pyapp/models/sessione_corso.pyapp/models/categoria_corsi.pyapp/templates/calendario_corsi.htmlapp/templates/add_corsi.htmlapp/templates/edit_corsi.htmlapp/templates/add_allegato_corsi.html
Route rilevanti:
/corsi_orfani/associare_corso/documentocorsimodelview/get_figlia_corsi/<categoria_padre_id>DataCorsoModelViewDocumentoCorsoModelViewAllegatoCorsoModelViewupload_certifica_form/<corso_id>- export/import Excel corso
Storage documentale¶
Pattern attestati:
/home/securehub/gestione/app/static/archive/dipendenti/corsi/<dipendente_id>/<corso_id>/<file>.pdf
Esempio verificato fisicamente:
/home/securehub/gestione/app/static/archive/dipendenti/corsi/7195/647/attestato_1630096_MASSIMO_ANNUNZIATA.pdf
Nota: per la migrazione non usare solo il nome file. Il criterio robusto e:
documento con corso_id valorizzato, dipendente_id valorizzato e categoria/titolo/nome
riconducibile a certificato/attestato.
Pattern registri:
/home/securehub/gestione/app/static/archive/corsi/registro_presenze/<corso_id>/<yyyymmdd>/<fascia>/<file>.pdf
Esempi verificati fisicamente:
/home/securehub/gestione/app/static/archive/corsi//registro_presenze/665/20260727/mattina/665_20260727_mattina.pdf/home/securehub/gestione/app/static/archive/corsi//registro_presenze/665/20260727/pomeriggio/665_20260727_pomeriggio.pdf
Nota: alcuni path hanno doppio slash corsi//registro_presenze; il filesystem li risolve, ma in migrazione conviene normalizzare.
Qualita dati¶
Esiti query:
- corsi senza date: 1
- corsi senza iscritti: 7
- coppie iscrizione duplicate corso/dipendente: 8
- date con orari incompleti: 0
- date con anno anomalo: 2
- corsi con
online_id: 0
Distribuzione corsi:
IN_PRESENZA, attivi: 164IN_PRESENZA, non attivi: 490
Date per anno:
- 2026: 338
- 2025: 426
- 2024: 144
- anomalie: anni
206e2206
Mapping verso SafeOps attuale¶
Corso¶
Legacy corsi -> SafeOps corso:
nome->nomedescrizione->descrizione, ma in SafeOps puo restare non centrale in UICodice_corso-> codice corso umano/catalogocodice-> codice istanza legacy o mapping esternodurata_ore->durata_orenumero_massimo_partecipanti->numero_massimo_partecipantiattivo->attivotipo-> mapping enum SafeOpscategoria_id-> mapping categoria corsocertificate_title,certificate_subtitle,data_certificato-> campi certificato
Serve una tabella o file di mapping legacy:
legacy_system = securehublegacy_table = corsilegacy_idsafeops_table = corsosafeops_id- checksum opzionale dei dati sorgente
Date corso¶
Legacy date_corsi -> SafeOps data_corso:
corso_idlegacy va tradotto tramite mapping corso.data->dataorario_inizio_mattinadatetime -> solotimeorario_fine_mattinadatetime -> solotimeorario_inizio_pomeriggiodatetime -> solotimeorario_fine_pomeriggiodatetime -> solotime
Prima della migrazione correggere o escludere le 2 date con anno anomalo.
Corsisti/iscrizioni¶
Legacy dipendente_corso -> SafeOps dipendente_corso:
corso_idtradotto;dipendente_idtradotto;esitonon disponibile: default consigliatoiscrittoocompletatosolo se esistono attestati collegati;- scadenza attestato da calcolare da documento/categoria corso quando possibile.
Duplicati: gestire le 8 coppie duplicate prima dell'import effettivo.
Attestati¶
Legacy documenti con:
corso_idvalorizzato;dipendente_idvalorizzato;- titolo/nome o categoria documentale di certificato.
Mapping SafeOps:
- documento nel documentale;
- relazione corso;
- relazione dipendente;
- relazione cliente se ricostruibile da affiliazione dipendente-cliente;
- aggiornamento libretto sicurezza/storico formativo.
Registri¶
Legacy documenti con:
corso_idvalorizzato;dipendente_idnullo;categoria_id = 23oppure titolo/nome registro.
Mapping SafeOps:
- documento registro firmato/generato collegato al corso;
- usare
percorso_filecome sorgente binaria; - se il registro e diviso per giorno/fascia, mantenere ogni file come evidenza oppure generare un pacchetto corso con manifest.
Allegati¶
Legacy allegati -> SafeOps document_version o allegati documentali:
- mantenere
file_hash; - deduplicare se stesso hash/nome/path;
- collegare al documento padre migrato.
Rischi tecnici¶
- Nessun tenant nel legacy: bisogna importare esplicitamente nel tenant target, presumibilmente
tenant_besant. - Credenziali WooCommerce hardcoded nel vecchio codice: non migrare, sostituire con configurazione sicura.
- Encoding sporco su categorie/nome corsi: serve normalizzazione UTF-8.
dipendente_corsonon ha esito: completamento corso va inferito da documenti/attestati.- Orari legacy sono
datetime, SafeOps usatime: conversione obbligatoria. - Date anomale presenti: 2 record fuori range.
- Registri divisi per mattina/pomeriggio: SafeOps deve mantenerli come evidenze o consolidarli nel pacchetto archivio.
- Path assoluti legacy: durante import vanno copiati nello storage SafeOps, non referenziati direttamente.
- Doppio slash nei path registro: normalizzare path prima della copia.
online_idnon valorizzato: aggancio WooCommerce storico non ricostruibile da quel campo.
Proposta migrazione¶
Step consigliati:
- Export read-only DB legacy in CSV/JSON per sole tabelle formazione.
- Export manifest file documentali con path, size, hash se disponibile.
- Normalizzazione encoding categorie/corsi.
- Mapping categorie legacy -> categorie SafeOps.
- Mapping dipendenti legacy -> dipendenti SafeOps via codice fiscale.
- Import corsi in tenant target con tabella mapping legacy.
- Import date corso convertendo datetime -> time.
- Import iscrizioni deduplicando corso/dipendente.
- Import registri come documenti corso.
- Import attestati come documenti dipendente/corso e popolamento libretto sicurezza.
- Generazione percorso corso derivato per ogni corso importato.
- Generazione pacchetto archivio solo dopo verifica campione.
Test minimi prima import reale¶
- Conteggio corsi importati = 654 meno eventuali esclusi dichiarati.
- Conteggio date = 910 meno 2 anomalie o con anomalie corrette.
- Nessuna coppia duplicata in
dipendente_corso. - Campione attestato: file copiato, documento collegato a corso e dipendente.
- Campione registro mattina/pomeriggio: file copiati e visibili in archivio corso.
- Categoria corso con
scadenza_annivalorizzata aggiorna libretto sicurezza. - Utente tenant diverso non vede corsi/documenti importati.
- Nessun path SafeOps punta direttamente a
/home/securehub/....
Conclusione¶
La vecchia formazione e migrabile. La parte piu importante non e importare solo i corsi, ma conservare il legame:
- corso;
- date/orari;
- iscritti;
- registro;
- attestati;
- categoria con scadenza;
- documento fisico;
- hash/allegati;
- mapping legacy.
Questo consente di ricostruire il percorso corso e creare il pacchetto archivio in SafeOps senza perdere evidenze storiche.