feat/phase-4b-health-genetics #36

Merged
jason merged 8 commits from feat/phase-4b-health-genetics into master 2026-03-09 23:38:19 -05:00
Showing only changes of commit 8635483332 - Show all commits

View File

@@ -2,32 +2,113 @@ const express = require('express');
const router = express.Router();
const { getDatabase } = require('../db/init');
// OFA tests that count toward GRCA eligibility
const GRCA_REQUIRED = ['hip_ofa', 'hip_pennhip', 'elbow_ofa', 'heart_ofa', 'heart_echo', 'eye_caer'];
const GRCA_CORE = {
hip: ['hip_ofa', 'hip_pennhip'],
elbow: ['elbow_ofa'],
heart: ['heart_ofa', 'heart_echo'],
eye: ['eye_caer'],
};
// Helper: compute clearance summary for a dog
function getClearanceSummary(db, dogId) {
const records = db.prepare(`
SELECT test_type, ofa_result, ofa_number, expires_at, test_date
FROM health_records
WHERE dog_id = ? AND test_type IS NOT NULL
ORDER BY test_date DESC
`).all(dogId);
const today = new Date();
const in90 = new Date(); in90.setDate(today.getDate() + 90);
const summary = {};
for (const [group, types] of Object.entries(GRCA_CORE)) {
const match = records.find(r => types.includes(r.test_type));
if (!match) {
summary[group] = { status: 'missing', record: null };
} else {
let status = 'pass';
if (match.expires_at) {
const exp = new Date(match.expires_at);
if (exp < today) status = 'expired';
else if (exp <= in90) status = 'expiring_soon';
}
summary[group] = { status, record: match };
}
}
return summary;
}
// GET all health records for a dog
router.get('/dog/:dogId', (req, res) => {
try {
const db = getDatabase();
const records = db.prepare(`
SELECT * FROM health_records
WHERE dog_id = ?
SELECT * FROM health_records
WHERE dog_id = ?
ORDER BY test_date DESC
`).all(req.params.dogId);
res.json(records);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET clearance summary (Hip / Elbow / Heart / Eyes) for a dog
router.get('/dog/:dogId/clearance-summary', (req, res) => {
try {
const db = getDatabase();
const dog = db.prepare('SELECT id, birth_date, chic_number FROM dogs WHERE id = ?').get(req.params.dogId);
if (!dog) return res.status(404).json({ error: 'Dog not found' });
const summary = getClearanceSummary(db, dog.id);
// Age check: must be >= 24 months for hip/elbow
let ageEligible = false;
if (dog.birth_date) {
const months = (new Date() - new Date(dog.birth_date)) / (1000 * 60 * 60 * 24 * 30.44);
ageEligible = months >= 24;
}
const allPass = Object.values(summary).every(s => ['pass', 'expiring_soon'].includes(s.status));
const grca_eligible = allPass && ageEligible;
res.json({ summary, grca_eligible, age_eligible: ageEligible, chic_number: dog.chic_number });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET CHIC eligibility check
router.get('/dog/:dogId/chic-eligible', (req, res) => {
try {
const db = getDatabase();
const dog = db.prepare('SELECT id, chic_number FROM dogs WHERE id = ?').get(req.params.dogId);
if (!dog) return res.status(404).json({ error: 'Dog not found' });
const summary = getClearanceSummary(db, dog.id);
const missing = Object.entries(summary)
.filter(([, v]) => v.status === 'missing')
.map(([k]) => k);
res.json({
chic_eligible: missing.length === 0,
chic_number: dog.chic_number || null,
missing_tests: missing,
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// GET single health record
router.get('/:id', (req, res) => {
try {
const db = getDatabase();
const record = db.prepare('SELECT * FROM health_records WHERE id = ?').get(req.params.id);
if (!record) {
return res.status(404).json({ error: 'Health record not found' });
}
if (!record) return res.status(404).json({ error: 'Health record not found' });
res.json(record);
} catch (error) {
res.status(500).json({ error: error.message });
@@ -37,20 +118,30 @@ router.get('/:id', (req, res) => {
// POST create health record
router.post('/', (req, res) => {
try {
const { dog_id, record_type, test_name, test_date, result, document_url, notes } = req.body;
const {
dog_id, record_type, test_type, test_name, test_date,
ofa_result, ofa_number, performed_by, expires_at,
document_url, result, vet_name, next_due, notes
} = req.body;
if (!dog_id || !record_type || !test_date) {
return res.status(400).json({ error: 'Dog ID, record type, and test date are required' });
return res.status(400).json({ error: 'dog_id, record_type, and test_date are required' });
}
const db = getDatabase();
const dbResult = db.prepare(`
INSERT INTO health_records (dog_id, record_type, test_name, test_date, result, document_url, notes)
VALUES (?, ?, ?, ?, ?, ?, ?)
`).run(dog_id, record_type, test_name, test_date, result, document_url, notes);
INSERT INTO health_records
(dog_id, record_type, test_type, test_name, test_date,
ofa_result, ofa_number, performed_by, expires_at,
document_url, result, vet_name, next_due, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
dog_id, record_type, test_type || null, test_name || null, test_date,
ofa_result || null, ofa_number || null, performed_by || null, expires_at || null,
document_url || null, result || null, vet_name || null, next_due || null, notes || null
);
const record = db.prepare('SELECT * FROM health_records WHERE id = ?').get(dbResult.lastInsertRowid);
res.status(201).json(record);
} catch (error) {
res.status(500).json({ error: error.message });
@@ -60,15 +151,27 @@ router.post('/', (req, res) => {
// PUT update health record
router.put('/:id', (req, res) => {
try {
const { record_type, test_name, test_date, result, document_url, notes } = req.body;
const {
record_type, test_type, test_name, test_date,
ofa_result, ofa_number, performed_by, expires_at,
document_url, result, vet_name, next_due, notes
} = req.body;
const db = getDatabase();
db.prepare(`
UPDATE health_records
SET record_type = ?, test_name = ?, test_date = ?, result = ?, document_url = ?, notes = ?
UPDATE health_records
SET record_type = ?, test_type = ?, test_name = ?, test_date = ?,
ofa_result = ?, ofa_number = ?, performed_by = ?, expires_at = ?,
document_url = ?, result = ?, vet_name = ?, next_due = ?, notes = ?,
updated_at = datetime('now')
WHERE id = ?
`).run(record_type, test_name, test_date, result, document_url, notes, req.params.id);
`).run(
record_type, test_type || null, test_name || null, test_date,
ofa_result || null, ofa_number || null, performed_by || null, expires_at || null,
document_url || null, result || null, vet_name || null, next_due || null, notes || null,
req.params.id
);
const record = db.prepare('SELECT * FROM health_records WHERE id = ?').get(req.params.id);
res.json(record);
} catch (error) {
@@ -87,4 +190,4 @@ router.delete('/:id', (req, res) => {
}
});
module.exports = router;
module.exports = router;