feat/phase-4b-health-genetics #36
@@ -16,28 +16,37 @@ function initDatabase() {
|
||||
// ── Dogs ────────────────────────────────────────────────────────────
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS dogs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
registration_number TEXT,
|
||||
breed TEXT NOT NULL,
|
||||
sex TEXT NOT NULL CHECK(sex IN ('male', 'female')),
|
||||
birth_date TEXT,
|
||||
color TEXT,
|
||||
microchip TEXT,
|
||||
litter_id INTEGER,
|
||||
is_active INTEGER DEFAULT 1,
|
||||
is_champion INTEGER DEFAULT 0,
|
||||
photo_urls TEXT DEFAULT '[]',
|
||||
notes TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
registration_number TEXT,
|
||||
breed TEXT NOT NULL,
|
||||
sex TEXT NOT NULL CHECK(sex IN ('male', 'female')),
|
||||
birth_date TEXT,
|
||||
color TEXT,
|
||||
microchip TEXT,
|
||||
litter_id INTEGER,
|
||||
is_active INTEGER DEFAULT 1,
|
||||
is_champion INTEGER DEFAULT 0,
|
||||
chic_number TEXT,
|
||||
age_at_death TEXT,
|
||||
cause_of_death TEXT,
|
||||
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)
|
||||
try {
|
||||
db.exec(`ALTER TABLE dogs ADD COLUMN is_champion INTEGER DEFAULT 0`);
|
||||
} catch (_) { /* column already exists */ }
|
||||
// migrate: add columns if missing (safe on existing DBs)
|
||||
const dogMigrations = [
|
||||
['is_champion', 'INTEGER DEFAULT 0'],
|
||||
['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 ─────────────────────────────────────────────────────────
|
||||
db.exec(`
|
||||
@@ -51,24 +60,24 @@ function initDatabase() {
|
||||
)
|
||||
`);
|
||||
|
||||
// ── Breeding Records ────────────────────────────────────────────────
|
||||
// ── Breeding Records ─────────────────────────────────────────────────
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS breeding_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sire_id INTEGER NOT NULL,
|
||||
dam_id INTEGER NOT NULL,
|
||||
breeding_date TEXT,
|
||||
due_date TEXT,
|
||||
conception_method TEXT CHECK(conception_method IN ('natural', 'ai', 'frozen', 'surgical')),
|
||||
notes TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
sire_id INTEGER NOT NULL,
|
||||
dam_id INTEGER NOT NULL,
|
||||
breeding_date TEXT,
|
||||
due_date TEXT,
|
||||
conception_method TEXT CHECK(conception_method IN ('natural', 'ai', 'frozen', 'surgical')),
|
||||
notes TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (sire_id) REFERENCES dogs(id),
|
||||
FOREIGN KEY (dam_id) REFERENCES dogs(id)
|
||||
)
|
||||
`);
|
||||
|
||||
// ── Litters ─────────────────────────────────────────────────────────
|
||||
// ── Litters ──────────────────────────────────────────────────────────
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS litters (
|
||||
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(`
|
||||
CREATE TABLE IF NOT EXISTS health_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
dog_id INTEGER NOT NULL,
|
||||
record_type TEXT NOT NULL,
|
||||
date TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
vet_name TEXT,
|
||||
notes TEXT,
|
||||
result TEXT,
|
||||
next_due TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
dog_id INTEGER NOT NULL,
|
||||
record_type TEXT NOT NULL,
|
||||
test_type TEXT,
|
||||
test_name TEXT,
|
||||
test_date TEXT NOT NULL,
|
||||
ofa_result TEXT,
|
||||
ofa_number TEXT,
|
||||
performed_by TEXT,
|
||||
expires_at TEXT,
|
||||
document_url TEXT,
|
||||
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)
|
||||
)
|
||||
`);
|
||||
@@ -126,7 +195,6 @@ function initDatabase() {
|
||||
)
|
||||
`);
|
||||
|
||||
// migrate: add new kennel columns if missing (safe on existing DBs)
|
||||
const kennelCols = [
|
||||
['kennel_name', "TEXT DEFAULT 'BREEDR'"],
|
||||
['kennel_tagline', 'TEXT'],
|
||||
@@ -139,12 +207,9 @@ function initDatabase() {
|
||||
['owner_name', 'TEXT'],
|
||||
];
|
||||
for (const [col, def] of kennelCols) {
|
||||
try {
|
||||
db.exec(`ALTER TABLE settings ADD COLUMN ${col} ${def}`);
|
||||
} catch (_) { /* already exists */ }
|
||||
try { 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();
|
||||
if (!existing) {
|
||||
db.prepare(`INSERT INTO settings (kennel_name) VALUES (?)`).run('BREEDR');
|
||||
|
||||
Reference in New Issue
Block a user