Merge pull request 'fix: COI correctly calculates parent×offspring and direct-relation pairings' (#47) from fix/coi-direct-relation into master
Reviewed-on: #47
This commit was merged in pull request #47.
This commit is contained in:
@@ -57,7 +57,15 @@ function isDirectRelation(db, sireId, damId) {
|
|||||||
* calculateCOI(db, sireId, damId)
|
* calculateCOI(db, sireId, damId)
|
||||||
* Wright Path Coefficient method.
|
* Wright Path Coefficient method.
|
||||||
* Dogs included at gen 0 in their own maps so parent x offspring
|
* Dogs included at gen 0 in their own maps so parent x offspring
|
||||||
* yields 25% COI. Per path: (0.5)^(sireGen + damGen + 1)
|
* yields ~25% COI.
|
||||||
|
*
|
||||||
|
* Fix: do NOT exclude sid/did from commonIds globally.
|
||||||
|
* - Exclude `did` from sireMap keys (the dam itself can't be a
|
||||||
|
* common ancestor of the sire's side for THIS pairing's offspring)
|
||||||
|
* - Exclude `sid` from damMap keys (same logic for sire)
|
||||||
|
* This preserves the case where the sire IS a common ancestor in the
|
||||||
|
* dam's ancestry (parent x offspring) while still avoiding reflexive
|
||||||
|
* self-loops.
|
||||||
*/
|
*/
|
||||||
function calculateCOI(db, sireId, damId) {
|
function calculateCOI(db, sireId, damId) {
|
||||||
const sid = parseInt(sireId);
|
const sid = parseInt(sireId);
|
||||||
@@ -65,8 +73,15 @@ function calculateCOI(db, sireId, damId) {
|
|||||||
const sireMap = getAncestorMap(db, sid);
|
const sireMap = getAncestorMap(db, sid);
|
||||||
const damMap = getAncestorMap(db, did);
|
const damMap = getAncestorMap(db, did);
|
||||||
|
|
||||||
|
// Common ancestors: in BOTH maps, but:
|
||||||
|
// - not the dam itself appearing in sireMap (would be a loop)
|
||||||
|
// - not the sire itself appearing in damMap already handled below
|
||||||
|
// We collect all IDs present in both, excluding only the direct
|
||||||
|
// subjects (did from sireMap side, sid excluded already since we
|
||||||
|
// iterate sireMap keys — but sid IS in sireMap at gen 0, and if
|
||||||
|
// damMap also has sid, that is the parent×offspring case we WANT).
|
||||||
const commonIds = [...sireMap.keys()].filter(
|
const commonIds = [...sireMap.keys()].filter(
|
||||||
id => damMap.has(id) && id !== sid && id !== did
|
id => damMap.has(id) && id !== did
|
||||||
);
|
);
|
||||||
|
|
||||||
let coi = 0;
|
let coi = 0;
|
||||||
@@ -103,11 +118,11 @@ function calculateCOI(db, sireId, damId) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================
|
// =====================================================================
|
||||||
// IMPORTANT: Specific named routes MUST be registered BEFORE
|
// IMPORTANT: Specific named routes MUST be registered BEFORE
|
||||||
// the /:id wildcard, or Express will match 'relations' and
|
// the /:id wildcard, or Express will match 'relations' and
|
||||||
// 'trial-pairing' as dog IDs and return 404/wrong data.
|
// 'trial-pairing' as dog IDs and return 404/wrong data.
|
||||||
// =============================================================
|
// =====================================================================
|
||||||
|
|
||||||
// POST /api/pedigree/trial-pairing
|
// POST /api/pedigree/trial-pairing
|
||||||
router.post('/trial-pairing', (req, res) => {
|
router.post('/trial-pairing', (req, res) => {
|
||||||
@@ -122,7 +137,7 @@ router.post('/trial-pairing', (req, res) => {
|
|||||||
const dam = db.prepare("SELECT * FROM dogs WHERE id = ? AND sex = 'female'").get(dam_id);
|
const dam = db.prepare("SELECT * FROM dogs WHERE id = ? AND sex = 'female'").get(dam_id);
|
||||||
|
|
||||||
if (!sire || !dam) {
|
if (!sire || !dam) {
|
||||||
return res.status(404).json({ error: 'Invalid sire or dam — check sex values in database' });
|
return res.status(404).json({ error: 'Invalid sire or dam \u2014 check sex values in database' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const relation = isDirectRelation(db, sire_id, dam_id);
|
const relation = isDirectRelation(db, sire_id, dam_id);
|
||||||
@@ -153,9 +168,9 @@ router.get('/relations/:sireId/:damId', (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// =============================================================
|
// =====================================================================
|
||||||
// Wildcard routes last
|
// Wildcard routes last
|
||||||
// =============================================================
|
// =====================================================================
|
||||||
|
|
||||||
// GET /api/pedigree/:id
|
// GET /api/pedigree/:id
|
||||||
router.get('/:id', (req, res) => {
|
router.get('/:id', (req, res) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user