feat(db): Phase 4b schema — OFA clearances, genetic_tests, cancer_history, eligibility fields

This commit is contained in:
2026-03-09 23:21:44 -05:00
parent 286b9c9bd0
commit 91ad50655c

View File

@@ -16,28 +16,37 @@ function initDatabase() {
// ── Dogs ──────────────────────────────────────────────────────────── // ── Dogs ────────────────────────────────────────────────────────────
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS dogs ( CREATE TABLE IF NOT EXISTS dogs (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL,
registration_number TEXT, registration_number TEXT,
breed TEXT NOT NULL, breed TEXT NOT NULL,
sex TEXT NOT NULL CHECK(sex IN ('male', 'female')), sex TEXT NOT NULL CHECK(sex IN ('male', 'female')),
birth_date TEXT, birth_date TEXT,
color TEXT, color TEXT,
microchip TEXT, microchip TEXT,
litter_id INTEGER, litter_id INTEGER,
is_active INTEGER DEFAULT 1, is_active INTEGER DEFAULT 1,
is_champion INTEGER DEFAULT 0, is_champion INTEGER DEFAULT 0,
photo_urls TEXT DEFAULT '[]', chic_number TEXT,
notes TEXT, age_at_death TEXT,
created_at TEXT DEFAULT (datetime('now')), cause_of_death TEXT,
updated_at TEXT DEFAULT (datetime('now')) photo_urls TEXT DEFAULT '[]',
notes TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
) )
`); `);
// migrate: add is_champion if missing (safe on existing DBs) // migrate: add columns if missing (safe on existing DBs)
try { const dogMigrations = [
db.exec(`ALTER TABLE dogs ADD COLUMN is_champion INTEGER DEFAULT 0`); ['is_champion', 'INTEGER DEFAULT 0'],
} catch (_) { /* column already exists */ } ['chic_number', 'TEXT'],
['age_at_death', 'TEXT'],
['cause_of_death', 'TEXT'],
];
for (const [col, def] of dogMigrations) {
try { db.exec(`ALTER TABLE dogs ADD COLUMN ${col} ${def}`); } catch (_) { /* already exists */ }
}
// ── Parents ───────────────────────────────────────────────────────── // ── Parents ─────────────────────────────────────────────────────────
db.exec(` db.exec(`
@@ -51,24 +60,24 @@ function initDatabase() {
) )
`); `);
// ── Breeding Records ──────────────────────────────────────────────── // ── Breeding Records ────────────────────────────────────────────────
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS breeding_records ( CREATE TABLE IF NOT EXISTS breeding_records (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
sire_id INTEGER NOT NULL, sire_id INTEGER NOT NULL,
dam_id INTEGER NOT NULL, dam_id INTEGER NOT NULL,
breeding_date TEXT, breeding_date TEXT,
due_date TEXT, due_date TEXT,
conception_method TEXT CHECK(conception_method IN ('natural', 'ai', 'frozen', 'surgical')), conception_method TEXT CHECK(conception_method IN ('natural', 'ai', 'frozen', 'surgical')),
notes TEXT, notes TEXT,
created_at TEXT DEFAULT (datetime('now')), created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (sire_id) REFERENCES dogs(id), FOREIGN KEY (sire_id) REFERENCES dogs(id),
FOREIGN KEY (dam_id) REFERENCES dogs(id) FOREIGN KEY (dam_id) REFERENCES dogs(id)
) )
`); `);
// ── Litters ───────────────────────────────────────────────────────── // ── Litters ─────────────────────────────────────────────────────────
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS litters ( CREATE TABLE IF NOT EXISTS litters (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -89,21 +98,81 @@ function initDatabase() {
) )
`); `);
// ── Health Records ────────────────────────────────────────────────── // ── Health Records (OFA-extended) ────────────────────────────────────
// test_type values: hip_ofa | hip_pennhip | elbow_ofa | heart_ofa |
// heart_echo | eye_caer | thyroid_ofa | dna_panel | vaccination |
// other
// ofa_result values: excellent | good | fair | borderline | mild |
// moderate | severe | normal | abnormal | pass | fail | carrier |
// clear | affected | n/a
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS health_records ( CREATE TABLE IF NOT EXISTS health_records (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL, dog_id INTEGER NOT NULL,
record_type TEXT NOT NULL, record_type TEXT NOT NULL,
date TEXT NOT NULL, test_type TEXT,
title TEXT NOT NULL, test_name TEXT,
description TEXT, test_date TEXT NOT NULL,
vet_name TEXT, ofa_result TEXT,
notes TEXT, ofa_number TEXT,
result TEXT, performed_by TEXT,
next_due TEXT, expires_at TEXT,
created_at TEXT DEFAULT (datetime('now')), document_url TEXT,
updated_at TEXT DEFAULT (datetime('now')), result TEXT,
vet_name TEXT,
next_due TEXT,
notes TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (dog_id) REFERENCES dogs(id)
)
`);
// migrate: add OFA-specific columns if missing
const healthMigrations = [
['test_type', 'TEXT'],
['ofa_result', 'TEXT'],
['ofa_number', 'TEXT'],
['performed_by', 'TEXT'],
['expires_at', 'TEXT'],
['document_url', 'TEXT'],
];
for (const [col, def] of healthMigrations) {
try { db.exec(`ALTER TABLE health_records ADD COLUMN ${col} ${def}`); } catch (_) { /* already exists */ }
}
// ── Genetic Tests (DNA Panel) ─────────────────────────────────────────
// result values: clear | carrier | affected | not_tested
// marker examples: PRA1, PRA2, prcd-PRA, GR-PRA1, GR-PRA2, ICH1,
// ICH2, NCL, DM, MD
db.exec(`
CREATE TABLE IF NOT EXISTS genetic_tests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL,
test_provider TEXT,
marker TEXT NOT NULL,
result TEXT NOT NULL CHECK(result IN ('clear', 'carrier', 'affected', 'not_tested')),
test_date TEXT,
document_url TEXT,
notes TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (dog_id) REFERENCES dogs(id)
)
`);
// ── Cancer History ───────────────────────────────────────────────────
db.exec(`
CREATE TABLE IF NOT EXISTS cancer_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL,
cancer_type TEXT,
age_at_diagnosis TEXT,
age_at_death TEXT,
cause_of_death TEXT,
notes TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (dog_id) REFERENCES dogs(id) FOREIGN KEY (dog_id) REFERENCES dogs(id)
) )
`); `);
@@ -126,7 +195,6 @@ function initDatabase() {
) )
`); `);
// migrate: add new kennel columns if missing (safe on existing DBs)
const kennelCols = [ const kennelCols = [
['kennel_name', "TEXT DEFAULT 'BREEDR'"], ['kennel_name', "TEXT DEFAULT 'BREEDR'"],
['kennel_tagline', 'TEXT'], ['kennel_tagline', 'TEXT'],
@@ -139,12 +207,9 @@ function initDatabase() {
['owner_name', 'TEXT'], ['owner_name', 'TEXT'],
]; ];
for (const [col, def] of kennelCols) { for (const [col, def] of kennelCols) {
try { try { db.exec(`ALTER TABLE settings ADD COLUMN ${col} ${def}`); } catch (_) { /* already exists */ }
db.exec(`ALTER TABLE settings ADD COLUMN ${col} ${def}`);
} catch (_) { /* already exists */ }
} }
// Seed a default settings row if none exists
const existing = db.prepare('SELECT id FROM settings LIMIT 1').get(); const existing = db.prepare('SELECT id FROM settings LIMIT 1').get();
if (!existing) { if (!existing) {
db.prepare(`INSERT INTO settings (kennel_name) VALUES (?)`).run('BREEDR'); db.prepare(`INSERT INTO settings (kennel_name) VALUES (?)`).run('BREEDR');