GEN-3b: import notation normalization (Uw=G, Sls, deaf flag, tags)
genotype.py: - Uw/uw aliased to G/g (same locus) so the D2 conflict group + pure-Uw cases stop being conflicts (Gg == Uwuw). - Sls/WP recognized as a SECOND spotting locus (S(l)s(l)=WP het); carried into mapped8locus alongside Sp (Sp+Sls = Superschecke). - dea/Dea/taub/hörend -> hearing/deaf phenotype FLAG (not a locus). - WFNZ/RV/GV/DP -> provenance/breeding tags (not genotype, not conflicts). - test_genotype.py: zero-dep unit tests for all four. extract.py: surface deaf+tags on animals; dedup conflict detection now compares the NORMALIZED genotype key (mapped8locus) instead of the raw string, so Uw=G no longer triggers a conflict. Result: Konflikte 32 -> 27, Zucht-Splits stays 0. Dedup identity = name + DOB + Zucht. Backend: Gerbil.IsDeaf (bool?) + additive migration AddGerbilDeafFlag (has-pending-model-changes clean) + GerbilDto/GerbilInput round-trip. ImportService sets IsDeaf from animal.deaf and preserves Sls + tags + deaf in RawImportData (kept out of the 8-locus compact Genotype contract until GEN-3a adopts them). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,23 @@ namespace GerbilManager.Tests
|
||||
Assert.Equal(2, await db.Litters.CountAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Execute_persists_deaf_flag_and_preserves_sls_and_tags()
|
||||
{
|
||||
using var db = NewDb();
|
||||
await new ImportService(db, _dir, _dir).RunAsync(execute: true);
|
||||
|
||||
var a1 = await db.Gerbils.SingleAsync(g => g.ExternalRef == "a1");
|
||||
// GEN-3b: deafness is a persisted phenotype flag (NOT a genotype locus).
|
||||
Assert.True(a1.IsDeaf);
|
||||
// Sls (2nd spotting locus) + provenance tags are preserved in RawImportData
|
||||
// (kept out of the 8-locus compact Genotype contract until GEN-3a adopts them).
|
||||
Assert.Contains("Sls", a1.RawImportData!);
|
||||
Assert.Contains("WFNZ", a1.RawImportData!);
|
||||
// and Sls must NOT leak into the compact 8-locus genotype string
|
||||
Assert.DoesNotContain("Sl", a1.Genotype!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComposeGenotype_strips_carets_and_fills_missing_loci()
|
||||
{
|
||||
@@ -139,7 +156,8 @@ namespace GerbilManager.Tests
|
||||
[
|
||||
{"id":"a1","name":"Kind Eins","dob":"01.02.2020","death":"","gender":null,
|
||||
"farbschlag":"Agouti","farbschlagVariants":["Agouti"],
|
||||
"genotype":{"mapped8locus":{"A":["a","a"],"C":["C","C"],"D":["D","?"],"E":["e","e^f"]},"rawGenotype":"aa CC D- ee[f]","unmappedTokens":[]},
|
||||
"genotype":{"mapped8locus":{"A":["a","a"],"C":["C","C"],"D":["D","?"],"E":["e","e^f"],"Sls":["Sl","sl"]},"rawGenotype":"aa CC D- ee[f] WP dea WFNZ","unmappedTokens":[]},
|
||||
"deaf":true,"tags":["WFNZ"],
|
||||
"zucht":"","parentRefs":[],"photos":[],"sourceFiles":["f1"],"conflict":false,
|
||||
"litterRef":{"litterId":"L1","method":"geburtsdatum+eltern","confidence":"hoch"}},
|
||||
{"id":"a2","name":"Streit","dob":"01.01.2019","death":"","gender":null,
|
||||
|
||||
Reference in New Issue
Block a user