159 lines
4.9 KiB
JavaScript
159 lines
4.9 KiB
JavaScript
|
|
const express = require('express');
|
||
|
|
const router = express.Router();
|
||
|
|
const { getDatabase } = require('../db/init');
|
||
|
|
|
||
|
|
// Golden Retriever panel markers tracked by Breedr
|
||
|
|
const GR_MARKERS = [
|
||
|
|
'PRA1', 'PRA2', 'prcd-PRA', 'GR-PRA1', 'GR-PRA2',
|
||
|
|
'ICH1', 'ICH2', 'NCL', 'DM', 'MD'
|
||
|
|
];
|
||
|
|
|
||
|
|
// GET all genetic tests for a dog
|
||
|
|
router.get('/dog/:dogId', (req, res) => {
|
||
|
|
try {
|
||
|
|
const db = getDatabase();
|
||
|
|
const tests = db.prepare(`
|
||
|
|
SELECT * FROM genetic_tests
|
||
|
|
WHERE dog_id = ?
|
||
|
|
ORDER BY marker ASC
|
||
|
|
`).all(req.params.dogId);
|
||
|
|
|
||
|
|
// Return a full panel including not_tested placeholders
|
||
|
|
const byMarker = {};
|
||
|
|
for (const t of tests) byMarker[t.marker] = t;
|
||
|
|
|
||
|
|
const panel = GR_MARKERS.map(marker => ({
|
||
|
|
marker,
|
||
|
|
...(byMarker[marker] || { result: 'not_tested', dog_id: Number(req.params.dogId) })
|
||
|
|
}));
|
||
|
|
|
||
|
|
res.json({ tests, panel });
|
||
|
|
} catch (error) {
|
||
|
|
res.status(500).json({ error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// GET pairing risk — compare sire + dam carrier status
|
||
|
|
// Usage: GET /api/genetics/pairing-risk?sireId=1&damId=2
|
||
|
|
router.get('/pairing-risk', (req, res) => {
|
||
|
|
try {
|
||
|
|
const { sireId, damId } = req.query;
|
||
|
|
if (!sireId || !damId) {
|
||
|
|
return res.status(400).json({ error: 'sireId and damId are required' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const db = getDatabase();
|
||
|
|
|
||
|
|
const getResults = (dogId) => {
|
||
|
|
const rows = db.prepare('SELECT marker, result FROM genetic_tests WHERE dog_id = ?').all(dogId);
|
||
|
|
const map = {};
|
||
|
|
for (const r of rows) map[r.marker] = r.result;
|
||
|
|
return map;
|
||
|
|
};
|
||
|
|
|
||
|
|
const sireResults = getResults(sireId);
|
||
|
|
const damResults = getResults(damId);
|
||
|
|
|
||
|
|
const risks = [];
|
||
|
|
for (const marker of GR_MARKERS) {
|
||
|
|
const s = sireResults[marker] || 'not_tested';
|
||
|
|
const d = damResults[marker] || 'not_tested';
|
||
|
|
|
||
|
|
// Both affected or carrier x carrier = risk
|
||
|
|
if (
|
||
|
|
(s === 'affected' || d === 'affected') ||
|
||
|
|
(s === 'carrier' && d === 'carrier')
|
||
|
|
) {
|
||
|
|
risks.push({
|
||
|
|
marker,
|
||
|
|
sire_result: s,
|
||
|
|
dam_result: d,
|
||
|
|
risk_level: (s === 'affected' || d === 'affected') ? 'high' : 'moderate',
|
||
|
|
note: s === 'affected' || d === 'affected'
|
||
|
|
? 'One or both parents are affected — do not breed'
|
||
|
|
: 'Both parents are carriers — 25% chance of affected offspring',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
sire_id: Number(sireId),
|
||
|
|
dam_id: Number(damId),
|
||
|
|
risks,
|
||
|
|
safe_to_pair: risks.length === 0,
|
||
|
|
});
|
||
|
|
} catch (error) {
|
||
|
|
res.status(500).json({ error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// GET single genetic test
|
||
|
|
router.get('/:id', (req, res) => {
|
||
|
|
try {
|
||
|
|
const db = getDatabase();
|
||
|
|
const test = db.prepare('SELECT * FROM genetic_tests WHERE id = ?').get(req.params.id);
|
||
|
|
if (!test) return res.status(404).json({ error: 'Genetic test not found' });
|
||
|
|
res.json(test);
|
||
|
|
} catch (error) {
|
||
|
|
res.status(500).json({ error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// POST create genetic test
|
||
|
|
router.post('/', (req, res) => {
|
||
|
|
try {
|
||
|
|
const { dog_id, test_provider, marker, result, test_date, document_url, notes } = req.body;
|
||
|
|
|
||
|
|
if (!dog_id || !marker || !result) {
|
||
|
|
return res.status(400).json({ error: 'dog_id, marker, and result are required' });
|
||
|
|
}
|
||
|
|
if (!['clear', 'carrier', 'affected', 'not_tested'].includes(result)) {
|
||
|
|
return res.status(400).json({ error: 'result must be: clear | carrier | affected | not_tested' });
|
||
|
|
}
|
||
|
|
|
||
|
|
const db = getDatabase();
|
||
|
|
const dbResult = db.prepare(`
|
||
|
|
INSERT INTO genetic_tests (dog_id, test_provider, marker, result, test_date, document_url, notes)
|
||
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||
|
|
`).run(dog_id, test_provider || null, marker, result, test_date || null, document_url || null, notes || null);
|
||
|
|
|
||
|
|
const test = db.prepare('SELECT * FROM genetic_tests WHERE id = ?').get(dbResult.lastInsertRowid);
|
||
|
|
res.status(201).json(test);
|
||
|
|
} catch (error) {
|
||
|
|
res.status(500).json({ error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// PUT update genetic test
|
||
|
|
router.put('/:id', (req, res) => {
|
||
|
|
try {
|
||
|
|
const { test_provider, marker, result, test_date, document_url, notes } = req.body;
|
||
|
|
|
||
|
|
const db = getDatabase();
|
||
|
|
db.prepare(`
|
||
|
|
UPDATE genetic_tests
|
||
|
|
SET test_provider = ?, marker = ?, result = ?, test_date = ?,
|
||
|
|
document_url = ?, notes = ?, updated_at = datetime('now')
|
||
|
|
WHERE id = ?
|
||
|
|
`).run(test_provider || null, marker, result, test_date || null, document_url || null, notes || null, req.params.id);
|
||
|
|
|
||
|
|
const test = db.prepare('SELECT * FROM genetic_tests WHERE id = ?').get(req.params.id);
|
||
|
|
res.json(test);
|
||
|
|
} catch (error) {
|
||
|
|
res.status(500).json({ error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
// DELETE genetic test
|
||
|
|
router.delete('/:id', (req, res) => {
|
||
|
|
try {
|
||
|
|
const db = getDatabase();
|
||
|
|
db.prepare('DELETE FROM genetic_tests WHERE id = ?').run(req.params.id);
|
||
|
|
res.json({ message: 'Genetic test deleted successfully' });
|
||
|
|
} catch (error) {
|
||
|
|
res.status(500).json({ error: error.message });
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
module.exports = router;
|