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>
96 lines
3.4 KiB
C#
96 lines
3.4 KiB
C#
using System.Text.Json.Serialization;
|
|
|
|
namespace GerbilManagerWebAPI.Import
|
|
{
|
|
// ---- Source shapes (tools/import/output JSON, produced by extract.py) ----
|
|
|
|
public sealed class SourceAnimal
|
|
{
|
|
public string Id { get; set; } = "";
|
|
public string Name { get; set; } = "";
|
|
public List<string> NameVariants { get; set; } = new();
|
|
public string Dob { get; set; } = "";
|
|
public string Death { get; set; } = "";
|
|
public string? Gender { get; set; }
|
|
public string Farbschlag { get; set; } = "";
|
|
public List<string> FarbschlagVariants { get; set; } = new();
|
|
public SourceGenotype Genotype { get; set; } = new();
|
|
public string Zucht { get; set; } = "";
|
|
public List<SourceParentRef> ParentRefs { get; set; } = new();
|
|
public List<string> Photos { get; set; } = new();
|
|
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
|
|
{
|
|
public Dictionary<string, List<string>> Mapped8locus { get; set; } = new();
|
|
public string RawGenotype { get; set; } = "";
|
|
public List<string> UnmappedTokens { get; set; } = new();
|
|
}
|
|
|
|
public sealed class SourceParentRef
|
|
{
|
|
public string Name { get; set; } = "";
|
|
public string Dob { get; set; } = "";
|
|
public string RoleGuess { get; set; } = "";
|
|
}
|
|
|
|
public sealed class SourceLitterRef
|
|
{
|
|
public string LitterId { get; set; } = "";
|
|
public string Method { get; set; } = "";
|
|
public string Confidence { get; set; } = ""; // "hoch" | "niedrig" | (ambiguous => candidates)
|
|
public List<string>? Candidates { get; set; }
|
|
}
|
|
|
|
public sealed class SourceLitter
|
|
{
|
|
public string Id { get; set; } = "";
|
|
public string LitterId { get; set; } = "";
|
|
public string Date { get; set; } = "";
|
|
public string DamName { get; set; } = "";
|
|
public string SireName { get; set; } = "";
|
|
public int? TotalBorn { get; set; }
|
|
public string Zuchtnummer { get; set; } = "";
|
|
public string Note { get; set; } = "";
|
|
public List<string> Warnings { get; set; } = new();
|
|
}
|
|
|
|
// ---- Report shapes (Julian-readable: counts per category + samples) ----
|
|
|
|
public sealed record ImportReport(
|
|
bool Executed,
|
|
LitterSummary Litters,
|
|
AnimalSummary Animals,
|
|
PhotoSummary Photos,
|
|
IReadOnlyList<string> Samples,
|
|
IReadOnlyList<string> Notes);
|
|
|
|
public sealed record LitterSummary(int InSource, int Created, int AlreadyImported);
|
|
|
|
public sealed record AnimalSummary(
|
|
int InSource,
|
|
int Created,
|
|
int LinkedToLitter,
|
|
int FarbschlagMatched,
|
|
int FarbschlagUnmatched,
|
|
int AlreadyImported,
|
|
QuarantineSummary Quarantined);
|
|
|
|
public sealed record QuarantineSummary(
|
|
int Conflicts,
|
|
int Stubs,
|
|
int DateOnlyLinks,
|
|
int AmbiguousLinks,
|
|
int Total);
|
|
|
|
public sealed record PhotoSummary(int Attached, int SourceFilesMissing);
|
|
}
|