Vai al contenuto

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, schema gestione
  • 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: 654
  • date_corsi: 910
  • dipendente_corso: 4.695
  • dipendenti: 3.476
  • documenti: 4.983
  • allegati: 4.775
  • categorie_corsi: 59
  • luoghi_corsi: 2
  • aule: 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:

  • id
  • nome
  • descrizione
  • codice
  • Codice_corso
  • certificate_title
  • certificate_subtitle
  • durata_ore
  • attivo
  • tipo
  • categoria_id
  • numero_massimo_partecipanti
  • data_certificato
  • online_id

Note:

  • codice e Codice_corso sono entrambi presenti e univoci.
  • tipo e enum legacy: IN_PRESENZA, VIDEOCONFERENZA, E_LEARNING, ESTERNO, NON_GESTITO.
  • Non esiste tenant_key.
  • Non esiste un campo esplicito multi_societa.
  • online_id esiste ma al momento risulta non popolato sui corsi controllati.

date_corsi

Campi rilevanti:

  • id
  • corso_id
  • data
  • orario_inizio_mattina
  • orario_fine_mattina
  • orario_inizio_pomeriggio
  • orario_fine_pomeriggio

Note:

  • Gli orari sono datetime, non time.
  • 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 (206 o 2206).

dipendente_corso

Campi rilevanti:

  • id
  • corso_id
  • dipendente_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:

  • id
  • titolo
  • percorso_file
  • nome_originale
  • uid_unico
  • data_creazione
  • docente_id
  • cliente_id
  • categoria_id
  • dipendente_id
  • corso_id
  • dati_extra
  • owner_id
  • note

Note:

  • Registro e attestati sono entrambi documenti.
  • Attestato tipico: documenti.corso_id + documenti.dipendente_id.
  • Registro tipico: documenti.corso_id valorizzato e dipendente_id nullo.
  • categoria_id = 23 corrisponde a Registro Presenze.
  • Le categorie certificate sono categorie documentali dedicate.

allegati

Campi rilevanti:

  • id
  • titolo
  • uid
  • percorso_file
  • file_type
  • upload_date
  • nome_originale
  • thumbnail_path
  • documento_id
  • tipo
  • file_hash

Note:

  • file_hash e gia disponibile e utile per deduplica/migrazione.
  • Gli allegati puntano a documenti.

categorie_corsi

Campi rilevanti:

  • id
  • nome
  • descrizione
  • codice
  • folder
  • parent_id
  • durata_ore
  • scadenza_anni
  • categoria_documentale_id
  • online_id

Note:

  • 29 categorie hanno scadenza_anni > 0.
  • categoria_documentale_id e 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:

  • CorsoModelView come Corsi
  • DocenteModelView come Docenti e Documenti
  • CategoriaCorsiView come Categorie Corsi
  • AulaModelView come Aule
  • LuogoCorsoModelView come Luoghi Corsi
  • DataCorsoModelView come Date Corsi
  • AddDipendenteCorsoView come Gestione Dipendenti nei Corsi
  • CalendarioCorsiView come Calendario Corsi
  • PartecipanteModelView come Corsisti
  • MansioniModelView come Mansioni

View/file specifici:

  • app/views/corsi_documenti_view.py
  • app/views/add_dipendenti_corsi.py
  • app/models/corso.py
  • app/models/datacorso.py
  • app/models/sessione_corso.py
  • app/models/categoria_corsi.py
  • app/templates/calendario_corsi.html
  • app/templates/add_corsi.html
  • app/templates/edit_corsi.html
  • app/templates/add_allegato_corsi.html

Route rilevanti:

  • /corsi_orfani
  • /associare_corso
  • /documentocorsimodelview/get_figlia_corsi/<categoria_padre_id>
  • DataCorsoModelView
  • DocumentoCorsoModelView
  • AllegatoCorsoModelView
  • upload_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: 164
  • IN_PRESENZA, non attivi: 490

Date per anno:

  • 2026: 338
  • 2025: 426
  • 2024: 144
  • anomalie: anni 206 e 2206

Mapping verso SafeOps attuale

Corso

Legacy corsi -> SafeOps corso:

  • nome -> nome
  • descrizione -> descrizione, ma in SafeOps puo restare non centrale in UI
  • Codice_corso -> codice corso umano/catalogo
  • codice -> codice istanza legacy o mapping esterno
  • durata_ore -> durata_ore
  • numero_massimo_partecipanti -> numero_massimo_partecipanti
  • attivo -> attivo
  • tipo -> mapping enum SafeOps
  • categoria_id -> mapping categoria corso
  • certificate_title, certificate_subtitle, data_certificato -> campi certificato

Serve una tabella o file di mapping legacy:

  • legacy_system = securehub
  • legacy_table = corsi
  • legacy_id
  • safeops_table = corso
  • safeops_id
  • checksum opzionale dei dati sorgente

Date corso

Legacy date_corsi -> SafeOps data_corso:

  • corso_id legacy va tradotto tramite mapping corso.
  • data -> data
  • orario_inizio_mattina datetime -> solo time
  • orario_fine_mattina datetime -> solo time
  • orario_inizio_pomeriggio datetime -> solo time
  • orario_fine_pomeriggio datetime -> solo time

Prima della migrazione correggere o escludere le 2 date con anno anomalo.

Corsisti/iscrizioni

Legacy dipendente_corso -> SafeOps dipendente_corso:

  • corso_id tradotto;
  • dipendente_id tradotto;
  • esito non disponibile: default consigliato iscritto o completato solo 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_id valorizzato;
  • dipendente_id valorizzato;
  • 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_id valorizzato;
  • dipendente_id nullo;
  • categoria_id = 23 oppure titolo/nome registro.

Mapping SafeOps:

  • documento registro firmato/generato collegato al corso;
  • usare percorso_file come 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_corso non ha esito: completamento corso va inferito da documenti/attestati.
  • Orari legacy sono datetime, SafeOps usa time: 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_id non valorizzato: aggancio WooCommerce storico non ricostruibile da quel campo.

Proposta migrazione

Step consigliati:

  1. Export read-only DB legacy in CSV/JSON per sole tabelle formazione.
  2. Export manifest file documentali con path, size, hash se disponibile.
  3. Normalizzazione encoding categorie/corsi.
  4. Mapping categorie legacy -> categorie SafeOps.
  5. Mapping dipendenti legacy -> dipendenti SafeOps via codice fiscale.
  6. Import corsi in tenant target con tabella mapping legacy.
  7. Import date corso convertendo datetime -> time.
  8. Import iscrizioni deduplicando corso/dipendente.
  9. Import registri come documenti corso.
  10. Import attestati come documenti dipendente/corso e popolamento libretto sicurezza.
  11. Generazione percorso corso derivato per ogni corso importato.
  12. 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_anni valorizzata 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.