4968b75472
Un check-up e un questionario sono sessioni separate: prima di questo
commit calcolaSessione leggeva solo le misure della sessione corrente,
quindi l'una non vedeva mai i dati dell'altra. misureValideAlla (nuova,
motore/index.ts) legge invece, per ogni test_id, la misura valida piu'
recente del cliente fra tutte le sue sessioni fino a quella data.
sottoDominiDellaSessione unisce sotto-domini da curva (questionario) e
sotto-domini fisici (sottoDominiFisici) in un'unica mappa, usata sia da
calcolaSessione (livello 1) sia da vista.ts (sottoDominiPresenti, per
"cosa manca" nel referto — non piu' una query separata sulla tabella
misure). Aggiornati i commenti di seed-demo.ts (il registro ora contiene
anche le voci f_*, anche se questa demo non le compila).
Test nuovi in motore-fisici-sessione.test.ts: il caso demo() dell'oracolo
end-to-end, un ingresso mancante o l'eta assente che lascia il
sotto-dominio fuori, la combinazione fra sessioni (check-up dopo il
questionario, sessione precedente che non vede il futuro, misura fuori
range sostituita dalla precedente valida). 202 test esistenti restano
verdi, 9 nuovi: 211 totali. tsc --noEmit: 0 errori (0 anche su 13d8cdd).
Refs #1
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
124 lines
6.2 KiB
TypeScript
124 lines
6.2 KiB
TypeScript
/**
|
|
* 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: questo script compila SOLO il questionario (le 20
|
|
* domande `q_*`) tramite `salvaDalForm` — non registra nessuna misura fisica (`f_*`), che
|
|
* arriva dal check-up col gestionale trainer, non da qui. Il registro le contiene entrambe
|
|
* (vedi `registro.ts`), ma senza `registraMisure` per le `f_*` questa sessione non ne vede
|
|
* nessuna. 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 di UNA sessione di solo questionario, 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 <altro-nome> 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<string, number> = {
|
|
// 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');
|