longevity: salvataggio compilazione questionario, versionata
This commit is contained in:
@@ -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<string, number>;
|
||||||
|
liberi?: Record<string, string>;
|
||||||
|
}
|
||||||
|
): 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;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user