/** * seed-demo.ts — un cliente dimostrativo con un referto vero da mostrare. * * COSA FA, in quest'ordine: * 1. apre i tre database (li crea se mancano; il registro dei test si semina da sé, * vedi src/lib/longevity/db.ts); * 2. crea un utente con ruolo `cliente` in piattaforme.db; * 3. crea il suo fascicolo — la riga con il nome in identity.db, quella senza nome in * longevity.db — passando da `creaCliente`, l'unico varco fra le due; * 4. compila il questionario per lui con risposte plausibili, attraverso `salvaDalForm`: * cioè lo stesso percorso della pagina vera, consenso compreso. Da lì i punteggi * vengono calcolati e congelati, ed è quello che il referto legge. * * COME SI USA: * npm run seed-demo # utente "demo-cliente", password generata * npm run seed-demo -- --utente nicola-demo --password sceltaDaTe * npm run seed-demo -- --data 2026-09-03 # data della compilazione (default: oggi) * percorsi: --utenti-db / --longevity-db / --identity-db, oppure le variabili * DB_PATH / LONGEVITY_DB_PATH / IDENTITY_DB_PATH (stessi nomi del container). * * ⚠️ NON è idempotente per scelta: se l'utente esiste già lo dice e si ferma, senza * toccare niente. Rifare la demo su dati esistenti significherebbe o duplicare un * fascicolo o cancellarne uno, e nessuna delle due è una cosa che uno script deve * decidere da solo. * * ⚠️ I dati sono INVENTATI e vanno detti tali a chi guarda: è una persona che non esiste. * Il referto che ne esce non è un esempio clinico, è un esempio di interfaccia. * * ⚠️ Cosa mostrerà davvero il radar: il registro contiene oggi SOLO le 20 domande del * questionario — i test fisici (handgrip, VO2max, plank, composizione corporea...) non * hanno ancora una riga, arrivano col gestionale trainer. Quindi escono con un valore gli * assi che il questionario copre almeno al 40%, e gli altri restano dichiarati * insufficienti con l'elenco di cosa manca. Non è un difetto della demo: è lo stato del * prodotto, ed è anche la regola di Nicola che si vede all'opera (annullare invece di * stimare). * * DIPENDE DA: tsx (devDependency) — il codice del prodotto è TypeScript e questo script * lo usa davvero invece di riscriverne una copia in SQL. */ import { randomBytes } from 'node:crypto'; import { createLongevityDb, createIdentityDb } from '../src/lib/longevity/db'; import { creaCliente, etaAllaData } from '../src/lib/longevity/anagrafica'; import { salvaDalForm, campiDelQuestionario } from '../src/lib/longevity/vista'; import { createDb } from '../src/lib/db'; import { createUser } from '../src/lib/auth'; const argv = process.argv.slice(2); const opt = (nome: string, def: string): string => { const i = argv.indexOf(nome); return i >= 0 && argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[i + 1] : def; }; const utente = opt('--utente', 'demo-cliente'); const password = opt('--password', randomBytes(9).toString('base64url')); const nome = opt('--nome', 'Giulia'); const cognome = opt('--cognome', 'Esempio'); const nascita = opt('--nascita', '1990-05-14'); const data = opt('--data', new Date().toISOString().slice(0, 10)); const utentiDb = createDb(opt('--utenti-db', process.env.DB_PATH ?? 'data/piattaforme.db')); const longevity = createLongevityDb(opt('--longevity-db', process.env.LONGEVITY_DB_PATH ?? 'data/longevity.db')); const identity = createIdentityDb(opt('--identity-db', process.env.IDENTITY_DB_PATH ?? 'data/identity.db')); const esistente = utentiDb.prepare('SELECT id FROM users WHERE username = ?').get(utente) as { id: number } | undefined; if (esistente) { console.error(`L'utente "${utente}" esiste già (id ${esistente.id}): non tocco niente.`); console.error('Usa --utente per una demo nuova.'); process.exit(1); } /** * Le risposte della persona inventata: una in salute ma non perfetta. Un questionario * con tutti i valori al massimo darebbe un referto tutto verde, che è il modo migliore * per non far vedere niente — né le curve, né la soglia del 40%, né cosa manca. */ const RISPOSTE: Record = { // sonno q_ore_sonno: 7, q_min_addorm: 20, q_risvegli: 1, q_riposato: 7, q_sonnolenza_diurna: 2, q_caffeina: 3, // energia e stress q_energia_media: 7, q_calo_pomeridiano: 4, q_tensione: 3, q_sopraffatto: 4, q_esaurimento: 1, q_controllo: 7, q_sicurezza_gestione: 8, q_pensieri_lavoro: 2, // stile di vita q_attivita: 3, q_alimentazione: 7, q_alcol_life: 4, q_sigarette: 0, q_luce: 2, q_schermi: 30, }; // Il registro è la fonte: se una domanda è stata aggiunta o rinominata, questo script deve // dirlo invece di salvare un questionario a metà senza che si veda. const campi = campiDelQuestionario(longevity, data); const mancanti = campi.filter((c) => !(c.id in RISPOSTE)).map((c) => c.id); const inventate = Object.keys(RISPOSTE).filter((id) => !campi.some((c) => c.id === id)); if (mancanti.length || inventate.length) { if (mancanti.length) console.error(`⚠️ domande del registro senza risposta qui: ${mancanti.join(', ')}`); if (inventate.length) console.error(`⚠️ risposte che il registro non conosce: ${inventate.join(', ')}`); console.error('Allinea RISPOSTE al registro prima di rifare la demo.'); process.exit(1); } const userId = createUser(utentiDb, utente, password, 'cliente'); const code = creaCliente(identity, longevity, { nome, cognome, sesso: 'F', data_nascita: nascita, user_id: userId, }); const esito = salvaDalForm(longevity, { client_code: code, data, eta: etaAllaData(nascita, data), risposte: RISPOSTE, consensoSanitario: 'sì', }); if (!esito.ok) { console.error(`Il questionario non è stato salvato: ${esito.errore}`); process.exit(1); } console.log(`utente ${utente} (ruolo cliente, id ${userId})`); console.log(`password ${password}`); console.log(`fascicolo ${code} — ${nome} ${cognome}, ${etaAllaData(nascita, data)} anni`); console.log(`sessione ${esito.sessioneId}, questionario del ${data}, ${campi.length} risposte`); console.log('\nEntra da /login e atterri sul referto: /longevity/io');