feat(api): rewrite health.js with OFA clearance fields, clearance-summary, chic-eligible endpoints
This commit is contained in:
@@ -2,6 +2,45 @@ 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 {
|
||||
@@ -11,23 +50,65 @@ router.get('/dog/:dogId', (req, res) => {
|
||||
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,14 +151,26 @@ 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 = ?
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user