const express = require('express'); const router = express.Router(); const { getDatabase } = require('../db/init'); // Helper function to calculate inbreeding coefficient function calculateCOI(sireId, damId, generations = 5) { const db = getDatabase(); // Get all ancestors for both parents function getAncestors(dogId, currentGen = 0, maxGen = generations) { if (currentGen >= maxGen) return []; const parents = db.prepare(` SELECT p.parent_type, p.parent_id, d.name FROM parents p JOIN dogs d ON p.parent_id = d.id WHERE p.dog_id = ? `).all(dogId); const ancestors = parents.map(p => ({ id: p.parent_id, name: p.name, type: p.parent_type, generation: currentGen + 1 })); parents.forEach(p => { ancestors.push(...getAncestors(p.parent_id, currentGen + 1, maxGen)); }); return ancestors; } const sireAncestors = getAncestors(sireId); const damAncestors = getAncestors(damId); // Find common ancestors const commonAncestors = []; sireAncestors.forEach(sireAnc => { damAncestors.forEach(damAnc => { if (sireAnc.id === damAnc.id) { commonAncestors.push({ id: sireAnc.id, name: sireAnc.name, sireGen: sireAnc.generation, damGen: damAnc.generation }); } }); }); // Calculate COI using path coefficient method let coi = 0; const processed = new Set(); commonAncestors.forEach(anc => { const key = `${anc.id}-${anc.sireGen}-${anc.damGen}`; if (!processed.has(key)) { processed.add(key); const pathLength = anc.sireGen + anc.damGen; coi += Math.pow(0.5, pathLength); } }); return { coefficient: Math.round(coi * 10000) / 100, // Percentage with 2 decimals commonAncestors: [...new Map(commonAncestors.map(a => [a.id, a])).values()] }; } // GET pedigree tree for a dog router.get('/:id', (req, res) => { try { const db = getDatabase(); const generations = parseInt(req.query.generations) || 5; function buildTree(dogId, currentGen = 0) { if (currentGen >= generations) return null; const dog = db.prepare('SELECT * FROM dogs WHERE id = ?').get(dogId); if (!dog) return null; dog.photo_urls = dog.photo_urls ? JSON.parse(dog.photo_urls) : []; const parents = db.prepare(` SELECT p.parent_type, p.parent_id FROM parents p WHERE p.dog_id = ? `).all(dogId); const sire = parents.find(p => p.parent_type === 'sire'); const dam = parents.find(p => p.parent_type === 'dam'); return { ...dog, generation: currentGen, sire: sire ? buildTree(sire.parent_id, currentGen + 1) : null, dam: dam ? buildTree(dam.parent_id, currentGen + 1) : null }; } const tree = buildTree(req.params.id); if (!tree) { return res.status(404).json({ error: 'Dog not found' }); } res.json(tree); } catch (error) { res.status(500).json({ error: error.message }); } }); // GET reverse pedigree (descendants) router.get('/:id/descendants', (req, res) => { try { const db = getDatabase(); const generations = parseInt(req.query.generations) || 3; function buildDescendantTree(dogId, currentGen = 0) { if (currentGen >= generations) return null; const dog = db.prepare('SELECT * FROM dogs WHERE id = ?').get(dogId); if (!dog) return null; dog.photo_urls = dog.photo_urls ? JSON.parse(dog.photo_urls) : []; const offspring = db.prepare(` SELECT DISTINCT d.id, d.name, d.sex, d.birth_date FROM dogs d JOIN parents p ON d.id = p.dog_id WHERE p.parent_id = ? AND d.is_active = 1 `).all(dogId); return { ...dog, generation: currentGen, offspring: offspring.map(child => buildDescendantTree(child.id, currentGen + 1)) }; } const tree = buildDescendantTree(req.params.id); if (!tree) { return res.status(404).json({ error: 'Dog not found' }); } res.json(tree); } catch (error) { res.status(500).json({ error: error.message }); } }); // POST calculate COI for a trial pairing router.post('/trial-pairing', (req, res) => { try { const { sire_id, dam_id } = req.body; if (!sire_id || !dam_id) { return res.status(400).json({ error: 'Both sire_id and dam_id are required' }); } const db = getDatabase(); const sire = db.prepare('SELECT * FROM dogs WHERE id = ? AND sex = "male"').get(sire_id); const dam = db.prepare('SELECT * FROM dogs WHERE id = ? AND sex = "female"').get(dam_id); if (!sire || !dam) { return res.status(404).json({ error: 'Invalid sire or dam' }); } const result = calculateCOI(sire_id, dam_id); res.json({ sire: { id: sire.id, name: sire.name }, dam: { id: dam.id, name: dam.name }, coi: result.coefficient, commonAncestors: result.commonAncestors, recommendation: result.coefficient < 5 ? 'Low risk' : result.coefficient < 10 ? 'Moderate risk' : 'High risk' }); } catch (error) { res.status(500).json({ error: error.message }); } }); module.exports = router;