diff --git a/src/lib/longevity/questionario.ts b/src/lib/longevity/questionario.ts new file mode 100644 index 0000000..5f1032a --- /dev/null +++ b/src/lib/longevity/questionario.ts @@ -0,0 +1,55 @@ +import type Database from 'better-sqlite3'; +import { apriSessione, registraMisure } from './misure'; +import { esisteTest } from './registro'; + +/** Versione del set di domande. Si incrementa quando cambia una domanda. */ +export const QUEST_VERSION = 'v1.0'; + +/** + * Campi testuali che si salvano ma non entrano MAI in una formula + * (indicazione esplicita del cliente, punto 7 della specifica). + */ +export const CAMPI_LIBERI = ['farmaci', 'problematiche_attuali', 'problematiche_pregresse', 'obiettivi']; + +export function salvaCompilazione( + db: Database.Database, + input: { + client_code: string; data: string; eta?: number; + risposte: Record; + liberi?: Record; + } +): number { + // Validare PRIMA di aprire la sessione: altrimenti un id sbagliato lascia + // in giro una sessione vuota. + for (const id of Object.keys(input.risposte)) { + if (!esisteTest(db, id)) throw new Error(`test_id non nel registro: ${id}`); + } + + const sessioneId = apriSessione(db, { + client_code: input.client_code, + data: input.data, + tipo: 'questionario', + eta_alla_data: input.eta, + quest_version: QUEST_VERSION, + }); + + registraMisure( + db, sessioneId, 'questionario', + Object.entries(input.risposte).map(([test_id, valore_num]) => ({ test_id, valore_num })) + ); + + if (input.liberi) { + const ins = db.prepare( + `INSERT INTO profilo_note (client_code, sessione_id, campo_id, testo) VALUES (?, ?, ?, ?)` + ); + const tx = db.transaction(() => { + for (const [campo, testo] of Object.entries(input.liberi!)) { + if (testo.trim() === '') continue; + ins.run(input.client_code, sessioneId, campo, testo); + } + }); + tx(); + } + + return sessioneId; +} diff --git a/tests/longevity/questionario.test.ts b/tests/longevity/questionario.test.ts new file mode 100644 index 0000000..dcf65ad --- /dev/null +++ b/tests/longevity/questionario.test.ts @@ -0,0 +1,67 @@ +import { describe, it, expect } from 'vitest'; +import { createLongevityDb } from '../../src/lib/longevity/db'; +import { seedRegistro } from '../../src/lib/longevity/registro'; +import { salvaCompilazione, QUEST_VERSION } from '../../src/lib/longevity/questionario'; + +function dbPronto() { + const db = createLongevityDb(':memory:'); + seedRegistro(db); + db.prepare(`INSERT INTO soggetti (client_code, sesso) VALUES ('ISL-0001', 'F')`).run(); + return db; +} + +describe('compilazione del questionario', () => { + it('salva le risposte punteggiate come misure', () => { + const db = dbPronto(); + const s = salvaCompilazione(db, { + client_code: 'ISL-0001', data: '2026-08-21', + risposte: { q_ore_sonno: 7.5, q_riposato: 8, q_alcol_life: 3 }, + }); + const n = db.prepare(`SELECT COUNT(*) n FROM misure WHERE sessione_id = ? AND fonte = 'questionario'`) + .get(s) as { n: number }; + expect(n.n).toBe(3); + }); + + it('marchia la compilazione con la versione del questionario', () => { + const db = dbPronto(); + const s = salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', risposte: { q_riposato: 6 } }); + const row = db.prepare(`SELECT tipo, quest_version FROM sessioni WHERE id = ?`).get(s) as + { tipo: string; quest_version: string }; + expect(row.tipo).toBe('questionario'); + expect(row.quest_version).toBe(QUEST_VERSION); + }); + + it('i campi liberi vanno in profilo_note, non fra le misure', () => { + const db = dbPronto(); + const s = salvaCompilazione(db, { + client_code: 'ISL-0001', data: '2026-08-21', + risposte: { q_riposato: 6 }, + liberi: { farmaci: 'nessuno', obiettivi: 'dormire meglio' }, + }); + const note = db.prepare(`SELECT campo_id, testo FROM profilo_note WHERE sessione_id = ? ORDER BY campo_id`) + .all(s) as { campo_id: string; testo: string }[]; + expect(note.map((n) => n.campo_id)).toEqual(['farmaci', 'obiettivi']); + const misure = db.prepare(`SELECT test_id FROM misure WHERE sessione_id = ?`).all(s) as { test_id: string }[]; + expect(misure.map((m) => m.test_id)).toEqual(['q_riposato']); + }); + + it('ogni compilazione e un record nuovo, non un aggiornamento', () => { + const db = dbPronto(); + salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', risposte: { q_riposato: 6 } }); + salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-09-21', risposte: { q_riposato: 9 } }); + const n = db.prepare(`SELECT COUNT(*) n FROM sessioni WHERE tipo = 'questionario'`).get() as { n: number }; + expect(n.n).toBe(2); + }); + + it('una risposta con id sconosciuto fa fallire la compilazione intera', () => { + const db = dbPronto(); + expect(() => + salvaCompilazione(db, { + client_code: 'ISL-0001', data: '2026-08-21', + risposte: { q_riposato: 6, q_domanda_nuova: 3 }, + }) + ).toThrow(/q_domanda_nuova/); + const n = db.prepare(`SELECT COUNT(*) n FROM sessioni`).get() as { n: number }; + expect(n.n).toBe(0); + }); +});