Files
nis2-agile/application/cli/ingest_doc_management_kb.php
T
DevEnv nis2-agileandClaude Opus 4.8 7b8a604739 [DOCS+KB] Gestione documentale: help (Permessi documentali) + i18n matrice IT/EN + ingest KB AI (v1.24.3)
- help.js: sezione 'Permessi documentali (ciclo di vita SGSI)' nel help Impostazioni
- settings.html: matrice permessi bilingue IT/EN (azioni/ruoli/hint) + cache-buster help.js
- KB AI: docs/kb/GESTIONE_DOCUMENTALE_SGSI.md + application/cli/ingest_doc_management_kb.php (ingerito in nis2_kb scope SYSTEM, ricercabilita verificata)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 14:40:28 +02:00

96 lines
6.0 KiB
PHP

<?php
/**
* ingest_doc_management_kb.php — Ingest KB (RAG, scope SYSTEM) della guida "Gestione documentale SGSI".
* Testo INLINE (il container monta solo application/, non docs/). Idempotente (delete per source + re-upsert).
* Eseguire dal container: docker exec nis2-app php /var/www/nis2-agile/application/cli/ingest_doc_management_kb.php
* Opzioni: --dry-run
*/
if (PHP_SAPI !== 'cli') { http_response_code(403); exit("CLI only\n"); }
if (!defined('BASE_PATH')) define('BASE_PATH', dirname(dirname(__DIR__)));
if (!defined('APP_PATH')) define('APP_PATH', dirname(__DIR__));
require_once APP_PATH . '/config/env.php';
require_once APP_PATH . '/config/config.php';
require_once APP_PATH . '/config/database.php';
require_once APP_PATH . '/services/EmbedService.php';
require_once APP_PATH . '/services/VectorService.php';
$opts = getopt('', ['dry-run']); $dryRun = isset($opts['dry-run']);
function logln(string $m): void { echo '[' . date('Y-m-d H:i:s') . "] $m\n"; }
function uuid(): string { $b = random_bytes(16); $b[6]=chr((ord($b[6])&0x0f)|0x40); $b[8]=chr((ord($b[8])&0x3f)|0x80); return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($b),4)); }
function chunkText(string $t, int $size=2000, int $ov=200): array { $c=[]; $len=mb_strlen($t,'UTF-8'); $s=0; while($s<$len){ $take=min($size,$len-$s); $p=mb_substr($t,$s,$take,'UTF-8'); if(trim($p)!=='') $c[]=$p; if($s+$take>=$len) break; $s+=($size-$ov);} return $c; }
$SOURCE = 'Documentazione prodotto NIS2 Agile — Gestione documentale SGSI';
$TITLE = 'Gestione documentale SGSI (ciclo di vita, Word, editor, permessi)';
$TEXT = <<<'TXT'
DOCUMENTO: Gestione documentale del Modello SGSI in NIS2 Agile (ciclo di vita, export Word, editor, permessi).
USO: aiutare l'utente a gestire i documenti del Sistema di Gestione (Manuale, politiche, procedure, istruzioni operative).
NOTA: descrive funzioni dell'interfaccia; non e' contenuto normativo. ISO 27001/27017/27018 sono buone prassi; gli obblighi in Italia derivano da NIS2 / D.Lgs. 138/2024 / Determinazioni ACN.
# Gestione documentale del Modello SGSI (Modello SGSI > Documenti)
I documenti del Modello SGSI (ISO 27001) — Manuale SGSI, politiche, procedure e istruzioni operative — si trovano nel modulo "Modello SGSI (ISO 27001)", passo 5 "Documenti".
## Ciclo di vita del documento (ISO/IEC 27001 cl. 7.5)
Ogni documento attraversa gli stati: Bozza -> In revisione -> Approvato -> Pubblicato -> Archiviato. Lo stato e' mostrato con un badge colorato.
- "Invia in revisione": da Bozza a In revisione.
- "Approva": da In revisione ad Approvato (registra chi approva e quando).
- "Pubblica": da Approvato a Pubblicato — il documento entra in vigore; vengono impostate la data di entrata in vigore e la prossima data di riesame (a 1 anno).
- "Rimanda in bozza": riporta un documento in revisione alla Bozza, con una nota.
- "Archivia": ritira un documento pubblicato.
- "Nuova versione": riapre un documento pubblicato come nuova Bozza con numero di versione incrementato; la versione precedente resta nello storico.
Ad ogni pubblicazione viene salvato uno snapshot di versione, consultabile con il pulsante "Storico".
## Scaricare in Word
- Pulsante "Word" su un documento: scarica quel singolo documento in formato .doc, modificabile in Word o LibreOffice (mantiene titoli, elenchi e tabelle).
- Pulsante "Scarica tutto (Word)": scarica l'intero set documentale in un unico file Word, con copertina e indice.
- Pulsante "Apri": mostra il documento in una nuova scheda del browser.
## Modificare il contenuto
Il pulsante "Modifica" apre un editor con barra strumenti (grassetto, corsivo, titoli, elenchi) e una modalita' HTML. La modifica e' consentita solo sui documenti in stato Bozza o In revisione. Per modificare un documento gia' Pubblicato occorre prima creare una "Nuova versione".
## Permessi documentali (chi puo' fare cosa)
I diritti sulle azioni del ciclo di vita sono configurabili per RUOLO in "Impostazioni > Permessi documentali", tramite una matrice Ruolo x Azione (modifica contenuto, invia in revisione, approva, pubblica, rimanda in bozza, archivia, nuova versione). Vale per la singola organizzazione. Di default sono abilitati Amministratore, Responsabile SGSI/Compliance e Direzione; il super amministratore ha sempre tutti i permessi. La matrice e' visibile a tutti i membri, ma solo Amministratore e Direzione possono modificarla.
TXT;
$text = preg_replace('/\n{3,}/', "\n\n", trim($TEXT));
$chunks = chunkText($text, 2000, 200);
logln('=== Ingest "Gestione documentale SGSI" (scope SYSTEM) ===');
logln(' testo: ' . strlen($text) . ' char -> ' . count($chunks) . ' chunk');
if ($dryRun) { logln('DRY-RUN: nessun upsert.'); exit(0); }
$embed = new EmbedService();
$vector = new VectorService();
$vector->ensureCollection($embed->dims);
try {
$vector->deleteByFilter(['must' => [
['key' => 'scope', 'match' => ['value' => 'SYSTEM']],
['key' => 'source', 'match' => ['value' => $SOURCE]],
]]);
logln(' rimossi eventuali chunk precedenti per questa fonte');
} catch (Throwable $e) { logln(' (warning) delete: ' . $e->getMessage()); }
$docUuid = uuid(); $points = [];
foreach ($chunks as $i => $chunk) {
$vec = null;
for ($try = 1; $try <= 5; $try++) {
try { $vec = $embed->embed($chunk); break; }
catch (Throwable $e) { if ($try === 5) { logln(" ERRORE embed chunk {$i}: " . $e->getMessage()); throw $e; } logln(" retry embed chunk {$i} (tentativo {$try})"); sleep($try); }
}
$points[] = ['id' => uuid(), 'vector' => $vec, 'payload' => [
'doc_uuid' => $docUuid,
'title' => $TITLE . ($i > 0 ? ' (parte ' . ($i + 1) . ')' : ''),
'chunk' => $chunk,
'entity_type' => 'guida_prodotto',
'source' => $SOURCE,
'lang' => 'it',
'scope' => 'SYSTEM',
'consulting_firm_id' => null,
'organization_id' => null,
'shared_with_orgs' => [],
'uploaded_by' => 0,
]];
}
foreach (array_chunk($points, 64) as $batch) { $vector->upsertBatch($batch); }
logln(' upsert OK: ' . count($points) . ' chunk SYSTEM (source="' . $SOURCE . '")');
logln('=== FATTO ===');