Fix kin-distance metric: shortest descent from direct lineage

Replaces the previous "depth in generations along the strict
lineal chain" definition (which excluded siblings, aunts, cousins
entirely) with the metric the user actually wants: the number of
descent-steps separating the target from the recipient's closest
direct ancestor or descendant.

Examples relative to the recipient:
- sibling:        1  (parent → sibling)
- great-aunt:     1  (great-grandparent → great-aunt)
- nephew:         2  (parent → sibling → nephew)
- first cousin:   2  (grandparent → aunt → cousin)
- second cousin:  3
- ego, parents, grandparents, ..., children, ..., great-greats: 0
- own spouse, step-parents, brothers-in-law: inherit partner's
  distance (so spouse-of-distance-1 is also distance 1)

Implementation:
- Anchor set seeded with R's direct ancestors + R + direct
  descendants (capped at 25 generations to bound runaway data).
- Multi-source BFS expanding by descent only.
- Spouse propagation at every level so a person and their
  spouse always share the same distance.
- Memoised per (recipient xref, max distance).

Tree preference key and range kept (NEWSLETTER_LINEAL_DEPTH,
0–10, default 3); only the semantics and the user-facing label
+ help text change, with concrete examples in both English and
German.
This commit is contained in:
2026-05-15 13:10:38 +02:00
parent ff743e484f
commit 105b09c4c5
4 changed files with 111 additions and 41 deletions
+3 -3
View File
@@ -311,13 +311,13 @@ final class NewsletterDispatchService
return array_fill_keys(array_keys($featured), true);
}
$depth = Configuration::linealDepth($tree);
$lineal_set = $this->relationship_finder->linealKin($self, $depth);
$max_distance = Configuration::linealDepth($tree);
$distances = $this->relationship_finder->kinDistances($self, $max_distance);
$detailed = [];
foreach ($featured as $xref => $_individual) {
if (isset($lineal_set[$xref])) {
if (isset($distances[$xref]) && $distances[$xref] <= $max_distance) {
$detailed[$xref] = true;
}
}