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:
@@ -27,7 +27,8 @@ namespace GerbilManagerWebAPI.Dtos
|
||||
string? ExternalRef,
|
||||
string? OriginBreeder,
|
||||
List<string> CharacterTraits,
|
||||
string? CharacterNote);
|
||||
string? CharacterNote,
|
||||
bool? IsDeaf);
|
||||
|
||||
public record LitterDto(
|
||||
Guid Id,
|
||||
@@ -75,7 +76,8 @@ namespace GerbilManagerWebAPI.Dtos
|
||||
string? ExternalRef,
|
||||
string? OriginBreeder,
|
||||
List<string>? CharacterTraits,
|
||||
string? CharacterNote);
|
||||
string? CharacterNote,
|
||||
bool? IsDeaf);
|
||||
|
||||
public record LitterInput(
|
||||
string Name,
|
||||
|
||||
@@ -97,12 +97,13 @@ namespace GerbilManagerWebAPI.Endpoints
|
||||
g.OriginBreeder = i.OriginBreeder;
|
||||
g.CharacterTraits = i.CharacterTraits ?? new List<string>();
|
||||
g.CharacterNote = i.CharacterNote;
|
||||
g.IsDeaf = i.IsDeaf;
|
||||
}
|
||||
|
||||
internal static GerbilDto ToDto(Gerbil g) => new(
|
||||
g.Id, g.Name, g.Gender, g.Status, g.LitterId, g.OriginContactId, g.ReceiverContactId,
|
||||
g.EnclosureId, g.ColorVarietyId, g.DateOfBirth, g.DateOfDeath, g.CauseOfDeath,
|
||||
g.GoHomeDate, g.Genotype, g.Notes, g.ImportSource, g.ExternalRef, g.OriginBreeder,
|
||||
g.CharacterTraits, g.CharacterNote);
|
||||
g.CharacterTraits, g.CharacterNote, g.IsDeaf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ namespace GerbilManagerWebAPI.Import
|
||||
public List<string> SourceFiles { get; set; } = new();
|
||||
public bool Conflict { get; set; }
|
||||
public SourceLitterRef? LitterRef { get; set; }
|
||||
|
||||
// GEN-3b normalization: hearing/deaf phenotype flag (null = not stated) and
|
||||
// provenance/breeding tags (WFNZ/RV/GV/DP) — neither is genotype.
|
||||
public bool? Deaf { get; set; }
|
||||
public List<string> Tags { get; set; } = new();
|
||||
}
|
||||
|
||||
public sealed class SourceGenotype
|
||||
|
||||
@@ -174,6 +174,7 @@ namespace GerbilManagerWebAPI.Import
|
||||
LitterId = litterId,
|
||||
ColorVarietyId = colorVarietyId,
|
||||
Genotype = ComposeGenotype(a.Genotype),
|
||||
IsDeaf = a.Deaf,
|
||||
ImportSource = ImportSourceTag,
|
||||
ExternalRef = a.Id,
|
||||
OriginBreeder = string.IsNullOrWhiteSpace(a.Zucht) ? null : a.Zucht.Trim(),
|
||||
@@ -181,6 +182,11 @@ namespace GerbilManagerWebAPI.Import
|
||||
{
|
||||
a.Genotype.RawGenotype,
|
||||
a.Genotype.UnmappedTokens,
|
||||
// GEN-3b: Sls (2nd spotting locus) preserved here until Kevin's GEN-3a
|
||||
// parser adopts it into the compact Genotype contract; tags + deaf too.
|
||||
Sls = a.Genotype.Mapped8locus.TryGetValue("Sls", out var sls) ? sls : null,
|
||||
a.Tags,
|
||||
a.Deaf,
|
||||
a.Zucht,
|
||||
a.SourceFiles,
|
||||
FarbschlagRaw = a.Farbschlag,
|
||||
|
||||
1381
GerbilManagerWebAPI/Migrations/20260606082045_AddGerbilDeafFlag.Designer.cs
generated
Normal file
1381
GerbilManagerWebAPI/Migrations/20260606082045_AddGerbilDeafFlag.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace GerbilManagerWebAPI.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddGerbilDeafFlag : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsDeaf",
|
||||
table: "Gerbils",
|
||||
type: "boolean",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsDeaf",
|
||||
table: "Gerbils");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -781,6 +781,9 @@ namespace GerbilManagerWebAPI.Migrations
|
||||
b.Property<string>("ImportSource")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool?>("IsDeaf")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<Guid?>("LitterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
|
||||
@@ -67,6 +67,12 @@ namespace GerbilManagerWebAPI.Models
|
||||
|
||||
/// <summary>FEAT-14: free-text character note; feeds the AI Verkaufstext.</summary>
|
||||
public string? CharacterNote { get; set; }
|
||||
|
||||
/// <summary>GEN-3b: hearing/deaf phenotype flag (NOT a genotype locus — it's the
|
||||
/// downstream effect of high white load / Sp×Sls). null = not stated, true = deaf
|
||||
/// (dea/taub), false = hearing (Dea/hörend). Set by the FEAT-8 import from the
|
||||
/// after-spsp deafness annotation; see hive/agents/god/GENETIK-notation.md.</summary>
|
||||
public bool? IsDeaf { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Shared normalisation for the separator-insensitive name search.</summary>
|
||||
|
||||
Reference in New Issue
Block a user