diff --git a/.claude/skills/ticket-triage/SKILL.md b/.claude/skills/ticket-triage/SKILL.md index 2591b38..c835aa6 100644 --- a/.claude/skills/ticket-triage/SKILL.md +++ b/.claude/skills/ticket-triage/SKILL.md @@ -35,6 +35,39 @@ wartet auf die Züchterin, `Resolved` ist fertig — beide überspringen. - Feedback überlebt den Import-Ingest (FK-freie Spalten) — Tickets bleiben erhalten, auch wenn die DB neu befüllt wird. +## Ziel-System: lokal ODER Prod (WICHTIG — zuerst klären) + +Die Tickets liegen dort, wo die Züchterin sie meldet — im Regelfall **Prod** +(TrueNAS Custom-App). Kläre zu Beginn, gegen welches System der Lauf geht, und +setze die **API-Base-URL** entsprechend für ALLE `feedback`/`gerbils`/`litters`-Aufrufe: + +| System | API-Base | Bookmark der Züchterin | +|---|---|---| +| **Prod** (Regelfall) | `http://truenas:8090/api` | http://truenas:8090/ | +| **Lokal** (Aspire) | `http://localhost:5179` | — (App via `dotnet run --project GerbilManager.AppHost`) | + +- **Ticket-CRUD läuft immer gegen das Ziel-System** (Prod: `…/api/feedback`). + Einzelticket: `GET /feedback/{id}`; nur die offenen: `GET /feedback?status=Open,Answered` + (spart den 2-MB-Volldump). Encoding: über Datei holen + Python-UTF-8 lesen. +- **Untersuchung bleibt lokal**: die Quellen (Stammbaum-xlsx, `_rpro3.db`, Wurfchronik, + `output/*.json`) liegen lokal. Datenfixes gehören re-ingest-stabil in + `conflict-decisions.json` / `rpro3-decisions.json` — NICHT die Live-DB patchen. +- **Prod-Re-Ingest ohne SSH** (bevorzugt): lokal `resolved_import.json` generieren + (extract → extract_contracts → merge_and_resolve), dann **per HTTP hochladen + ingesten**: + ```bash + curl -X POST http://truenas:8090/api/import/ingest-resolved/upload \ + -F "resolved=@tools/import/output/resolved_import.json" \ + [-F "photos=@neue_fotos.zip"] # photos optional, nur bei neuen Fotos + ``` + Der Endpoint staget die Datei serverseitig und ruft denselben UPSERT-Ingest auf + (manuell/verified/feedback überleben). **DB-Copy vom lokalen Stand ist VERBOTEN**, + sobald Prod eigenständig kuratiert wurde — es überschreibt die Prod-Kuratierung; + nur der Upsert-Ingest bewahrt sie. (Fallback ohne Endpoint: `resolved_import.json` + per `scp` + `docker cp` nach `ix-gerbilmanager-api-1:/tools/import/output/` und + `POST …/api/import/ingest-resolved`.) +- Vor jeder **Prod-Mutation** (Ingest, Ticket-Schließen im großen Stil) kurz beim + Nutzer rückversichern. + ## Ablauf ### 1. Tickets holen & einteilen diff --git a/CLAUDE.md b/CLAUDE.md index 37ed659..08e4337 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -110,7 +110,15 @@ python tools/import/extract_contracts.py python tools/import/merge_and_resolve.py # 4. In die laufende DB laden (API muss laufen): UPSERT + Freeze curl -X POST http://localhost:5179/import/ingest-resolved +# 4b. Gegen PROD (ohne SSH): resolved_import.json per HTTP hochladen + ingesten +curl -X POST http://truenas:8090/api/import/ingest-resolved/upload \ + -F "resolved=@tools/import/output/resolved_import.json" # [-F "photos=@neue_fotos.zip"] nur bei neuen Fotos ``` +- **`POST /import/ingest-resolved/upload`** (multipart `resolved` + optional `photos`-ZIP): staged die + hochgeladene Datei serverseitig in `Import:SourcePath` und ruft denselben UPSERT-Ingest — damit läuft + der Import **gegen Prod ohne SSH/`docker cp`**. Der alte `POST /import/ingest-resolved` (liest die + serverseitige Datei) bleibt unverändert. **DB-Copy vom lokalen Stand ist verboten**, sobald Prod + kuratiert wurde — nur der Upsert-Ingest bewahrt manuelle/verified/feedback-Zeilen. - **`IngestResolvedService` (seit 2026-07-12: UPSERT statt Total-Wipe).** Importierte Zeilen (Gerbils/Litters/Contacts) werden **per deterministischer Id gematcht und in place @@ -284,6 +292,18 @@ python test_extract.py test_extract_docx.py test_genotype.py test_merge_resol (`WuerfeListPage`, Filter `showInChronicle==true`) aus, zeigt ihn aber weiter auf der Tier-Akte (parentLitters-Query, ungefiltert). Use-Case: extern entstandener Wurf eines nicht-residenten Tieres (z. B. Akane × Bonaparte). +- **Über-gemergte Würfe korrigieren**: `conflict-decisions.json` kennt **`litterChildren`** + [{date, matchChild, keep[], renameTo?, note}] — autoritative Kinder-Whitelist eines Wurfs + (später Schritt in `merge_and_resolve.py` nach `litterOrder`). Match per `date` + `matchChild` + (ein aktuell zugeordnetes Kind, disambiguiert gleich-datierte Würfe); Kinder außerhalb `keep` + werden aus dem Wurf gelöst (`LitterId=None`, Tier bleibt erhalten), optional `renameTo` setzt + den kanonischen Wurf-Namen. Use-Case: Wurfchronik-Abnehmer-Sammelseiten, die fälschlich in einen + datierten Buchstaben-Wurf gemergt wurden (Tickets 4b9f49fb M-Wurf, 8d259edf Gale). +- **`renameTo` ist eltern-verknüpfungssicher**: umbenannte Tiere werden im Eltern-Namensindex + zusätzlich unter ihrem **Quell-Namen** (`_pre_rename_name`) geführt, sonst verlieren Würfe, die + den alten Namen referenzieren, ihren Eltern-Link (Ticket e0a0c304 Kruke→Kuke). `dateOfDeath` + ist als Resolution-Override unterstützt (setzt Todesdatum + Status Deceased; rpro3 hat keine + Sterbespalte). ## Verifikations-Checkliste vor „fertig" diff --git a/GerbilManager.Tests/ApiFactory.cs b/GerbilManager.Tests/ApiFactory.cs index 94aa793..c36e671 100644 --- a/GerbilManager.Tests/ApiFactory.cs +++ b/GerbilManager.Tests/ApiFactory.cs @@ -29,6 +29,13 @@ public sealed class ApiFactory : WebApplicationFactory public string ContractRoot { get; } = Path.Combine(Path.GetTempPath(), $"gerbil-contract-tests-{Guid.NewGuid():N}"); + /// Optionaler Isolations-Override für den Import-Quellordner (Import:SourcePath), + /// damit der Upload-Ingest-Test in einem Temp-Verzeichnis arbeitet statt im echten Repo. + public string? ImportSourcePath { get; init; } + + /// Optionaler Isolations-Override für den Foto-Ordner (Photos:RootPath). + public string? PhotosRootPath { get; init; } + protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.UseEnvironment("Testing"); @@ -37,6 +44,8 @@ public sealed class ApiFactory : WebApplicationFactory builder.UseSetting("ConnectionStrings:gerbilmanager", "Host=localhost;Database=test;Username=test;Password=test"); builder.UseSetting("Contracts:RootPath", ContractRoot); + if (ImportSourcePath is not null) builder.UseSetting("Import:SourcePath", ImportSourcePath); + if (PhotosRootPath is not null) builder.UseSetting("Photos:RootPath", PhotosRootPath); builder.ConfigureServices(services => { diff --git a/GerbilManager.Tests/FeedbackEndpointTests.cs b/GerbilManager.Tests/FeedbackEndpointTests.cs index 965b121..93caab7 100644 --- a/GerbilManager.Tests/FeedbackEndpointTests.cs +++ b/GerbilManager.Tests/FeedbackEndpointTests.cs @@ -500,6 +500,38 @@ public class FeedbackEndpointTests : IClassFixture } } + [Fact] + public async Task Get_single_feedback_by_id_and_status_filter() + { + var client = _factory.CreateClient(); + + // Zwei Tickets mit unterscheidbaren Namen anlegen. + var open = await client.PostAsJsonAsync("/feedback", new { message = "Offenes Ticket", context = "gerbil-detail", entityName = "FilterOpen" }); + var openId = JsonDocument.Parse(await open.Content.ReadAsStringAsync()).RootElement.GetProperty("id").GetString()!; + var other = await client.PostAsJsonAsync("/feedback", new { message = "Zweites Ticket", context = "gerbil-detail", entityName = "FilterOther" }); + var otherId = JsonDocument.Parse(await other.Content.ReadAsStringAsync()).RootElement.GetProperty("id").GetString()!; + + // Einzel-GET liefert genau dieses Ticket (mit attachments-Feld). + var single = JsonDocument.Parse(await client.GetStringAsync($"/feedback/{openId}")).RootElement; + Assert.Equal("FilterOpen", single.GetProperty("entityName").GetString()); + Assert.Equal(JsonValueKind.Array, single.GetProperty("attachments").ValueKind); + + // Unbekannte Id -> 404. + Assert.Equal(HttpStatusCode.NotFound, (await client.GetAsync($"/feedback/{Guid.NewGuid()}")).StatusCode); + + // Zweites Ticket auf Answered setzen, dann Status-Filter prüfen. + await client.PutAsJsonAsync($"/feedback/{otherId}", new { status = "Answered" }); + var answered = JsonDocument.Parse(await client.GetStringAsync("/feedback?status=Answered")).RootElement; + Assert.Contains(answered.EnumerateArray(), f => f.GetProperty("id").GetString() == otherId); + Assert.DoesNotContain(answered.EnumerateArray(), f => f.GetProperty("id").GetString() == openId); + + // Kombinierter Filter (Open,Answered) enthält beide. + var both = JsonDocument.Parse(await client.GetStringAsync("/feedback?status=Open,Answered")).RootElement; + var ids = both.EnumerateArray().Select(f => f.GetProperty("id").GetString()).ToHashSet(); + Assert.Contains(openId, ids); + Assert.Contains(otherId, ids); + } + private static object Animal(Guid id, string name, string gender, Guid contactId) => new { Id = id, diff --git a/GerbilManager.Tests/ImportIngestUploadEndpointTests.cs b/GerbilManager.Tests/ImportIngestUploadEndpointTests.cs new file mode 100644 index 0000000..a8501f2 --- /dev/null +++ b/GerbilManager.Tests/ImportIngestUploadEndpointTests.cs @@ -0,0 +1,61 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Text; + +namespace GerbilManager.Tests; + +/// +/// POST /import/ingest-resolved/upload — Upload-Variante des Ingests: eine hochgeladene +/// resolved_import.json wird serverseitig in den Import-Quellordner gestaged und direkt ingested. +/// Damit lässt sich der Import gegen PROD fahren, ohne die Datei per SSH/docker cp in den Container +/// zu kopieren. Der Test isoliert Quell-/Fotoordner über ApiFactory in Temp-Verzeichnisse. +/// +public class ImportIngestUploadEndpointTests +{ + [Fact] + public async Task Upload_stages_resolved_json_and_runs_ingest() + { + var srcDir = Path.Combine(Path.GetTempPath(), "ingest-upload-" + Guid.NewGuid().ToString("N")); + var photoDir = Path.Combine(Path.GetTempPath(), "ingest-upload-photos-" + Guid.NewGuid().ToString("N")); + using var factory = new ApiFactory { ImportSourcePath = srcDir, PhotosRootPath = photoDir }; + var client = factory.CreateClient(); + + // Minimaler, aber valider Payload (leere Sammlungen) — genügt, um Staging + Ingest-Lauf zu prüfen. + const string json = "{\"Gerbils\":[],\"Litters\":[],\"Contacts\":[],\"SaleContracts\":[],\"GerbilPhotos\":[]}"; + + using var form = new MultipartFormDataContent(); + var file = new ByteArrayContent(Encoding.UTF8.GetBytes(json)); + file.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + form.Add(file, "resolved", "resolved_import.json"); + + var resp = await client.PostAsync("/import/ingest-resolved/upload", form); + Assert.Equal(HttpStatusCode.OK, resp.StatusCode); + + var body = await resp.Content.ReadAsStringAsync(); + Assert.DoesNotContain("Error:", body); // Ingest lief, keine "file not found"-Fehlermeldung + + // Die hochgeladene Datei wurde an den erwarteten Ort gestaged. + Assert.True(File.Exists(Path.Combine(srcDir, "resolved_import.json"))); + + try { Directory.Delete(srcDir, recursive: true); } catch { /* best effort */ } + try { Directory.Delete(photoDir, recursive: true); } catch { /* best effort */ } + } + + [Fact] + public async Task Upload_rejects_empty_file_with_400() + { + var srcDir = Path.Combine(Path.GetTempPath(), "ingest-upload-" + Guid.NewGuid().ToString("N")); + using var factory = new ApiFactory { ImportSourcePath = srcDir }; + var client = factory.CreateClient(); + + using var form = new MultipartFormDataContent(); + var file = new ByteArrayContent(Array.Empty()); + file.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + form.Add(file, "resolved", "resolved_import.json"); + + var resp = await client.PostAsync("/import/ingest-resolved/upload", form); + Assert.Equal(HttpStatusCode.BadRequest, resp.StatusCode); + + try { Directory.Delete(srcDir, recursive: true); } catch { /* best effort */ } + } +} diff --git a/GerbilManager.Tests/IngestResolvedServiceTests.cs b/GerbilManager.Tests/IngestResolvedServiceTests.cs index 0e526cc..45a1a20 100644 --- a/GerbilManager.Tests/IngestResolvedServiceTests.cs +++ b/GerbilManager.Tests/IngestResolvedServiceTests.cs @@ -257,5 +257,34 @@ namespace GerbilManager.Tests Assert.Equal(ContractId, contract.Id); Assert.Single(contract.Animals); } + + [Fact] + public async Task IngestResolved_rederives_status_after_deceased_override() + { + // Ticket 37ab228a "Gaida": ein verstorbenes Tier blieb nach dem Ingest auf Status + // 'Breeding' hängen, weil der Freeze das Todesdatum aus dem Override zurückschrieb, + // ohne den Status neu abzuleiten. Der Ingest muss GerbilStatusService.Apply erneut aufrufen. + using var db = NewDb(); + var config = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { { "Import:SourcePath", _dir } }) + .Build(); + + db.GerbilOverrides.Add(new GerbilOverride + { + Id = Guid.NewGuid(), + GerbilId = FatherId, + EntityName = "Papa", + IsVerified = false, // "geschützt" — wird trotzdem angewandt + OverrideJson = "{\"dateOfDeath\":\"2025-10-09\"}", + UpdatedAt = DateTimeOffset.UtcNow, + }); + await db.SaveChangesAsync(); + + await new IngestResolvedService(db, config, null!).RunAsync(); + + var father = await db.Gerbils.SingleAsync(g => g.Id == FatherId); + Assert.NotNull(father.DateOfDeath); // Todesdatum aus Override angewandt + Assert.Equal(GerbilStatus.Deceased, father.Status); // ... und Status daraus abgeleitet + } } } diff --git a/GerbilManagerWebAPI/Endpoints/FeedbackEndpoints.cs b/GerbilManagerWebAPI/Endpoints/FeedbackEndpoints.cs index f075a93..e8f3f01 100644 --- a/GerbilManagerWebAPI/Endpoints/FeedbackEndpoints.cs +++ b/GerbilManagerWebAPI/Endpoints/FeedbackEndpoints.cs @@ -55,12 +55,21 @@ namespace GerbilManagerWebAPI.Endpoints return TypedResults.Created($"/feedback/{entity.Id}", ToDto(entity)); }); - group.MapGet("/", async (ApplicationContext db) => + group.MapGet("/", async (ApplicationContext db, string? status) => { // In memory verarbeiten: SQLite (Test-Host) kann weder ORDER BY noch WHERE-Vergleiche // auf DateTimeOffset-Spalten übersetzen. var rows = await db.Feedback.ToListAsync(); + // Optionaler Status-Filter (?status=Open,Answered) — kommagetrennt, case-insensitiv. + // Spart der Triage den 2-MB-Volldump; soft-gelöschte Tickets werden dann ausgeblendet. + if (!string.IsNullOrWhiteSpace(status)) + { + var wanted = status.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + rows = rows.Where(f => f.DeletedAt is null && wanted.Contains(f.Status)).ToList(); + } + // Aufräumen: soft-gelöschte Tickets werden nach TrashRetentionDays endgültig entfernt // (lazy beim Abruf — genügt für Single-User, kein Hintergrunddienst nötig). var cutoff = DateTimeOffset.UtcNow.AddDays(-TrashRetentionDays); @@ -90,6 +99,22 @@ namespace GerbilManagerWebAPI.Endpoints .ToList()); }); + // Einzelnes Ticket (inkl. Anhang-Metadaten). Praktisch für die Triage, um ein Ticket + // gezielt zu laden, statt die ganze Liste zu ziehen. 404 bei unbekannter Id. + group.MapGet("/{id:guid}", async Task, NotFound>> ( + Guid id, ApplicationContext db) => + { + var entity = await db.Feedback.FirstOrDefaultAsync(f => f.Id == id); + if (entity is null) + return TypedResults.NotFound(); + + var atts = await db.FeedbackAttachments + .Where(a => a.FeedbackId == id) + .Select(a => new FeedbackAttachmentDto(a.Id, a.FileName, a.ContentType, a.Size)) + .ToListAsync(); + return TypedResults.Ok(ToDto(entity, atts)); + }); + group.MapPut("/{id:guid}", async Task, NotFound, BadRequest>> ( Guid id, FeedbackUpdate input, ApplicationContext db, Push.PushNotifier push) => { diff --git a/GerbilManagerWebAPI/Endpoints/ImportEndpoints.cs b/GerbilManagerWebAPI/Endpoints/ImportEndpoints.cs index 623c74c..210d3f2 100644 --- a/GerbilManagerWebAPI/Endpoints/ImportEndpoints.cs +++ b/GerbilManagerWebAPI/Endpoints/ImportEndpoints.cs @@ -44,6 +44,39 @@ namespace GerbilManagerWebAPI.Endpoints return TypedResults.Ok(result); }); + // Upload-Variante: lädt eine resolved_import.json (+ optional Fotos-ZIP) hoch, legt sie in + // dem Verzeichnis ab, aus dem der Ingest liest, und ingested direkt. Damit lässt sich der + // Import gegen PROD fahren, OHNE die Datei per SSH/docker cp in den Container zu kopieren + // (der lokal generierte Payload geht per HTTP an die Prod-API). Abwärtskompatibel — der + // bestehende /ingest-resolved-Endpoint bleibt unverändert. + group.MapPost("/ingest-resolved/upload", async Task, BadRequest>> ( + IFormFile resolved, IFormFile? photos, + ApplicationContext db, IConfiguration config, IWebHostEnvironment env) => + { + if (resolved.Length == 0) + return TypedResults.BadRequest("resolved_import.json ist leer."); + + var sourceDir = IngestResolvedService.ResolveSourceDir(config, env); + Directory.CreateDirectory(sourceDir); + + // resolved_import.json an den erwarteten Ort schreiben. + var destPath = Path.Combine(sourceDir, "resolved_import.json"); + await using (var fs = File.Create(destPath)) + await resolved.CopyToAsync(fs); + + // Optionale Fotos-ZIP in den Source-Dir entpacken (Einträge relativ), damit der + // Foto-Kopierschritt (Path.Combine(sourceDir, relPath)) neue Fotos findet. + if (photos is not null) + { + await using var zs = photos.OpenReadStream(); + using var seekable = await ToSeekable(zs); + ExtractZipInto(seekable, sourceDir); + } + + var result = await new IngestResolvedService(db, config, env).RunAsync(); + return TypedResults.Ok(result); + }).WithTags("Import").DisableAntiforgery(); + // ── RPRO3 ────────────────────────────────────────────────────────── group.MapPost("/rpro3/analyze", async Task, BadRequest>> ( IFormFile backup, IFormFile? images, @@ -143,6 +176,25 @@ namespace GerbilManagerWebAPI.Endpoints return map; } + // Entpackt ein ZIP relativ in destDir (Einträge behalten ihren relativen Pfad). Schützt vor + // Zip-Slip (Einträge, die aus destDir ausbrechen wollen) und überspringt Verzeichnis-Einträge. + private static void ExtractZipInto(Stream zip, string destDir) + { + var root = Path.GetFullPath(destDir); + using var archive = new ZipArchive(zip, ZipArchiveMode.Read, leaveOpen: true); + foreach (var e in archive.Entries) + { + if (string.IsNullOrEmpty(e.Name)) continue; // Verzeichnis-Eintrag + var target = Path.GetFullPath(Path.Combine(root, e.FullName)); + if (!target.StartsWith(root + Path.DirectorySeparatorChar, StringComparison.Ordinal) + && !string.Equals(target, root, StringComparison.Ordinal)) + continue; // Zip-Slip abwehren + Directory.CreateDirectory(Path.GetDirectoryName(target)!); + try { e.ExtractToFile(target, overwrite: true); } + catch { /* defektes Archiv-Entry überspringen */ } + } + } + private static void TryDeleteDir(string dir) { try { if (Directory.Exists(dir)) Directory.Delete(dir, recursive: true); } diff --git a/GerbilManagerWebAPI/Import/IngestResolvedService.cs b/GerbilManagerWebAPI/Import/IngestResolvedService.cs index 8172a3c..d871de2 100644 --- a/GerbilManagerWebAPI/Import/IngestResolvedService.cs +++ b/GerbilManagerWebAPI/Import/IngestResolvedService.cs @@ -18,12 +18,23 @@ namespace GerbilManagerWebAPI.Import public IngestResolvedService(ApplicationContext db, IConfiguration config, IWebHostEnvironment? env) { _db = db; - var contentRoot = env?.ContentRootPath ?? Directory.GetCurrentDirectory(); - _sourceDir = config["Import:SourcePath"] - ?? Path.GetFullPath(Path.Combine(contentRoot, "..", "tools", "import", "output")); + _sourceDir = ResolveSourceDir(config, env); _resolvedJsonPath = Path.Combine(_sourceDir, "resolved_import.json"); _photoRoot = config["Photos:RootPath"] - ?? Path.Combine(contentRoot, "photo-storage"); + ?? Path.Combine(env?.ContentRootPath ?? Directory.GetCurrentDirectory(), "photo-storage"); + } + + /// + /// Verzeichnis, in dem der Ingest resolved_import.json (+ referenzierte Fotos) erwartet. + /// Öffentlich, damit der Upload-Endpoint die hochgeladene Datei an genau denselben Ort stagen kann + /// (siehe POST /import/ingest-resolved/upload) — so lässt sich der Ingest gegen Prod fahren, + /// ohne die Datei per SSH/docker cp in den Container zu kopieren. + /// + public static string ResolveSourceDir(IConfiguration config, IWebHostEnvironment? env) + { + var contentRoot = env?.ContentRootPath ?? Directory.GetCurrentDirectory(); + return config["Import:SourcePath"] + ?? Path.GetFullPath(Path.Combine(contentRoot, "..", "tools", "import", "output")); } public async Task RunAsync() @@ -313,6 +324,10 @@ namespace GerbilManagerWebAPI.Import if (gerbilsById.TryGetValue(ov.GerbilId, out var gg)) { GerbilSnapshotService.ApplyOverride(gg, ov.OverrideJson); + // Der Override kann Todesdatum/Abgabe/Geburtsdatum setzen; Status neu ableiten, + // damit ein verstorbenes/abgegebenes Tier nicht als 'Zucht' hängen bleibt + // (Ticket 37ab228a "Gaida"). Status/Gehege sind selbst NICHT eingefroren. + GerbilStatusService.Apply(gg, DateOnly.FromDateTime(DateTime.UtcNow)); overridesApplied++; } } diff --git a/GerbilManagerWebAPI/Program.cs b/GerbilManagerWebAPI/Program.cs index d6f85d1..30f6363 100644 --- a/GerbilManagerWebAPI/Program.cs +++ b/GerbilManagerWebAPI/Program.cs @@ -90,20 +90,24 @@ if (!app.Environment.IsEnvironment("Testing")) var db = scope.ServiceProvider.GetRequiredService(); db.Database.Migrate(); - // Startup sweep: derive status for animals that silently crossed the 7-year threshold - // since the last write. No-op if all statuses are already current. + // Startup sweep: heal any stored status that disagrees with the derived status — animals + // that silently crossed the 7-year threshold (→ Deceased), or whose death date / Abgabe was + // set on a path that didn't re-derive the status (e.g. a frozen GerbilOverride re-applied after + // ingest — Ticket 37ab228a "Gaida"). Only currently-active animals (Breeding/Pet/ForSale) are + // considered, so nobody gets un-deceased. No-op if all statuses are already current. var today = DateOnly.FromDateTime(DateTime.UtcNow); var candidates = await db.Gerbils - .Where(g => g.Status != GerbilStatus.Deceased && g.Status != GerbilStatus.GivenAway - && g.DateOfDeath == null && g.ReceiverContactId == null - && g.DateOfBirth != null - && g.DateOfBirth < today.AddYears(-GerbilStatusService.MaxAgeYears)) + .Where(g => g.Status != GerbilStatus.Deceased && g.Status != GerbilStatus.GivenAway) .ToListAsync(); - if (candidates.Count > 0) + var healed = 0; + foreach (var g in candidates) { - foreach (var g in candidates) g.Status = GerbilStatus.Deceased; - await db.SaveChangesAsync(); + var derived = GerbilStatusService.Derive(g.Status, g.DateOfBirth, g.DateOfDeath, + isAbgegeben: g.ReceiverContactId is not null, today); + if (derived != g.Status) { g.Status = derived; healed++; } } + if (healed > 0) + await db.SaveChangesAsync(); } app.UseCors(LanCorsPolicy); diff --git a/docs/ticket-triage.md b/docs/ticket-triage.md index b966410..30665b8 100644 --- a/docs/ticket-triage.md +++ b/docs/ticket-triage.md @@ -1,406 +1,55 @@ -# Ticket-Triage — Matrix - -Stand: automatischer Triage-Lauf. 74 von 77 Tickets ausgewertet, 3 ohne Ergebnis (nachzutriagieren). - -- Rückfrage gestellt (wartet auf Züchterin): **25** -- An Umsetzungs-Cluster übergeben: **49** -- fixLayer-Verteilung: {'import-data': 24, 'import-logic': 23, 'frontend': 7, 'needs-info': 19, 'backend': 1} - -## Cluster (nach overlapKey) - -### rpro3-uebernahme (5 Ticket(s) · import-data, import-logic, needs-info) -Dateien: GerbilManagerWebAPI/Endpoints/AcquisitionEndpoints.cs, GerbilManagerWebAPI/Import/Rpro3/Rpro3Dedup.cs, GerbilManagerWebAPI/Import/Rpro3/Rpro3ImportService.cs, GerbilManagerWebAPI/Import/Rpro3/rpro3-decisions.json, GerbilManagerWebAPI/Models/AcquisitionRecord.cs, tools/import/_rpro3.db, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py, tools/import/rpro3_lookup.py -- [Cluster] **Jana: Daten aus RennmausPro übernehmen (Herkunft Clan of little Longnoses)** (Jana, S/high) — Resolution in tools/import/conflict-decisions.json (per externalRef ...adb0649c... oder name-only "Jana") mit originBreeder="Clan of little longnoses", correctDob="07.12.2009", gender="female", genotype="aa c[chm]c[h] DD ee[f] Gg Pp spsp", farbschlag="Colourpoint-Kohlfuchsschimme - `id=4e7e86bc-c7fc-498b-be5a-1cf019a5768e` -- [Cluster] **RennmausPro-Daten + Fotos für Blacky × Kuke Nachkommen übernehmen** (Domi, L/medium) — rpro3-Übernahme im Cluster umsetzen: rids sicher zuordnen (Blacky=1, Kuke=2, Alvin=3, Eddy=34, Flori=143, Feivel per Namen noch suchen), rpro3-decisions.json um Merge/Farbe/Genotyp/DOB/Herkunft ergänzen, Kruke/Kuke-Namensmerge klären, prüfen welche DB-Tiere den 4 rpro3-Kindern en - `id=ddacd093-b3f4-419f-911d-39ef9e746ae3` -- [Cluster] **RennmausPro-Gesundheitsdaten und Gewichte für Blacky (und alle Tiere) übernehmen** (Blacky von PZ Seligenstadt, L/medium) — Im rpro3-Import (Rpro3ImportService.cs, BuildHealthAndWeights / GerbilIdOfRid) die Weights/Health/Fotos auf das per Identität gematchte, bereits existierende Stammbaum-Gerbil abbilden statt auf ein neues rpro3-Gerbil (Reconciliation rid↔vorhandene Gerbil-Id, analog Ahnen-Merge f3 - `id=2bc311df-2f1e-400c-8fa0-2468cc9c9d9b` -- [Cluster] **Blacky von PZ Seligenstadt: Erwerbs- und Todesdaten aus RennerPro übernehmen** (Blacky von PZ Seligenstadt, S/high) — (1) Erwerb: AcquisitionRecord für GerbilId 9b40b9e5… anlegen (Date 2009-09-01, Price 9.50, Note „aus RennmausPro III“) via POST /acquisitions – FK-frei und re-ingest-stabil, per Design aus rpro3 herktier_tb. (2) Todesdatum: 2011-07-13 → 2011-06-13 korrigieren (causeOfDeath option - `id=267474c8-bd1d-47e7-8c1f-4250ebfbf2cb` -- [RÜCKFRAGE] **Christinas Eltern/Stammbaum aus RennmausPro verknüpfen** (Christina, M/medium) — Nach Bestätigung der Züchterin: Ist es „Cristina" (fremd_tb id=6398, Eltern MID=125/PID=166), Eltern über den RennmausPro-Ahnen-Import bzw. per conflict-decisions.json (name+dob) re-ingest-stabil setzen; andernfalls die von ihr genannten Eltern-Namen als Verknüpfung eintragen. - `id=a78eaa79-db55-4da1-99b0-ed954f9b07c7` - -### jungtier-reihenfolge (4 Ticket(s) · frontend, import-logic, needs-info) -Dateien: EF-Migration, GerbilManagerWebAPI/Dtos/ApiDtos.cs, GerbilManagerWebAPI/Endpoints/GerbilEndpoints.cs, GerbilManagerWebAPI/Migrations, GerbilManagerWebAPI/Models/Gerbil.cs, GerbilManagerWebAPI/Models/Gerbil.cs (neues Order-Feld + Migration), gerbil-manager-web/src/api/types.ts, gerbil-manager-web/src/pages/WurfDetailPage.tsx, tools/import/extract.py, tools/import/merge_and_resolve.py -- [Cluster] **Jungtier-Reihenfolge im Wurf ist alphabetisch statt gewünscht (E-Wurf 2010: Eliza, Eddy)** (E-Wurf 2010, M/medium) — Generalisierbarer Fix: persistiertes, re-ingest-stabiles Order-Feld pro Jungtier (FK-frei analog GerbilOverride, da Import-Tiere) + manuelles Umsortieren in der Wurf-Akte; WurfDetailPage nach diesem Feld statt nach name sortieren. Alternativ Kind-Reihenfolge im Importer (merge_an - `id=573aad81-157e-47fe-8693-c1a4b685222b` -- [RÜCKFRAGE] **Jungtiere im Wurf falsch sortiert (alphabetisch statt Wurfchronik-Reihenfolge)** (C-Wurf 2010, L/medium) — Reihenfolge bei der Zuechterin bestaetigen (Chronik-Listenreihenfolge). Danach Import um ein Kinder-Ordinal je Gerbil erweitern, Backend-Feld plus Migration ergaenzen, WurfDetailPage orderBy von name auf das Ordinal umstellen. - `id=399dbeb2-6909-43fe-b00f-7b6a2b9fc7b2` -- [Cluster] **O-Wurf: Jungtiere alphabetisch statt in Geburtsreihenfolge** (O-Wurf, L/high) — Ein persistiertes int-Reihenfolgefeld (litterOrder/birthOrder) einfuehren: im Import (extract.py/merge_and_resolve.py) aus der Wurfchronik-Zeilenreihenfolge deterministisch je Kind setzen (re-ingest-stabil), am Gerbil-Model + Migration + Summary-DTO ausgeben, und in WurfDetailPag - `id=8ff0ebe6-159a-49ce-80ad-23c100c21056` -- [Cluster] **Jungtier-Reihenfolge im F-Wurf 2010 alphabetisch statt in Wurfchronik-Reihenfolge** (F-Wurf 2010, L/medium) — Wurfchronik-Kind-Reihenfolge im Import (extract.py/merge_and_resolve.py) als sortierbaren Index je Jungtier erfassen und persistieren (neues nullable Feld z. B. BirthOrder/LitterSequence auf Gerbil + EF-Migration), beim Merge den Chronik-Index auch für mitgemergte Stammbaum-Tiere - `id=fa73d0db-53b7-4cc5-ac2a-6cd72dafab4e` - -### extern-badge (3 Ticket(s) · import-data) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Chris: falsches "Extern"-Badge entfernen (isResident=true setzen)** (Chris, S/high) — In tools/import/conflict-decisions.json eine resolution {"name": "Chris", "dob": "08.05.2010", "isResident": true} ergänzen (Override gewinnt im Sweep, analog Echo/Elia), dann merge_and_resolve.py neu laufen lassen + POST /import/ingest-resolved. Alternativ per UI ein GerbilOverr - `id=73d99dc4-d95c-49a1-b826-ceb359d8f6cf` -- [Cluster] **Extern-Badge bei Kruke entfernen (isResident auf true)** (Kruke, S/high) — In tools/import/conflict-decisions.json den Kruke-addAnimals-Eintrag von isResident:false auf isResident:true ändern (expliziter Override gewinnt im Residenz-Sweep von merge_and_resolve.py immer), Pipeline neu laufen lassen und POST /import/ingest-resolved. Dann verschwindet das - `id=34220307-23bc-420c-80f8-d273615caffd` -- [Cluster] **Krukes Nachzuchten fehlen — fälschlich als nicht-resident (extern) eingestuft** (Kruke, S/high) — In tools/import/conflict-decisions.json den Kruke-Eintrag isResident von false auf true setzen (bzw. den false-Override entfernen — der Sweep macht sie als Elternteil eigener Würfe ohnehin resident) und neu importieren. Domänenkorrekt, da ihre Nachzucht den Clan-Namen 'von den kl - `id=b7016e5f-9985-4f17-a3f6-2048289958aa` - -### rpro3-eltern-ergaenzen (2 Ticket(s) · import-data) -Dateien: Stammbaum-Viewer (Danny-Akte), tools/import/_rpro3.db, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Dannys Eltern (Daniel × Daniela) aus RennmausPro ergänzen** (Danny von Maintaler PZ, S/high) — Eltern per Hiro-Präzedenz (Commit bbd3058) in tools/import/conflict-decisions.json ergänzen: resolutions-Eintrag auf Danny (dob 09.10.2009) mit father="Daniel"/mother="Daniela" plus addAnimals-Stubs Daniel (male, Marder-Hell, aa c[chm]c[h] DD EE GG PP spsp, Herkunft Maintaler Pri - `id=32d57d3d-8bc3-406b-9844-965f7071ded0` -- [Cluster] **Blacky umbenennen + fehlende Eltern (Snoopy und Bella) aus RennmausPro ergänzen** (Blacky von PZ Seligenstadt, S/high) — In tools/import/conflict-decisions.json (Muster Hiro/Commit bbd3058): resolutions-Eintrag für Blacky (Match via externalRef stammbaum-blackypzseligenstadt-23072009) mit renameTo="Blacky", father="Snoopy", mother="Bella"; dazu addAnimals-Stubs {Snoopy, male} und {Bella, female} al - `id=cf6e2c99-746a-46fb-93e2-8ec81ae55663` - -### resident-flag (1 Ticket(s) · import-data) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx (extern-Badge), tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Philo als Zuchttier/Bestandstier markieren (isResident)** (Philo, S/high) — Expliziten isResident=true-Override für Philo in tools/import/conflict-decisions.json aufnehmen (Resolution per name='Philo' + dob=2011-06-11). Ein expliziter isResident-Override gewinnt immer (vgl. Echo/Elia), ist re-ingest-stabil. Danach merge_and_resolve.py neu laufen + POST / - `id=58bb53f2-280c-4676-9b79-f58497be995c` - -### danny-dublette-maintal (1 Ticket(s) · import-data) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Zwei Dannys (Maintaler PZ / PZ Maintal) sind dasselbe Tier — zusammenführen** (Danny von Maintaler PZ, S/high) — Merge-/Alias-Entscheidung in tools/import/conflict-decisions.json ergänzen, damit beide Namensvarianten zu einem Tier deduppen (Ziel-Id 55ce13fc beibehalten). Genotyp-Konflikt beim Merge klären (55ce13fc: "...EE Gg Pp Spsp DP" vs 4756dac8: "...EE Uwuw[d] Pp Spsp DP"). Danach extr - `id=6f521e7c-753c-4947-b8e3-7fe81dba8b12` - -### todesursache-nachtrag (1 Ticket(s) · import-logic) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Todesursache für Danny von Maintaler PZ nachtragen** (Danny von Maintaler PZ, S/high) — Importer-Weg (re-ingest-stabil): Override-Block in merge_and_resolve.py (~Z. 3204-3309) um ein Feld deathCause/causeOfDeath erweitern (analog notes/originBreeder), das g["CauseOfDeath"] setzt und in has_ovr aufgenommen wird; dann Decision-Eintrag in conflict-decisions.json per ex - `id=42782b23-784f-4125-8002-c372de244fdf` - -### nachzucht-link-route (1 Ticket(s) · frontend) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx -- [Cluster] **Nachzucht-Links auf der Tier-Akte tot (falsche Route /tiere/)** (Danny von Maintaler PZ, S/high) — In gerbil-manager-web/src/pages/GerbilDetailPage.tsx Zeile 687 den Link-Pfad von `/tiere/${child.id}` auf `/rennmaeuse/${child.id}` ändern (Einzeiler). Danach npx tsc --noEmit und die Nachzucht-e2e-Specs prüfen; keine Datenänderung, kein Re-Ingest nötig. - `id=20a6cc0e-3798-4f3c-8452-efb32ce6dc84` - -### chris-genotyp (1 Ticket(s) · import-data) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Chris: Gencode korrigieren (Punktschecke)** (Chris, S/high) — Resolution in tools/import/conflict-decisions.json für Chris ergänzen (Match name "Chris" + dob 08.05.2010) mit "genotype":"aa Cc[-] DD Ee gg P- Spsp" (optional "farbschlag":"Punktschecke"), dann merge_and_resolve.py + POST /import/ingest-resolved. R-Locus bleibt wie in bestehend - `id=d8debbb8-5e82-4e5f-8313-209c6caae0d4` - -### wurf-foto (1 Ticket(s) · needs-info) -Dateien: GerbilManagerWebAPI/Endpoints/LitterEndpoints.cs, GerbilManagerWebAPI/Models/Litter.cs, gerbil-manager-web/src/pages/WurfDetailPage.tsx, tools/import/extract.py -- [RÜCKFRAGE] **Foto des N-Wurfs aus der Wurfchronik anzeigen — nicht vorhanden/kein Feature** (N-Wurf, L/high) — Zunächst per Rückfrage klären, ob die Züchterin irgendwo ein Wurf-Foto gespeichert hat (Ordner/Datei) oder die Jungtier-Einzelfotos meint. Falls Wurf-Fotos gewünscht: Feature bauen (Litter-Foto-Feld + Migration + Upload/Import + UI-Anzeige auf WurfDetailPage). Kein Importer-Daten - `id=97a3b4d9-4859-4b0a-8e20-2c602ec2c167` - -### rpro3-ahnen-uebernahme (1 Ticket(s) · import-data) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Stammbaum von Malina of minor diabolus aus RennerPro ergänzen** (Malina of minor diabolus, M/high) — Eltern re-ingest-stabil im Importer materialisieren: conflict-decisions.json addAnimals für Big Donat (externer Ahn, DOB 2008-05-09, Clan of Amazing (CH), isResident=false) und Skarlett B (DOB 2009-03-03, Sunny's Sun, isResident=false) + addLitters {mother:Skarlett mit motherDob - `id=ed0eef40-3ccd-4c36-899c-0cf274bab8a9` - -### totgeburt-tg-tracking (1 Ticket(s) · import-logic) -Dateien: GerbilManagerWebAPI/Dtos/ApiDtos.cs, GerbilManagerWebAPI/Models/Litter.cs, gerbil-manager-web/src/api/types.ts, gerbil-manager-web/src/pages/WurfDetailPage.tsx, gerbil-manager-web/src/pages/WurfFormPage.tsx, gerbil-manager-web/src/strings/de.ts, tools/import/merge_and_resolve.py -- [Cluster] **C-Wurf 2010: Totgeburt (TG) wird nicht angezeigt — Wurfchronik-Mortalität geht im Merge verloren** (C-Wurf 2010, M/high) — Full-Stack, re-ingest-stabil: (1) neues Litter-Feld Stillborn (int?, "Totgeburten") + EF-Migration + DTO + api/types.ts; (2) in merge_and_resolve.py stillborn→Stillborn und diedLater→DeathsWithin8Weeks aus dem Extrakt übernehmen (Fallback via survivedToGoHome); (3) WurfDetailPage - `id=f4935e41-3ec9-4a9b-895f-6a817e63d8b3` - -### abnehmer-zeile-akte (1 Ticket(s) · frontend) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx -- [Cluster] **Abnehmer-Zeile fehlt bei verstorbenen Tieren auf der Tier-Akte** (Momo, S/high) — In GerbilDetailPage.tsx Zeile 510 den Deceased-Guard entfernen und die Abnehmer-Zeile statusunabhängig zeigen, wenn ein receiverContactId vorliegt (z. B. `{g.receiverContactId && ()}`). Reine UI-Änderung, Daten sind korrekt; WurfDetailPage zeig - `id=f628ae14-0edb-4120-a84f-0da1fe054b0a` - -### rennmauspro-eltern-ergaenzen (1 Ticket(s) · import-data) -Dateien: Gerbil Charlie (7d301ab5-6043-5d6f-914b-7550f5b0154e), Gerbil Dante (93457716-65d2-5432-a52a-553ae873ca6d), Wurf C-Wurf 2010 (f1fece40-f31a-5757-b89c-402fa47abbd4), tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Charlie: Mutter Evy fehlt (RennmausPro-Ahnin nicht importiert)** (Charlie von den Kleinen Chaoten, S/high) — Wie Commit bbd3058 (Hiros Eltern): in conflict-decisions.json Evy als nicht-residenten Ahnen-Stub via addAnimals {name:"Evy", gender:female, dob:"20.09.2008"} anlegen und je Kind (Charlie + Dante, dob 08.05.2010) eine resolution mit mother:"Evy", motherDob:"20.09.2008" setzen. Da - `id=a419a933-83e1-4383-bf29-6af8c504ae6b` - -### wurfchronik-ocr-mutter-fehlname (1 Ticket(s) · import-logic) -Dateien: Wurfchronik_Bilder/Wurfchronik Teil 1_page_0017.md, gerbil-manager-web WurfDetailPage (Scan-Beleg), tools/import/conflict-decisions.json, tools/import/extract.py, tools/import/merge_and_resolve.py -- [Cluster] **Z-Wurf: falsche Mutter "Macy" ist OCR-Fehllesung von "Mary"** (Z-Wurf, M/high) — Z-Wurf-motherId und die 4 Jungtiere (Zeus/Pina/Flummy/Lea) auf 6ec4527c (Mary v. PZ Seligenstadt) umbiegen und das synthetische "Macy" (516752f8) aufloesen/mergen. Re-ingest-stabil umsetzen: entweder Namenskorrektur in der Wurfchronik-Extraktion (page_0017 "Macy"->"Mary", damit D - `id=f42b6215-0896-4352-ae24-572829e6b42e` - -### zuchttier-badge (1 Ticket(s) · frontend) -Dateien: gerbil-manager-web/src/index.css, gerbil-manager-web/src/pages/GerbilDetailPage.tsx, gerbil-manager-web/src/pages/GerbilsPage.tsx, gerbil-manager-web/src/strings/de.ts -- [Cluster] **Positives „Zuchttier"-Icon/Badge vor dem Verstorben-Status** (Flori von den Kleinen Chaoten, S/medium) — Neues positives „Zuchttier"-Badge/Icon im .ak-heroover-Block der GerbilDetailPage.tsx VOR der ak-statusbadge (Z.267-269) einfügen, analog zum Extern-Badge (Z.270-274). String in de.ts (bei externalBadge, Z.59-60), CSS-Variante in index.css. Kriterium: Tier hatte mind. einen eigen - `id=ec669e7a-1e9a-46aa-9498-bd6efab8a4d7` - -### domi-todesdatum (1 Ticket(s) · needs-info) -Dateien: tools/import/conflict-decisions.json -- [RÜCKFRAGE] **Domi: Todesdatum-Zweifel der Züchterin** (Domi, S/high) — Datum ist quellenkonform und traceable, daher keine Korrektur ohne Bestätigung. Falls die Züchterin ein anderes Datum nennt: variables Import-Tier (isResident=false, fromWurfchronik) über conflict-decisions.json als name+dob-Resolution mit dateOfDeath-Override korrigieren (analog - `id=929273e9-5463-4934-92ff-60cf25c71030` - -### dante-wurf (1 Ticket(s) · needs-info) -Dateien: gerbil 93457716-65d2-5432-a52a-553ae873ca6d, gerbil-manager-web/src/pages/GerbilDetailPage.tsx, litter eb8047f2-c234-512b-b719-fe6ac6610d9c, tools/import/conflict-decisions.json -- [RÜCKFRAGE] **Dante: gemeldeter fehlender Wurf ist bereits vorhanden (Q-Wurf)** (Dante, S/medium) — Antwort abwarten: (a) meint sie den Q-Wurf und sieht ihn nicht → Anzeige/Refresh prüfen und Ticket schließen; (b) zusätzlicher, wirklich fehlender Wurf → per addLitters in conflict-decisions.json ergänzen + neu importieren. Separat prüfen (eigenes Thema): Vertrags-Mismatch — Prov - `id=9af81ae2-464c-480b-8199-6f73fcda8619` - -### todesursache-am-suffix (1 Ticket(s) · import-logic) -Dateien: tools/import/merge_and_resolve.py (parse_death_info), tools/import/test_merge_resolve.py -- [Cluster] **Todesursache bei Eliza: überflüssiges "am" abschneiden** (Eliza, S/high) — Im cod-Cleanup-Block von parse_death_info (tools/import/merge_and_resolve.py ~Z.729-731) trailing Datums-Bindewörter generisch abschneiden, z. B. cod = re.sub(r'\s+(am\|vom\|den\|im)$', '', cod, flags=re.I).strip(' ,;.-'). Danach neu extrahieren + POST /import/ingest-resolved. no - `id=47e66759-1790-4da9-baba-6357c96011d3` - -### flori-vaterschaft (1 Ticket(s) · needs-info) -- [RÜCKFRAGE] **Flori als Vater von 4 Würfen verlinkt – Züchterin erwartet nur JD** (Flori von den Kleinen Chaoten, M/medium) — Nach Bestätigung der Züchterin (welche der 4 Würfe wirklich von Flori sind) in tools/import/conflict-decisions.json die fälschlich Flori zugeordneten Litter-Vater-Links austragen/korrigieren (Litter-Parent-Override bzw. resolution), dann merge_and_resolve.py neu laufen lassen und - `id=b7fbdb94-202b-488b-ab5f-6a2b868e65e3` - -### falscher-vater-dublette-verstorben (1 Ticket(s) · import-logic) -Dateien: gerbil:04b14348-Feivel-tot, gerbil:be5bca76-Feivel-vdKC, litter:47d3b15a-P2-Wurf, litter:692c382b-Z2-Wurf, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Feivels Würfe fehlen – Vater dem falschen Namensvetter zugeordnet** (Feivel von den Kleinen Chaoten, M/high) — Im Importer (merge_and_resolve.py) die Vater/Mutter-Namensauflösung der Wurfchronik-Würfe um eine Sterbedatum-Plausibilität erweitern (Kandidat muss zum Wurfdatum leben) -> der 2012 verstorbene Feivel fällt weg, der lebende Feivel von den Kleinen Chaoten gewinnt. Fallback: geziel - `id=b441c5ea-ec07-4f87-aa2e-09399ab2a027` - -### charlett-mwurf-owurf (1 Ticket(s) · import-data) -Dateien: tools/import/conflict-decisions.json, tools/import/extract.py, tools/import/merge_and_resolve.py -- [Cluster] **M-Wurf/O-Wurf: Mutter Charlett fehlt, M-Wurf unvollständig** (M-Wurf, M/medium) — Charlett per conflict-decisions.json addAnimals mit rpro3-Daten anlegen (female, dob 2010-05-14, Kohlfuchs-hell-Schecke, genotype aa Cc[chm] DD ee G- PP Spsp, Herkunft Clan of little longnoses, Eltern Sébastian×Angéline), als Mutter von M-Wurf (2011-03-03) und O-Wurf (2011-04-12) - `id=7d032fc5-f6b8-4c72-891d-f87fc4935ac9` - -### extern-nachzucht-versteckt (1 Ticket(s) · frontend) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx -- [Cluster] **Eddys Nachzucht (Sohn Oskar) fehlt auf der Akte externer Ahnen** (Eddy von den Kleinen Chaoten, S/high) — Frontend-Fix in gerbil-manager-web/src/pages/GerbilDetailPage.tsx: Abschnitt "Nachzucht in der Zucht" (breedingOffspring, ~Zeile 673) auch für nicht-residente Tiere rendern, damit externe Ahnen ihre residenten Nachkommen zeigen. Oskar erscheint dann automatisch (steht bereits in - `id=e5900a79-c21c-435d-af3e-9dc9518c5b40` - -### zuchtpartner-zeile (1 Ticket(s) · frontend) -Dateien: gerbil-manager-web/e2e/mock-api.ts, gerbil-manager-web/src/pages/GerbilDetailPage.tsx, gerbil-manager-web/src/strings/de.ts -- [Cluster] **Zuchtpartner-Zeile in der Tier-Akte anzeigen** (Alvin von den kleinen Chaoten, S/high) — Frontend-only: In GerbilDetailPage.tsx aus den bereits geladenen parentLitters eine Distinct-Liste der jeweils anderen Elternteile ableiten (litter.motherId wenn Tier=Vater, sonst fatherId) und als eigene Kv-Zeile "Zuchtpartner" mit Links auf die Partner-Akten rendern; nur bei is - `id=ce309e79-4e4a-4219-a112-8706dc80de42` - -### nachzucht-verlinkung (1 Ticket(s) · import-logic) -Dateien: GerbilManagerWebAPI/Import/Rpro3/rpro3-decisions.json, gerbil-manager-web/src/pages/GerbilDetailPage.tsx, tools/import/merge_and_resolve.py -- [Cluster] **Nachzucht-Verlinkung bei Blacky und Flori fehlerhaft** (Danny von Maintaler PZ, M/medium) — Blacky-Record 22fe841c in 9b40b9e5 zusammenfuehren (Import-Dedup, deterministische IDs, re-ingest-stabil), analog zum "zwei Danny"-Fall; dann konsolidieren sich Wuerfe/Nachzucht. Fuer Flori Feivels Zuchteinsatz/Residenz anhand der handschriftlichen Wurfchronik pruefen (laeuft ueb - `id=8e402d22-f5cb-4993-af05-cfab8c62ccb2` - -### toter-elternteil-namensdublette (1 Ticket(s) · import-logic) -Dateien: Gerbil 29cbd5e6 (Eliza *2010), Litter 2afbbfda-80d3-5e8e-88b5-852b9eaa942a (J5-Wurf), tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py (_resolve_name, parent_age_plausible), tools/import/test_merge_resolve.py -- [RÜCKFRAGE] **Toter Elternteil als Mutter: falscher Wurf auf Elizas Akte (Namensdublette)** (Eliza, M/high) — In merge_and_resolve.py::_resolve_name / parent_age_plausible einen Sterbedatum-Filter ergänzen: Kandidat ausschließen, wenn DateOfDeath vor dem Wurfdatum liegt (Gestations-Marge ~25 Tage). Regressionstest in test_merge_resolve.py. Nach Antwort der Züchterin die korrekte lebende - `id=b22896bc-755c-48a2-aa01-e3788453feb2` - -### geschlecht-override (1 Ticket(s) · import-logic) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Domi als weiblich statt männlich importiert** (Domi, S/high) — Zwei Teile: (1) conflict-decisions.json-Eintrag für Domi {name:"Domi", dob:"2010-06-25", gender:"m"}; (2) in merge_and_resolve.py (~Z.3291) den Guard lockern, sodass ein expliziter decision-gender auch ein bereits gesetztes Geschlecht überschreibt (aktuell nur bei unknown/None). - `id=4edc51d5-1792-4f2a-b75f-c9e57363f70d` - -### a-wurf-jungtierzahl (1 Ticket(s) · needs-info) -Dateien: GerbilManagerWebAPI/Endpoints/LitterEndpoints.cs, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **A-Wurf: gemeldete Geburtenzahl (4/2 Todgeburten) widerspricht 3 eingetragenen Jungtieren** (A-Wurf, S/medium) — Antwort der Züchterin abwarten: entweder totalBorn=5 (3 Pups + 2 Todgeburten) oder ein Pup gehört nicht in den Wurf (dann Merge 0001/0013 korrigieren). Danach totalBorn/deathsWithin8Weeks im Importer setzen (merge_and_resolve.py / conflict-decisions.json bzw. addLitters), kein Li - `id=a93938a6-f2d1-4fb6-ba0f-c96b72671236` - -### kruke-dob (1 Ticket(s) · import-data) -Dateien: tools/import/conflict-decisions.json -- [Cluster] **Kruke: Geburtsdatum 18.09.2009 ergänzen** (Kruke, S/high) — In tools/import/conflict-decisions.json beim Kruke-Eintrag (ca. Zeile 949) "dob": "18.09.2009" ergänzen (Format DD.MM.YYYY wie beim Wynn-Eintrag), dann merge_and_resolve.py laufen lassen und POST /import/ingest-resolved. Kein Code- oder Schema-Change nötig; plausibel als Mutter e - `id=b72acf1e-4999-44ae-a2dc-58408864e242` - -### scheckungsart-erfassung (1 Ticket(s) · import-logic) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Alvins Scheckungsart (Punktschecke) wird nicht angezeigt – Feld leer** (Alvin von den kleinen Chaoten, S/high) — spottingType="Punktschecke" für Alvin (b04dc268…, DOB 2010-02-18) setzen. Kanonisch/re-ingest-stabil: conflict-decisions.json-Resolution per name+dob um Feld spottingType erweitern UND merge_and_resolve.py-Resolutions-Anwender (~Zeile 3235-3279) um einen spottingType-Zweig analog - `id=9d86a7c2-c4dc-4ad2-aedd-4abaf13ca26b` - -### herkunft-clan-kleine-chaoten (1 Ticket(s) · import-logic) -Dateien: GerbilManagerWebAPI/Import/Rpro3/Rpro3ImportService.cs, POST /import/ingest-resolved, tools/import/merge_and_resolve.py -- [Cluster] **Herkunft "Clan kleine Chaoten" global auf "Zucht der Kleinen Chaoten" vereinheitlichen** (Bodo, M/high) — Kanonisches Herkunfts-Label "Zucht der Kleinen Chaoten" definieren und beide Importer darauf normalisieren: (1) RPRO3-Importer an der OriginBreeder-Zuweisung (Rpro3ImportService.cs ~Z.444) "Clan kleine Chaoten"/Varianten mappen; (2) Python-Importer (merge_and_resolve.py) Literal - `id=aaee068b-16a8-46a7-b973-a06577cc5ab1` - -### wurfchronik-dublette-merge (1 Ticket(s) · import-logic) -Dateien: gerbil JackJack ff6735c6-ddc9-5277-a976-1867e5242818, gerbil JackSack b6d8b3ef-3077-53cf-bc52-93fd82e3982b, litter A-Wurf 29f4bae9-ca7d-56c4-b8bc-8283d5284b87, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **JackJack und JackSack im A-Wurf sind dasselbe Tier (Dublette)** (A-Wurf, M/high) — Beide Datensätze zu einem Tier zusammenführen und den reichen Datensatz (JackSack, page_0013, id b6d8b3ef: Farbe/Genotyp/Sterbedatum/Abnehmer) behalten, den Stub (JackJack, page_0001, id ff6735c6) unterdrücken. Da der Decision-Layer bisher nur isResident/notes/receiver kann (kein - `id=0cf6b838-3ee1-4ec0-ac9d-43901e6b5320` - -### wurfchronik-eltern-fehlen (1 Ticket(s) · import-logic) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Wurf S1 fehlt bei Flori (Eltern Flori x Delia of cb-animals nicht verknüpft)** (Flori von den Kleinen Chaoten, M/medium) — Wurf S1 im Importer mit Vater Flori (90a37fe0) und Mutter Delia of cb-animals (81204d7e) verknüpfen; beide existieren und sind altersplausibel. Bevorzugt per conflict-decisions.json. Achtung Dubletten-Risiko: addLitters erzeugt eine neue Litter (decision-litter-wurf-s1) und würde - `id=55ff9203-58c3-4470-b960-5ca035cb4666` - -### dastan-fehlmerge (1 Ticket(s) · import-logic) -Dateien: 7 Wuerfe 2015 (T6/Y4/E6/H6/Dastan+Elly/Snoops+Dastan/Z5), Genotyp + isResident + Farbschlag, Gerbil fba979a3 (Dastan), Litter 1eb3e1fb (D-Wurf 2010, Blacky+Kuke), tools/import/conflict-decisions.json, tools/import/extract.py, tools/import/merge_and_resolve.py -- [Cluster] **Dastan: Import-Fehl-Merge trennt Pet- und Zucht-Dastan nicht** (Dastan, L/high) — Force-Split im Dedup (merge_and_resolve.py + conflict-decisions.json): 2015er-Zucht-Dastan-Quellzeilen (Teil1 p45/46/50, Teil2 p6) nicht in den 2010er Pet-Dastan mergen. Pet-Dastan fba979a3 korrigieren: Genotyp aa C- DD E- G- P- Spsp (Kragenschecke), isResident=false, "Breeding F - `id=0ebc250f-17a7-45d8-8b21-91e563fbfd9c` - -### rpro3-erwerb-uebernahme (1 Ticket(s) · needs-info) -Dateien: GerbilManagerWebAPI/Endpoints/AcquisitionEndpoints.cs, GerbilManagerWebAPI/Import/Rpro3/Rpro3ImportService.cs, GerbilManagerWebAPI/Models/AcquisitionRecord.cs, tools/import/_rpro3.db -- [RÜCKFRAGE] **Erwerb-Daten aus RennmausPro für Bodo, Coco, JackJack übernehmen** (Bodo, S/medium) — Erwerbs-Felder (Datum, von wem bzw. „eigene Zucht", ggf. Preis/Notiz) für die drei Tiere von der Züchterin erfragen (Rückfrage gestellt) und via POST /acquisitions als ingest-stabile AcquisitionRecords eintragen. Alternativ vollständigen RennmausPro-Export beschaffen und den rpro - `id=efe47d3f-6da8-4928-a05d-875d7a410a44` - -### rpro3-foto-import (1 Ticket(s) · needs-info) -Dateien: /photos-Endpoint, GerbilManagerWebAPI/Import/Rpro3, GerbilPhotosTab.tsx, tools/import/_rpro3.db (photo_tb) -- [RÜCKFRAGE] **Blacky: Fotos aus RennerPro fehlen (nur Dateinamen, keine Bilddateien)** (Blacky von PZ Seligenstadt, L/high) — Speicherort der echten RennerPro-Bilddateien klaeren (Rueckfrage gestellt), dann rpro3-Foto-Import bauen: photo_tb-Dateinamen je stamm-id/rid auf die Bilddateien mappen und ueber den /photos-Endpoint an die gematchten Gerbils haengen (ingest-stabil, deterministisch). Gilt generel - `id=00493a79-b4e1-4856-b0c4-9e08508ca484` - -### tod-abgabe-erfassung (1 Ticket(s) · backend) -Dateien: GerbilDetailPage.tsx (Felder Tod/Abgabe/Abnehmer), GerbilManagerWebAPI Endpoints/GerbilEndpoints.cs (Edit → GerbilOverride), Kontakt-Anlage (Abnehmer Caroline Emmert) -- [Cluster] **Gaida: Tod (09.10.2025, Lungenentzündung) und Abgabe an Caroline Emmert erfassen** (Gaida von den Kleinen Chaoten, S/high) — Neuen Abnehmer-Kontakt "Caroline Emmert" anlegen (existiert nicht, 20 Kontakte geprüft). Auf Gaidas Akte via UI-Edit setzen: dateOfDeath=2025-10-09, causeOfDeath="Lungenentzündung mit Fieber", receiverContactId=neuer Kontakt, status→Verstorben/Deceased, goHomeDate leer lassen. De - `id=c3f374ec-83a2-4999-bbeb-ef3bdbbd9bfb` - -### eltern-namensdopplung (1 Ticket(s) · needs-info) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Alvins „Nachkommen mit Zuchteinsatz“ zeigt falsche Tiere (Namensdopplung)** (Alvin von den kleinen Chaoten, M/medium) — Eltern-Links der 2014-2016er Würfe von Sunny(31533666)/Zora(527a353c)/Lexy(bd97a229)/Zorro(f50bcf7f) auf die korrekten jüngeren gleichnamigen Tiere umhängen (DOB-Plausibilität im Resolver bzw. conflict-decisions.json) und neu importieren; für Lee(8eb68783) & Sam(5b86e826) nach An - `id=d1069edb-1001-4e24-b436-147948b85c8a` - -### gerbil-name-suffix (1 Ticket(s) · import-logic) -Dateien: gerbil c7dbc0b0-8a35-57a1-8f64-2b5351a273d6 (Uriana), tools/import/merge_and_resolve.py -- [Cluster] **Mutter-Name auf Xhemiles Akte ohne Zuchtsuffix ("Uriana" statt "Uriana von den Kleinen Chaoten")** (Xhemile gen. Chanel von den Kleinen Chaoten, S/high) — In tools/import/merge_and_resolve.py in get_normalized_gerbil_name (gerbil_norm_map, Zeile 389ff) Eintrag "uriana": "Uriana von den Kleinen Chaoten" ergänzen (Norm-Key eindeutig, nur dieser Proband). Danach neu generieren (extract.py + merge_and_resolve.py) und POST /import/inges - `id=c55a677c-be2e-4f34-820e-a695ef9260b2` - -### xtra-bounty (1 Ticket(s) · import-data) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Tiername falsch: „Extra Bounty" muss „Xtra Bounty" heißen** (Extra Bounty, S/high) — Im bereits vorhandenen conflict-decisions.json-Block für externalRef „Wurfchronik Teil 2_page_0006.md-0c9c7be0-de7f-48fa-b8be-c6e87cbcd8fa" ein Feld "renameTo": "Xtra Bounty" ergänzen (Match-Key externalRef bleibt stabil, renameTo greift in merge_and_resolve.py). Danach merge_and - `id=878f4b6e-e5c2-4c35-8b91-e08127914fe4` - -### kuke (1 Ticket(s) · import-data) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py, tools/import/test_merge_resolve.py -- [Cluster] **Kuke (fälschlich "Kruke"): Name korrigieren + RennmausPro-Daten ergänzen** (Kruke, M/high) — Im Importer beheben: addAnimals-Stub weiterhin auf "Kruke" gekeyt lassen (deterministische Id + 6 Wurf-Elternlinks hängen an dieser Schreibweise), Anzeigenamen via resolutions {name:Kruke, renameTo:Kuke} korrigieren. Reichfelder ergänzen: dob 18.09.2009, zucht "M.Knoss" (OriginBr - `id=1fffdd0e-b9c3-4bcf-a7c9-705fdb111acb` - -### wurfchronik-teil2-phantom (1 Ticket(s) · needs-info) -Dateien: GerbilManagerWebAPI/Import/IngestResolvedService.cs (Stale-Removal), tools/import (Wurfchronik-Teil-2-Import), tools/import/conflict-decisions.json -- [RÜCKFRAGE] **Unbekanntes Tier „Nymphs" aus Wurfchronik Teil 2 (Phantom-Zuchtrüde)** (Nymphs, M/medium) — Antwort der Züchterin abwarten. Bei echtem Tier: korrekte Felder/Namen via Override bzw. Chronik-Parsing ergänzen. Bei Phantom/OCR-Fehler: Datensatz unterdrücken/entfernen. Prüfen, ob Nymphs vom aktuellen tools/import-Lauf überhaupt noch erzeugt wird (Teil-2-Import ohne committet - `id=ca8a1c48-1407-44ed-908f-986cbc0e5e0b` - -### willow (1 Ticket(s) · import-data) -Dateien: Live-DB Gerbil 3f9c5a26-af3a-55dc-9c4b-4ebdea4ce36f, Wurfchronik Teil 2 Excel Quelle (Seite 6), tools/import (Wurfchronik Teil 2 Import), tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py (Dedup-Regel) -- [Cluster] **Willow: falsches Geschlecht (m→w) + fehlende Eltern/Geburtsdatum aus Wurfchronik Teil 2** (Willow, M/medium) — Als variables Wurfchronik-Tier im Importer korrigieren (re-ingest-stabil): Geschlecht auf weiblich setzen; DOB + Eltern aus der Wurfchronik-Teil-2-Excel (Seite 6) bzw. per Dublettenabgleich mit „Willow of Black Forest" (DOB 16.01.2017, Black-Forest-Stammbaum) ermitteln und über c - `id=9935323f-883e-4686-b3ff-de8d10de4722` - -### hero-abgabe-widerspruch (1 Ticket(s) · needs-info) -Dateien: Gerbil Hero 7f0d43cc-0cf6-5afc-98bd-63ab6f031c04, Litter D3-Wurf 421851c3-dedd-5620-80ec-a06e27c9cec9, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Hero (Alvins Sohn): Abgabe-/Sterbenotiz widerspricht D3-Wurf-Vaterschaft** (Alvin von den kleinen Chaoten, S/medium) — Antwort abwarten. Fall A (blieb bei ihr, D3 korrekt): in conflict-decisions.json name+dob-Resolution fuer Hero (*31.10.2010) die falsche Abgabe-/Sterbenotiz per notes-Override korrigieren, isResident bleibt true. Fall B (2011 abgegeben, D3 gehoert anderem Hero): D3-Vaterlink loes - `id=72d18800-342b-4935-9aad-311a1607b3da` - -### genna-herkunft (1 Ticket(s) · import-data) -Dateien: Herkunft/originBreeder, Stammbaum (Eltern Epoxy x Reika), tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Genna: falsche Herkunft (soll "Clan of Golden Lights" sein)** (Genna, M/high) — In tools/import/conflict-decisions.json eine name(+dob)-Resolution fuer Genna anlegen: originBreeder="Clan of Golden Lights" (merge_and_resolve.py wendet den Override ~Z.3236 an). Optional anreichern: DOB 2014-06-20, Farbe Zobel, Genotyp aa c[chm]c[chm] DD Ee gg Pp spsp. Stammbau - `id=2fbbc0a2-a80c-4d90-a250-567fdd915abe` - -### extern-zuchttier-wurfleiste (1 Ticket(s) · needs-info) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Extern zugekauftes Zuchttier (Blacky von PZ Seligenstadt) zeigt Wurf-Leiste** (Blacky von PZ Seligenstadt, M/medium) — Antwort abwarten. (a) "Nur Vorfahre, nie selbst gezüchtet" -> Blacky per isResident=false-Override in conflict-decisions.json extern markieren (analog Akane/Naémi/Bentley), Wurf-Leiste verschwindet automatisch; dabei die fälschlich "von den Kleinen Chaoten" benannten Nachkommen ( - `id=7dedec07-99b8-4b0c-9f51-bf14a583ec67` - -### xhemile-falsche-mutter (1 Ticket(s) · import-data) -Dateien: Wurf 5b6c873a-a508-5c4d-af5b-66a37c071391, tools/import/conflict-decisions.json, tools/import/extract.py (chart-position Eltern-Resolver), tools/import/merge_and_resolve.py -- [Cluster] **Xhemile gen. Chanel: falsche Mutter (Uriana statt Oyama), Vater korrekt** (Xhemile gen. Chanel von den Kleinen Chaoten, S/high) — Re-ingest-stabiler Fix im Importer (variables Tier, kein Live-DB-Patch): conflict-decisions.json-Resolution fuer Xhemile (name+dob 2021-05-16) mit Mutter-Override auf Oyama von den Kleinen Chaoten (89c61419-4681-5c79-954f-7010a3feefa6, *2018-12-26); Vater Ramzi (6a6d47ec-6ace-5e1 - `id=25890c71-dfde-4a1c-b77e-b04a6b5a9ad3` - -### verpaarung-phantom-tiere (1 Ticket(s) · import-logic) -Dateien: resolved_import.json, tools/import/extract.py, tools/import/test_extract.py -- [Cluster] **"Verpaarung von …" als Tiername importiert (Phantom-Tiere)** (Verpaarung von Chevrolet Camaro of Topolino, M/high) — In tools/import/extract.py Namenszellen, die mit "Verpaarung von" (case-insensitive) beginnen, nicht als Eigennamen/Tier akzeptieren, sondern verwerfen (mit verworfen-Provenance). Danach extract.py, merge_and_resolve.py, POST /import/ingest-resolved; die stale Phantom-Tiere werde - `id=cc85c890-042f-4393-bbcf-f919716a3b12` - -### elliot (1 Ticket(s) · import-data) -Dateien: Contact (neu: Luise Neubert-Kutschke), Gerbil.gender, Gerbil.goHomeDate, Gerbil.receiverContactId, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Elliot (*2023): Geschlecht auf männlich korrigieren + Abgabe an Luise Neubert-Kutschke nachtragen** (Elliot, M/high) — Fix im Importer (re-ingest-stabil): conflict-decisions.json-Resolution auf name=Elliot + dob=25.02.2023 (NICHT nur Name — zweiter Elliot *11.09.2015 existiert; alternativ externalRef docx-161-elliot-2023-02-25) mit gender=male; addContact für Luise Neubert-Kutschke (Am Steinberg - `id=6af0ad73-5987-4310-a757-a47282a4f1f9` - -### shelly-name-merge (1 Ticket(s) · import-data) -Dateien: tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py, tools/import/output/animals.json -- [RÜCKFRAGE] **Shelly: Name unvollständig – Wurfchronik-Rufname nicht mit Stammbaum-Vollnamen gemergt** (Shelly, S/high) — In tools/import/conflict-decisions.json einen Namens-/Dedup-Alias hinterlegen, der den Wurfchronik-„shelly" mit dem Stammbaum-Vollnamen-Record (samgenshellyvdbuntenfellnasen / sammygenshellyvdbuntenfellnasen) merged bzw. den korrekten Vollnamen setzt (merge_and_resolve.py Dedup), - `id=7deec818-ad29-4053-86fb-5e67ac180e70` - -### tai-verstorben (1 Ticket(s) · needs-info) -- [RÜCKFRAGE] **Tai als verstorben markieren – Todesdatum fehlt in RennerPro-Kopie** (Tai, S/high) — Todesdatum (und ggf. Ursache) bei der Züchterin erfragen, dann status=Deceased + dateOfDeath (+ causeOfDeath) auf das Tier setzen. Dabei Ingest-Persistenz beachten: Status wird laut CLAUDE.md nicht per GerbilOverride eingefroren – entweder Freeze-Scope prüfen oder eine name+dob-R - `id=b222b459-57c5-40ea-88b0-0fa696869c6b` - -### elena-dublette (1 Ticket(s) · import-data) -Dateien: gerbil a0aff85b (Elena of KK Chaos), gerbil bc2400c9 (Elena), litter f26293c2, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [Cluster] **Elena ohne Stammbaum ist Dublette von „Elena of KK Chaos"** (Elena, M/high) — Manuelle Merge-/Dubletten-Entscheidung in tools/import/conflict-decisions.json anlegen, die die Wurfchronik-Elena (bc2400c9) mit „Elena of KK Chaos" (a0aff85b) zusammenführt → ein Tier mit vollem Stammbaum (Eltern-Wurf f26293c2 Ibo × Skarlett), DOB 2013-12-05, Genotyp, Status Dec - `id=6cdb22b4-f8c0-4d0b-913c-d16392031585` - -### stammbaum-ohne-eltern (1 Ticket(s) · needs-info) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx, gerbil-manager-web/src/pages/StammbaumPage.tsx, tools/import/conflict-decisions.json -- [RÜCKFRAGE] **Aaren: Stammbaum wirkt leer (keine Eltern hinterlegt)** (Aaren, S/high) — Rückfrage an die Züchterin, ob Aarens Eltern bekannt sind. Falls ja: über conflict-decisions.json (addAnimals/parents, deterministische GUID) ergänzen + Re-Ingest, dann füllt sich der Stammbaum. Falls Eltern unbekannt (zugekauft): leerer Stammbaum ist korrekt; ggf. UX-Hinweis "ke - `id=7a9831f6-4d68-41e7-add2-bc06393e6ed6` - -### drake-jr-eltern (1 Ticket(s) · needs-info) -Dateien: gerbil-manager-web/src/pedigree/build.ts, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Drake Jr: Stammbaum leer, weil keine Eltern hinterlegt** (Drake Jr, S/medium) — Zur Frage der Züchterin: Ja, Drake Jr ist Vater von Q3-Wurf (2014-06-21) mit Jacky v.d. Kleinen Chaoten, 12 Jungtiere. Nach Antwort der Züchterin die Eltern von Drake Jr im Importer verknüpfen (conflict-decisions.json name+dob-Resolution bzw. addLitters mit Drake Jr als Kind, mer - `id=31811914-257b-4c83-bb42-85cc2d140277` - -### vorfahre-nachzucht-zeile (1 Ticket(s) · frontend) -Dateien: gerbil-manager-web/src/pages/GerbilDetailPage.tsx, gerbil-manager-web/src/strings/de.ts -- [Cluster] **Vorfahren: Wurf-Karten durch Nachzucht-Zeile ersetzen** (Percy of little runners, S/high) — In GerbilDetailPage.tsx fuer isResident===false zusaetzlich zum/statt des nonResidentNote eine kompakte Zeile rendern, die die Nachzucht(en) benennt (Kinder-Namen/Links aus dem offspring-Query, der unabhaengig von der Residenz feuert, oder die Wurf-Namen). Neuen de.ts-String (z.B - `id=fbb732a3-f20b-4f12-8408-748be6cf8625` - -### berni-montana-merge (1 Ticket(s) · import-logic) -Dateien: Gerbil 763bf91f (Montana), Gerbil 999baccb (Berni), tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Berni ist vermutlich Rufname von Montana (Dublette)** (Berni, M/high) — Nach Bestätigung der Züchterin: Alias/Merge-Decision in conflict-decisions.json anlegen (Berni → Montana v.d. Kleinen Chaoten 763bf91f…), die 3 Berni-Würfe (M10/Polar/M12) auf Montana umhängen und den datumsgleichen 2017-07-20-Wurf deduplizieren, dann neu importieren. - `id=0921dba5-d3a7-47d7-ae95-0c6e535815d1` - -### x16-wurf-eltern-fehlen (1 Ticket(s) · import-data) -Dateien: Stammbaum von Goldfuchs Sp (Pikachu) Kids.xlsx, Wurfchronik der Kleine Chaoten Teil1.xlsx (Wurf X16), tools/import/conflict-decisions.json, tools/import/extract.py, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Zhuāngzǐ: Eltern/Wurf fehlen — Wurf X16 (Nisha × Zenon von Elea) eindeutig bestimmt** (Zhuāngzǐ von den Kleinen Chaoten, M/medium) — Data-driven, re-ingest-stabil in conflict-decisions.json: name+dob-Resolution (bzw. addLitters für X16) für Zhuāngzǐ UND die gleich betroffene Schwester Zaibunissa, die Wurf X16 mit Mutter Nisha of Black Forest (06dc519d-f92e-5d09-95b4-61d7bbdc228a) und Vater Zenon von Elea (cd2e - `id=785851d0-7b37-45a7-adcc-e78db9c3c4b9` - -### chiko (1 Ticket(s) · needs-info) -Dateien: Gerbil 7d3b7c47-6320-5181-b612-56676e4516d5 (Chiko), tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Chiko: leerer Stammbaum, reiner Wurfchronik-Partner ohne weitere Daten** (Chiko, S/medium) — Ruckfrage an Zuchterin nach Herkunft/Farbe/DOB von Chiko gestellt. Nach Antwort: falls Daten vorhanden -> Anreicherung via conflict-decisions.json (name+dob-Resolution bzw. addAnimals) + Re-Import; falls nur Partnertier ohne Daten -> als korrekt bestatigen (ggf. GerbilOverride/ve - `id=bc859109-32df-4506-9f7d-6f6415c00f02` - -### kruemmel-todesdatum (1 Ticket(s) · needs-info) -Dateien: Gerbil 38c6b3c9-18ca-530f-8657-02d62e7c0e59 (Krümmel), Litter 10ddf880 (F-Wurf 2010), tools/import/_rpro3.db (geprüft, kein Treffer) -- [RÜCKFRAGE] **Krümmel (F-Wurf 2010): kein Todesdatum in keiner Quelle** (F-Wurf 2010, S/high) — Auf Todesdatum der Züchterin warten. Nennt sie eines, per UI/GerbilOverride direkt am Tier 38c6b3c9 nachtragen (ingest-stabil) – KEIN Importer-Fix nötig, da die Daten in keiner Quelldatei existieren. Nennt sie keins, Feld bleibt korrekt leer und Ticket kann geschlossen werden. - `id=21c8ff24-cc03-4508-b6c6-849ab36ba5b2` - -### percy-eltern (1 Ticket(s) · import-logic) -Dateien: tools/import/conflict-decisions.json, tools/import/extract.py, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Percy of little runners: Mutter nicht verknüpft (Stammbaum-Fehlparse + Selbstreferenz)** (Percy of little runners, M/high) — 1) extract.py chart-position: Kandidaten, die auf das Tier selbst zeigen (gleiche id/Name+DOB), als Elternteil ausschließen (Logik-Bug, unabhängig vom Einzelfall). 2) Sobald die Züchterin die echte Mutter nennt: Eltern-Link (Mutter, ggf. Vater Rasputin of little runners) in confl - `id=e87a6da5-0ff1-46b6-b4ec-5d05a1605f5f` - -### dusty-stammbaum-dublette (1 Ticket(s) · needs-info) -Dateien: DB-Gerbil 41d7577e-bcaf-5967-a6ed-fe0b52171f77, DB-Gerbil 43e784a4-61bd-5b52-af74-c007c9cd6996, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py -- [RÜCKFRAGE] **Dusty: Stammbaum fehlt – vermutete Dublette (m. Zuchtbock vs. w. Jungtier)** (Dusty, M/medium) — Bei Bestätigung durch die Züchterin: beide Dusty per Force-Merge in conflict-decisions.json zusammenführen (merge_and_resolve.py), Geschlecht auf männlich korrigieren und Eltern Hero × Mira / Geburtslitter D3-Wurf übernehmen; danach neu importieren + re-ingest → Stammbaum erschei - `id=034b4988-5447-4c54-ad28-9a6708133517` - -### rpro3-stammbaum-verknuepfung (1 Ticket(s) · import-logic) -Dateien: GerbilManagerWebAPI/Import/Rpro3/Rpro3Dedup.cs, GerbilManagerWebAPI/Import/Rpro3/Rpro3ImportService.cs, GerbilManagerWebAPI/Import/Rpro3/rpro3-decisions.json, Stammbaum-Viewer (StammbaumPage.tsx), tools/import/_rpro3.db -- [Cluster] **Katinka: RennmausPro-Daten nicht mit Excel-Stammbaum verknüpft (Farbe/Genotyp/Nachzucht fehlen)** (Katinka of little runner's, L/medium) — rpro3-Importer so erweitern, dass externe Ahnen per Name (+Eltern/DOB) an bestehende Stammbaum-Records gematcht und Felder (Farbe, genotype, DOB, weitere Ahnen, Nachzucht) übernommen werden, statt Duplikate anzulegen — re-ingest-stabil mit deterministischen GUIDs. Für Katinka kon - `id=740360e5-151d-4dca-a596-f71bc128771b` - -### foto-fehlattribution (1 Ticket(s) · import-logic) -Dateien: Foto-Import-Heuristik, tools/import/extract.py (_attach_photos) -- [Cluster] **Percy: Profilfoto zeigt uni-schwarz statt schwarz-schecke** (Percy of little runners, M/medium) — Cluster: Foto-Zuordnung fuer Percy in tools/import/extract.py (_attach_photos, ~Z.388) gegen die Quell-xlsx pruefen; korrektes schwarz-schecke-Foto zuordnen bzw. das fehlattribuierte uni-schwarz-Foto entfernen; danach re-ingest (re-ingest-stabil). Kein Genotyp-/Farbschlag-Fix noe - `id=e04ceda2-1701-4820-b21f-376dc6d0c5d1` - -### rpro3-ahnen-import (1 Ticket(s) · import-data) -Dateien: GerbilManagerWebAPI/Import/Rpro3/rpro3-decisions.json, gerbil 509f4aae-1189-585c-b410-555e75d5d907, litter 4d3afe0a-1d78-5c55-bd99-4f216bd84afc, tools/import/_rpro3.db, tools/import/conflict-decisions.json, tools/import/merge_and_resolve.py, tools/import/rpro3_lookup.py -- [Cluster] **Stammbaum von Fast Boy unvollständig – Eltern aus RennmausPro ergänzen** (Fast Boy of Golden Lights, M/medium) — Fast Boys echte Eltern Charly × Ziwa aus _rpro3.db data-driven materialisieren/verknüpfen (statt Danjo-of-Golden-Lights × Xtra-Bounty), Großeltern Ziwa=Danjo × Isiri ergänzen; korrekte Charly-rid über RPRO3-Eltern-Relation wählen; falsche Platzhalter-Eltern + Litter 4d3afe0a erse - `id=0e139f1f-8aa7-4705-a430-0985cf9aa625` - -## Offene Rückfragen (NeedsInfo) - -- **Foto des N-Wurfs aus der Wurfchronik anzeigen — nicht vorhanden/kein Feature** (N-Wurf): Aktuell zeigt die App Fotos nur bei einzelnen Tieren (aus den Stammbaum-Dateien). Bei Würfen gibt es noch keine Fotos – und in der Wurfchronik-Datei selbst sind gar keine Bilder gespeichert. Hast du von diesem N-Wurf (Wurfdatum 28.03.2011) irgendwo ein Foto abgelegt, das du hier sehen möchtest? Wenn ja, sag mir bitte, in welchem Ordner oder welcher Datei es liegt – dann kann ich es einbauen. - `id=97a3b4d9-4859-4b0a-8e20-2c602ec2c167` -- **Jungtiere im Wurf falsch sortiert (alphabetisch statt Wurfchronik-Reihenfolge)** (C-Wurf 2010): Aktuell werden die Jungtiere eines Wurfs alphabetisch nach ihrem Namen sortiert. In welcher Reihenfolge sollen sie stattdessen erscheinen - genau so, wie sie in der Wurfchronik von oben nach unten aufgelistet sind? - `id=399dbeb2-6909-43fe-b00f-7b6a2b9fc7b2` -- **Domi: Todesdatum-Zweifel der Züchterin** (Domi): Bei Domi steht als Sterbedatum der 15.07.2012 (Mittelohrentzündung) — sie wäre damit knapp 2 Jahre alt geworden (geboren 25.06.2010). Dieses Datum stammt so aus deiner Wurfchronik. Passt es, oder erinnerst du dich an ein anderes Datum? Dann korrigiere ich es gern. - `id=929273e9-5463-4934-92ff-60cf25c71030` -- **Dante: gemeldeter fehlender Wurf ist bereits vorhanden (Q-Wurf)** (Dante): Bei Dante ist unter „Würfe“ bereits ein Wurf eingetragen: der Q-Wurf vom 13.06.2011, Mutter Malina, mit den vier Jungtieren Amy, Beatrice, Quentin (Eddy) und Quvin. Meinst du genau diesen Wurf – siehst du ihn auf Dantes Seite vielleicht gerade nicht? Oder geht es um einen anderen, zusätzlichen Wurf, den ich noch ergänzen soll? - `id=9af81ae2-464c-480b-8199-6f73fcda8619` -- **Flori als Vater von 4 Würfen verlinkt – Züchterin erwartet nur JD** (Flori von den Kleinen Chaoten): In der App ist Flori zurzeit als Vater von vier Würfen eingetragen: R-Wurf (08.10.2011, Mutter Yumi), W-Wurf (25.12.2011, Mutter Jana – hier steht Feivel), A1-Wurf (15.02.2012, Mutter Jana) und Wurf J1 (11.06.2012, Mutter Delia – hier steht JD). Alle vier kommen aus deiner Wurfchronik. Du meinst, Flori war nur bei einem Wurf der Vater (bei JD). Magst du in deiner handgeschriebenen Wurfchronik nachschauen und mir sagen, welche dieser vier Würfe wirklich von Flori stammen? Die falschen Vater-Angaben trage ich dann aus, dann verschwindet z. B. Feivel aus Floris Nachzuchten. tools/import/conflict-decisions.json,tools/import/merge_and_resolve.py,GerbilDetailPage.tsx (Nachzuchten-Ansicht) - `id=b7fbdb94-202b-488b-ab5f-6a2b868e65e3` -- **Toter Elternteil als Mutter: falscher Wurf auf Elizas Akte (Namensdublette)** (Eliza): Es gibt in den Daten mehrere Rennmäuse mit dem Namen Eliza. Der Wurf mit Vanille, Jake und Jerry (geboren am 28.02.2015, Vater Jack Jr.) ist versehentlich bei der ältesten Eliza gelandet (geboren am 20.08.2010) – diese Eliza war Anfang 2015 aber schon nicht mehr am Leben, deshalb zeigt ihre Akte fälschlich einen Wurf. Zwei andere Eliza-Mäuse kommen als echte Mutter in Frage: eine geboren am 31.08.2013 und eine geboren am 13.02.2014. Welche der beiden hat diesen Wurf bekommen? - `id=b22896bc-755c-48a2-aa01-e3788453feb2` -- **A-Wurf: gemeldete Geburtenzahl (4/2 Todgeburten) widerspricht 3 eingetragenen Jungtieren** (A-Wurf): Im A-Wurf (18.02.2010) sind aktuell drei männliche Jungtiere eingetragen: Alvin, JackSack und JackJack. Du schreibst, es wurden 4 Junge geboren, davon 2 Todgeburten. Damit ich die Zahlen richtig eintrage: Kamen die 2 tot geborenen Jungtiere zusätzlich zu diesen drei dazu (dann wären es insgesamt 5 Geburten)? Oder gehört eines der drei genannten Tiere nicht in diesen Wurf, sodass es genau 4 Geburten waren? - `id=a93938a6-f2d1-4fb6-ba0f-c96b72671236` -- **JackJack und JackSack im A-Wurf sind dasselbe Tier (Dublette)** (A-Wurf): Kurze Rückfrage zu diesem Tier aus dem A-Wurf: In der Wurfchronik steht es mit zwei leicht unterschiedlichen Namen – einmal „JackJack" (auf der Wurf-Übersicht) und einmal „JackSack" (auf der Einzelseite mit Farbe, Genetik und Abgabe an Larissa Draeger). Beide meinen dasselbe Tier, geboren am 18.02.2010. Wie heißt die Rennmaus richtig? Sobald du mir das sagst, führe ich die beiden Einträge zu einem einzigen zusammen. - `id=0cf6b838-3ee1-4ec0-ac9d-43901e6b5320` -- **Erwerb-Daten aus RennmausPro für Bodo, Coco, JackJack übernehmen** (Bodo): Ich trage dir die Erwerbs-Daten (woher das Tier kam, das Datum und – falls vorhanden – Preis oder eine Notiz) gerne so in die App ein, wie sie bei „Erwerb" in RennmausPro stehen. In der RennmausPro-Sicherung, die mir vorliegt, finde ich Bodo, Coco und JackJack aber nicht. Magst du mir für die drei jeweils kurz aufschreiben, was bei „Erwerb" steht (Datum, von wem bzw. „eigene Zucht", und ggf. Preis oder Notiz)? Dann pflege ich es ein. - `id=efe47d3f-6da8-4928-a05d-875d7a410a44` -- **Blacky: Fotos aus RennerPro fehlen (nur Dateinamen, keine Bilddateien)** (Blacky von PZ Seligenstadt): Die RennerPro-Daten, die wir vorliegen haben, nennen zwar die Dateinamen von Blackys Fotos (blacky.jpg, B1.JPG, bla.JPG), enthalten die Bilder selbst aber nicht. Wo liegen denn die echten Foto-Dateien aus deinem RennerPro-Programm - zum Beispiel in einem Bilder-Ordner auf dem Rechner oder auf dem TrueNAS? Sobald wir an diesen Ordner kommen, koennen wir Blackys Fotos (und die der anderen Tiere) uebernehmen. - `id=00493a79-b4e1-4856-b0c4-9e08508ca484` -- **Alvins „Nachkommen mit Zuchteinsatz“ zeigt falsche Tiere (Namensdopplung)** (Alvin von den kleinen Chaoten): Bei Alvin zeigt die App unter „Nachkommen mit Zuchteinsatz“ gerade Hero, Flippi und Gimli — aber zusätzlich auch Sunny, Zora, Lexy und Zorro, während Lee und Sam fehlen. Damit ich es richtig zuordnen kann, eine kurze Rückfrage: Hatten Lee und Sam bei dir eigene Babys (jeweils einen eigenen Wurf)? Und stimmt es, dass Sunny, Zora, Lexy und Zorro NICHT in deiner Zucht waren (deren angezeigte Würfe gehören vermutlich zu gleichnamigen, später geborenen Mäusen)? - `id=d1069edb-1001-4e24-b436-147948b85c8a` -- **Unbekanntes Tier „Nymphs" aus Wurfchronik Teil 2 (Phantom-Zuchtrüde)** (Nymphs): Das Tier „Nymphs" habe ich aus deiner eingescannten Wurfchronik Teil 2 (Seite 26) übernommen. Dort taucht es nur als Zuchtrüde auf (im Original „CP-Nymphs") — ganz ohne Geburtsdatum, Farbe, Eltern oder zugeordneten Wurf. Kannst du dich an einen Rennmaus-Rüden namens „Nymphs" erinnern, oder ist das ein Lese-/Übertragungsfehler, den ich entfernen soll? - `id=ca8a1c48-1407-44ed-908f-986cbc0e5e0b` -- **Hero (Alvins Sohn): Abgabe-/Sterbenotiz widerspricht D3-Wurf-Vaterschaft** (Alvin von den kleinen Chaoten): Bei Hero (Sohn von Alvin, geboren am 31.10.2010) steht in den Notizen, dass er am 1.4.2011 an Brigitta Struve abgegeben wurde und dort am 4.4.2014 verstarb. Gleichzeitig ist er als Vater des D3-Wurfs mit Mira vom 22.11.2013 eingetragen - also mitten in der Zeit, in der er laut Notiz schon bei Brigitta war. Das passt zeitlich nicht zusammen. Was stimmt: Blieb Hero bei dir und hatte mit Mira seinen einen Wurf (dann ist die Abgabe an Brigitta ein Versehen)? Oder wurde er 2011 abgegeben - und der D3-Wurf gehoert eigentlich zu einem anderen Rennmaeuserich? - `id=72d18800-342b-4935-9aad-311a1607b3da` -- **Extern zugekauftes Zuchttier (Blacky von PZ Seligenstadt) zeigt Wurf-Leiste** (Blacky von PZ Seligenstadt): Kurze Frage zu Blacky von PZ Seligenstadt: Hast du früher selbst mit ihm gezüchtet, sind also bei dir eigene Würfe mit ihm gefallen? Oder ist er nur ein Vorfahre aus einer fremden Zucht, den du selbst nie zur Zucht eingesetzt hast? Danach richte ich, ob und welche Würfe bei ihm angezeigt werden. - `id=7dedec07-99b8-4b0c-9f51-bf14a583ec67` -- **Shelly: Name unvollständig – Wurfchronik-Rufname nicht mit Stammbaum-Vollnamen gemergt** (Shelly): In Ihrem Stammbaum taucht dieses Tier mit einem laengeren Namen auf: einmal als „Sammy gen. Shelly v.d. bunten Fellnasen" und einmal als „Sam gen. Shelly v.d. bunten Fellnasen". In der Wurfchronik (Vater vom R5-Wurf) steht aber nur „Shelly". Wie lautet sein vollstaendiger, richtiger Name genau – und heisst er „Sam" oder „Sammy"? - `id=7deec818-ad29-4053-86fb-5e67ac180e70` -- **Tai als verstorben markieren – Todesdatum fehlt in RennerPro-Kopie** (Tai): Danke für den Hinweis! In unserer RennerPro-Kopie ist Tai (Tai gen. Karlchen, geboren am 01.10.2011) leider noch nicht als verstorben eingetragen – dort steht kein Todesdatum. Kannst du mir bitte sagen, wann Tai ungefähr gestorben ist (Monat und Jahr reichen), und falls bekannt woran? Dann trage ich das direkt ein. GerbilManagerWebAPI/Endpoints/GerbilEndpoints.cs,GerbilManagerWebAPI/Import/IngestResolvedService.cs,tools/import/conflict-decisions.json,tools/import/_rpro3.db (Backup 27.06., ohne Todesdatum für Tai) - `id=b222b459-57c5-40ea-88b0-0fa696869c6b` -- **Aaren: Stammbaum wirkt leer (keine Eltern hinterlegt)** (Aaren): Zu Aaren (geb. 20.04.2024): In der App ist er als zugekauftes Zuchttier aus einem Kaufvertrag eingetragen - seine Eltern (Vater/Mutter) sind bei uns bisher nicht hinterlegt. Deshalb bleibt seine Ahnentafel leer, es gibt oberhalb von ihm nichts zum Anzeigen. Weißt du, wie Aarens Mutter und Vater heißen? Dann können wir sie ergänzen und der Stammbaum füllt sich. Falls er mit unbekannten Eltern gekauft wurde, ist der leere Stammbaum korrekt so. - `id=7a9831f6-4d68-41e7-add2-bc06393e6ed6` -- **Drake Jr: Stammbaum leer, weil keine Eltern hinterlegt** (Drake Jr): Ja, Drake Jr hat einen Wurf gezeugt: den 'Q3-Wurf' am 21.06.2014 mit Jacky v.d. Kleinen Chaoten (12 Jungtiere, darunter Quebec, Pünktchen und Kai-Uwe). Der Stammbaum bleibt aber leer, weil bei Drake Jr keine Eltern hinterlegt sind - in der Wurfchronik (Seite 9) war er nur als Elternteil genannt, ohne eigene Abstammung. Weißt du, wer die Eltern von Drake Jr waren? Zum Beispiel: Ist er ein Nachkomme von 'Drake von den kleinen Chaoten' (geb. 06.06.2012)? Dann können wir seinen Stammbaum ergänzen. Drake Jr hat keine verknüpften Eltern (litterId=null); als reiner Elternteil-Eintrag aus der Wurfchronik importiert, fehlt seine eigene Abstammung, daher zeigt der Ahnenbaum nur Platzhalter. - `id=31811914-257b-4c83-bb42-85cc2d140277` -- **Christinas Eltern/Stammbaum aus RennmausPro verknüpfen** (Christina): Ich habe die RennmausPro-Daten durchsucht, finde dort aber keinen Eintrag genau unter dem Namen „Christina". Am ehesten passt eine „Cristina" (ohne „h", weiblich, geboren am 16.09.2014, mit fremder Herkunft). Ist das dieselbe Maus wie deine Christina? Falls ja, sag mir bitte kurz Bescheid – dann verknüpfe ich ihre Eltern/den Stammbaum. Falls nein, nenn mir einfach die Namen von Christinas Mutter und Vater, dann trage ich sie ein. ["tools/import/_rpro3.db", "tools/import/conflict-decisions.json", "tools/import/merge_and_resolve.py", "GerbilManagerWebAPI/Import/Rpro3"] - `id=a78eaa79-db55-4da1-99b0-ed954f9b07c7` -- **Berni ist vermutlich Rufname von Montana (Dublette)** (Berni): Bei diesem Tier „Berni" steht in deiner Wurfchronik der Wurf „M10 – Berni (Montana) + Frodo" – also Berni mit Montana in Klammern. Ist „Berni" einfach der Rufname deiner „Montana v.d. Kleinen Chaoten" (geboren 14.06.2015), sodass beide dasselbe Tier sind? Dann führen wir die beiden Einträge zusammen. - `id=0921dba5-d3a7-47d7-ae95-0c6e535815d1` -- **Zhuāngzǐ: Eltern/Wurf fehlen — Wurf X16 (Nisha × Zenon von Elea) eindeutig bestimmt** (Zhuāngzǐ von den Kleinen Chaoten): Ich habe in der Wurfchronik den Wurf vom 21.02.2020 gefunden: Zhuāngzǐs Eltern sind demnach Nisha (of Black Forest) als Mutter und Zenon von Elea als Vater — im Chronik-Eintrag stehen „Zhuāng Zhōu" (= Zhuāngzǐ) und Zaibunissa ausdrücklich als die behaltenen Jungtiere dieses Wurfs. „Gandari" und „Gaidas" finde ich allerdings nirgends in der App. Kannst du mir kurz sagen, wer Gandari und Gaidas sind und wie sie mit Zhuāngzǐ zusammenhängen (z. B. seine Wurfgeschwister aus genau diesem Wurf, die noch fehlen)? - `id=785851d0-7b37-45a7-adcc-e78db9c3c4b9` -- **Chiko: leerer Stammbaum, reiner Wurfchronik-Partner ohne weitere Daten** (Chiko): Chiko taucht bei dir nur ein einziges Mal auf: als Vater von Sherrys Wurf am 26.04.2018 (aus der Wurfchronik Teil 2, Seite 35). Zu Chiko selbst gibt es sonst keine Angaben - kein Geburtsdatum, keine Farbe und keine eigenen Eltern, deshalb bleibt sein Stammbaum leer. Weisst du noch, woher Chiko kam (z. B. zugekauft und von wem) und welche Farbe er hatte? Dann koennen wir seine Akte ergaenzen. Falls er einfach nur Sherrys Partner ohne weitere Daten war, sag kurz Bescheid - dann lassen wir es so. - `id=bc859109-32df-4506-9f7d-6f6415c00f02` -- **Krümmel (F-Wurf 2010): kein Todesdatum in keiner Quelle** (F-Wurf 2010): Hallo! Ich habe jetzt auch die RennmausPro-Dateien durchgeschaut, wie du vorgeschlagen hast. Dort gibt es zwar zwei Tiere namens „Krümel"/„Krümmel" – die sind aber viel älter (geboren 2003 bzw. 2005) und haben ganz andere Nachkommen, das ist also nicht unser Krümmel aus dem F-Wurf 2010. Für unseren Krümmel (geboren am 21.09.2010) ist damit weder in der handschriftlichen Wurfchronik noch in den RennmausPro-Dateien ein Todesdatum eingetragen. Weißt du vielleicht noch ungefähr, wann er gestorben ist (Jahr oder Alter reicht schon)? Dann tragen wir es gerne nach – ansonsten lassen wir das Feld einfach leer. - `id=21c8ff24-cc03-4508-b6c6-849ab36ba5b2` -- **Percy of little runners: Mutter nicht verknüpft (Stammbaum-Fehlparse + Selbstreferenz)** (Percy of little runners): Bei Percy (of little runners, geboren am 16.12.2017) konnte die automatische Stammbaum-Erkennung die Mutter leider nicht sicher zuordnen: sie hat sich im Diagramm „verheddert" und versehentlich Percy selbst in die Mutter-Rolle gesetzt. Als Vater erkennen wir Rasputin of little runners. Kannst du mir bitte sagen, wie Percys Mutter mit vollem Namen heißt? Dann trage ich sie fest ein. - `id=e87a6da5-0ff1-46b6-b4ec-5d05a1605f5f` -- **Dusty: Stammbaum fehlt – vermutete Dublette (m. Zuchtbock vs. w. Jungtier)** (Dusty): Bei dir gibt es zwei Mäuse mit dem Namen „Dusty": einmal den Zuchtbock Dusty, der 2015 Vater der Würfe mit Bleach, Sanza und Harumi war (genau bei ihm fehlt der Stammbaum), und einmal eine Dusty, die am 22.11.2013 im D3-Wurf aus Hero × Mira geboren wurde. Ist das dieselbe Maus? Falls ja, können wir Dustys Eltern (Hero und Mira) verknüpfen und sein Stammbaum wird angezeigt – die genannte Dusty ist nämlich als Weibchen erfasst, der Zuchtbock aber als Männchen, deshalb sind sie bisher getrennt. - `id=034b4988-5447-4c54-ad28-9a6708133517` - -## Ohne Triage-Ergebnis (nachziehen) - -- Domi `id=7c6d53ac-c353-4a6f-874f-96d363e732d0` -- A-Wurf `id=a555dd0a-f693-487c-8c7f-db8e29b363f5` -- Blacky von PZ Seligenstadt `id=690946e6-714f-4816-b6b9-b76a5da6057a` \ No newline at end of file +# Ticket-Triage — Lauf gegen Prod (2026-07-21) + +Untersuchung: 11 Cluster-Agenten + 1 Hiro-Agent (read-only) gegen lokale Quellen (Stammbaum-xlsx, _rpro3.db, Wurfchronik) + Prod-API. Fotos vom Haupt-Loop ausgewertet. Datenfixes zentral in , lokal regeneriert + verifiziert. + +## A) Daten-Fixes — angewandt + lokal in resolved_import.json verifiziert (bereit für Prod-Re-Ingest) + +- **00715df9** Merle: Merles Farbe/Gencode habe ich aus ihrem eigenen Hinweis uebernommen (Aa chmchm D- ee GG P- Spsp), und ihre Eltern Akane und Bonaparte von den Schlossmaeusen sind bereits richtig eingetragen. Nur ihr genaues Geburtsdatum fehlt mir noch, weil der Stammbaum 'Pukas Kids' bei mir nicht auffindbar ist. +- **06b6734d** Jamie: Die zwei Jamie-Eintraege gehoeren zusammen. Ich fuehre sie zu einem zusammen (Geburtsdatum 27.12.2010, Sohn von Danny und Jana); dadurch landen auch alle seine Wuerfe und Nachzuchten beim richtigen Jamie. +- **1500f0cc** Targa / Druna: Targa hatte zwei Wuerfe, beide mit Marlin of Black Forest: einen am 14. Oktober 2020 und einen am 17. April 2021. Ihre Tochter Druna (Silberagouti) stammt aus dem Wurf vom 17. April 2021 und wird neu angelegt. Beide wurden zusammen an Nicole Nuzzo abgegeben, wo Druna am 29. Juli 2024 und Targa am 24. Oktober 2022 an Tumoren starben. +- **1be92139** Living Force's Idefix (*05.04.2016, 9eee3314): Idefix' Mutter ist Living Force's Vally. Ich habe sie zusammen mit seinem Vater (Living Force's Nando) im Stammbaum ergaenzt. +- **2c49eebf** Socke → Marty von den Kleinen Chaoten: Socke ist dasselbe Tier wie Marty von den Kleinen Chaoten und war versehentlich dreifach angelegt. Die Doppel-Eintraege werden zusammengefuehrt, der Name auf Marty von den Kleinen Chaoten gesetzt und seine richtigen Eltern Milow und Hina eingetragen (die falsche Zuordnung zu Taro faellt weg). +- **3269a722** Targa: Targas Geburtswurf vom 1. April 2019 wird jetzt korrekt als Wurf angelegt: Vater ist South Dakota von den Kleinen Chaoten (Silberschimmel), Mutter ist Chevrolet Corvette of Topolino. Ihre Wurfschwester Tennessee wird demselben Wurf zugeordnet, damit auch deren Eltern stimmen. +- **37cf2f40** Ichika von den Kleinen Chaoten: Bei Ichika waren die Eltern falsch. Richtig sind Jeremy von den Kleinen Chaoten (Vater) und Inory von den Kleinen Chaoten (Mutter) - das wird korrigiert. +- **44a9b677** Hiro: Hiro- und Mino-Doppeleinträge zusammengeführt (der vollständige mit Stammbaum bleibt); Würfe mit Skarlett und Mino×Misaki hängen jetzt am richtigen Tier; April-Wurf der richtigen Skarlett zugeordnet. +- **45576826** Blacky von Privatzucht Seligenstadt: Es gab Blacky versehentlich doppelt (einmal als "von PZ Seligenstadt", einmal als "von Privatzucht Seligenstadt"). Beide Eintraege sind zu einem zusammengefuehrt; der Name "Blacky von Privatzucht Seligenstadt" bleibt stehen. Geburts- und Todesdatum stimmen bei beiden ueberein, es gehen keine Daten verloren. +- **49548ac0** Jana: Jana und Jana of little longnoses sind dasselbe Tier. Ich fuehre die beiden zu einem Eintrag zusammen (Name: Jana of little longnoses, Herkunft Clan of little longnoses), mit ihren Eltern Milon und Darina gen. Talina. +- **5fe7c884** Ethan von den Kleinen Chaoten: Bei Ethan fehlte die Mutter. Laut Stammbaum sind seine Eltern Braden gen. Scotty (Vater, schon eingetragen) und Shika von den Kleinen Chaoten (Mutter). Shika wird angelegt und als Mutter ergaenzt. +- **71e3e6a1** Roni: Ronis Mutter ist Bonnie gen. Amika von den Kleinen Chaoten (sein Vater Kaleo gen. Halio ist bereits eingetragen). Sie wird als Mutter angelegt und bei Roni ergaenzt. Ihr eigenes Geburtsdatum und ihre Eltern stehen in den Unterlagen leider nicht. +- **7f9b3724** Sakura = Gwen gen. Sakura of little Angels: Sakura und 'Gwen gen. Sakura of little Angels' werden zu einem Tier zusammengefuehrt (Herkunft: Clan of little Angels), sodass ihr Geburts- und Sterbedatum, ihre Eltern und ihre Wuerfe an einem einzigen Eintrag haengen. +- **873aca5d** Jacky v.d. Kleinen Chaoten: Das ist derselbe Hinweis wie beim anderen Jacky-Ticket - Jackys Eltern werden auf Jack von Privatzucht Maintal (Vater) und Holly gen. Lilly of Ulmer Strolche (Mutter) korrigiert; dieses Ticket kann zusammen mit dem anderen geschlossen werden. +- **918a9a3f** Jacky v.d. Kleinen Chaoten: Jackys Eltern werden korrigiert: Vater ist Jack von Privatzucht Maintal und Mutter Holly gen. Lilly of Ulmer Strolche (bisher faelschlich Paul und Akina). Damit bekommen Jacky und ihr Bruder Jack Jr dieselben Eltern, genau wie du beschrieben hast. +- **9325e316** Domi (D-Wurf 2010): Bei Domi wird jetzt eingetragen, dass er zusammen mit seinem Wurfbruder Dastan an Susanne und Nina Thomas abgegeben wurde. +- **9ad13a73** Joghurt v. Privat (*06.09.2013, bab013c9): Joghurt kam aus privater Haltung; seine Eltern sind unbekannt. Bei uns war faelschlich ein erfundener Wurf mit BlackFire als Vater eingetragen -- das habe ich entfernt, seine Eltern bleiben jetzt korrekt unbekannt. +- **9bbfb6a0** Stacy: Wir uebernehmen Stacys Daten aus RennerPro: Sie ist die Gold-Schecke, geboren am 23.10.2012, Herkunft Gelnhausener Privatzucht. Ihre Farbe und ihr Erbbild werden ergaenzt und ihr Stammbaum mit den Eltern Vati (Goldfuchs) und Mutti (Agouti-Schecke) wird eingetragen. +- **a50116bf** Kuke: Kuke war doppelt angelegt; die beiden Eintraege werden zusammengefuehrt, sodass Farbe (Schwarz-Schecke), Genotyp und ihr Todestag (6.4.2011, gestorben bei einem Ausbruch-Unfall) an einem Tier stehen. Ihre Eltern sind laut RennerPro Marc und Sara. Ihre Nachzucht mit Blacky (Alvin, Flori, Eddy, Feivel) ist bereits richtig. +- **aa5238b6** Eliza: Es gab zwei Tiere namens Eliza. Die hier gezeigte Eliza (geboren 2010, gestorben 2011) war nie ein Zuchttier und ist NICHT die Mutter des J5-Wurfs — das war die spaetere Eliza (geboren 2014). Der J5-Wurf haengt bereits an der richtigen Eliza; nur der falsche Hinweis auf ihrer Seite wird entfernt. +- **bbf2898b** Malou von den Kleinen Chaoten (frueher 'Schlaganfall Sommer 2019', *10.09.2015): Malou war bei uns versehentlich zweimal angelegt. Ich habe die beiden Eintraege zusammengefuehrt; dabei bekommt Malou ihre richtigen Eltern Koji (Vater) und Zoe (Mutter), und der alte Zusatz 'frueher Schlaganfall Sommer 2019' faellt weg. +- **c092f8d2** Jamie: Die beiden Jamie-Eintraege werden zu einem zusammengefuehrt (Geburtsdatum 27.12.2010, aus dem J-Wurf, Sohn von Danny und Jana). Weitere Tiere mit Namen Jamie (eines von 2012, und Jamie Lannister von 2017) sind eigene Tiere und bleiben getrennt. +- **d02c2a1b** Blacky von Privatzucht Seligenstadt: Bei Blacky standen zwei falsche Jungtiere als Nachzucht: Antares of Ulmer Strolche und Chagan of Ulmer Strolche. Beide sind nicht von Blacky. Laut RennerPro stammt Antares von Grisu und Carlotta, Chagan von Akuma und Bekter. Sie werden von Blacky geloest; Blackys echte Nachzucht mit Kuke (Alvin, Flori, Eddy, Feivel) bleibt. +- **d8e6f1a2** Living Force's Idefix (*05.04.2016, 9eee3314): Bei Idefix war der Stammbaum kaputt: Idefix war irrtuemlich als eigener Elternteil eingetragen. Ich habe seine echten Eltern eingetragen: Vater Living Force's Nando und Mutter Living Force's Vally (beide aus der Zucht Living Force). +- **dc64ac0d** Wurf von Danny von PZ Maintal + Jana of little longnoses (J-Wurf): Es gibt keinen Wurf 'Hitze' — das war nur ein Tippfehler. Der Danny-und-Jana-Wurf steht momentan doppelt in der Liste. Sobald die beiden Jana-Eintraege zusammengefuehrt sind, verschmelzen die zwei Wuerfe automatisch zum J-Wurf, und Jamie steht dort zusammen mit seinen Geschwistern Jack, Chip und Jules. +- **e0a0c304** Kuke: Das Tier "Kruke" heisst in Wirklichkeit "Kuke" — der Name wird korrigiert. +- **e96db323** Jamie: Die beiden Jamie-Eintraege sind dasselbe Tier (Sohn von Jana und Danny) und werden zu einem zusammengefuehrt (Geburtsdatum 27.12.2010). +- **f8d55b7a** Stacy: Gleiche Sache wie beim anderen Stacy-Ticket: Wir tragen die Gold-Schecke Stacy mit Geburtsdatum 23.10.2012, Herkunft Gelnhausener Privatzucht, Farbe/Erbbild und ihren Eltern Vati und Mutti aus RennerPro ein. +- **ff8d6bd7** Blacky von Privatzucht Seligenstadt: Bei Blacky wird die "Extern"-Markierung entfernt. Er zaehlt als eigenes Bestandstier (zugekauftes Zuchttier), weil er bei dir mit Kuke Nachzucht hatte. + +## B) Bereits korrekt (nur bestätigen) + +- **8b1dbd19** Gaida von den Kleinen Chaoten: Alle Daten aus dem frueheren Gaida-Ticket sind uebernommen: ihr Todesdatum 09.10.2025, die Todesursache Lungenentzuendung mit Fieber, das Abgabedatum 05.01.2025, die Abnehmerin Caroline Emmert und ihre Eltern (eine Geschwisterverpaarung aus dem Wurf von Nisha und Zenon). Nur ihr Status stand faelschlich noch auf 'Zucht' statt 'verstorben' - das wird zusammen mit der anderen Korrektur behoben. +- **bd351aa2** Jana: Janas Herkunft steht bereits richtig als Clan of little longnoses. Ihr Sohn Feivel (geboren 25.12.2011) ist bereits als Nicht-Zuchttier gefuehrt — der Feivel, der in der Zucht war, ist ein anderer (der Sohn von Blacky und Kuke). Nur ihr Sohn Jamie war in der Zucht. Sobald die beiden Jana-Eintraege zusammengefuehrt sind, zeigt Janas Seite genau das. + +## C) Rückfrage nötig (→ NeedsInfo) + +- **5851bb94** Spike: Die Verpaarung Spike x Jacky habe ich in deiner Wurfchronik gefunden: dort steht der D7-Wurf (geboren am 14.08.2015, Jungtiere Smartie, Cookie und Cloe) mit den Eltern Spike und Jacky. Spike (geboren 03.04.2015) waere zu diesem Zeitpunkt aber erst gut 4 Monate alt gewesen. War der Vater dieses Wurfs ein anderes Tier - und wenn ja, wer war der richtige Vater von Smartie, Cookie und Cloe? Oder soll die Verpaarung mit Jacky bei Spike einfach entfernt werden? +- **a547be62** Eliza: Bei Eliza (geboren am 20.08.2010, Tochter von Blacky und Kuke, verstorben 6.4.2011) stimmt der Gencode nicht — der angezeigte Wert ist nur automatisch aus dem Farbschlag "Schwarz" abgeleitet. Wie lautet ihr korrekter Gencode? (Du hattest ihn frueher in deiner Liste aller Blacky-&-Kuke-Nachkommen genannt; magst du ihn fuer genau diese Eliza kurz wiederholen?) +- **f212b73c** Bonaparte von den Schlossmaeusen: Bonapartes Eltern (und Merles genaues Geburtsdatum) finde ich nur im Stammbaum 'Pukas Kids', den ich hier leider nicht oeffnen kann - die Datei ist bei mir nicht vorhanden. Magst du mir Bonapartes Vater und Mutter direkt mit Namen nennen? Und falls im selben Stammbaum vermerkt: an welchem Datum ist Merle geboren? Alternativ kannst du mir die Excel-Datei 'Stammbaum von Pukas Kids' nochmal schicken. +- **fbe53c6b** Fumi von den Kleinen Chaoten: Ich finde Fumi weder in den Stammbaum-Excel-Dateien noch in der Wurfchronik als Jungtier - dort steht sie nur als Mutter (zusammen mit Roni). Aus welchem Wurf stammt Fumi bzw. wer sind ihre Eltern (gern mit Geburtsdatum)? + +## D) Code-Fixes (Deploy nötig, KEIN Re-Ingest) + +- **37ab228a** Gaida von den Kleinen Chaoten [backend]: Gaida (04358c1f) hat in Prod dateOfDeath=2025-10-09, goHomeDate=2025-01-05, receiverContactId (Caroline Emmert) UND causeOfDeath gesetzt, aber Status=Breeding -> erscheint deshalb unter dem Default-Filter status=['Breedi +- **4b9f49fb** Ungeklärt-Wurf 03.03.2011 (= M-Wurf) [import-logic]: Der Wurf 2c545f8a ('Ungeklaert', 03.03.2011, Eltern Blacky von PZ Seligenstadt x Charlett of little longnoses) ist eine Fehl-Verschmelzung aus 3 Wurfchronik-Quellen mit demselben Datum: (1) Teil1 S.15 = der ECHTE M-Wurf +- **8d259edf** Gale von den Kleinen Chaoten (frueher Drake Jr, 7f09dd7b) [import-data]: Umbenennung Drake Jr -> 'Gale von den Kleinen Chaoten', Notiz und Eltern (Vater Drake von den Kleinen Chaoten, Mutter Holly gen Lilly of Ulmer Strolche) sind bereits korrekt gesetzt (conflict-decisions + Prod). OFFEN sin +- **9f92c834** CoCo [frontend]: CoCo (5e1931b5, *17.04.2010, Genotyp aa CC DD EE GG PP Spsp rere = Schwarz-Schecke) IST eine Schecke (Sp-Locus Spsp), aber ihr Feld SpottingType ist null. Die Tier-Akte (GerbilDetailPage.tsx Zeile 506: `{g.spottingType & +- **f89e95ad** A-Wurf (Blacky von PZ Seligenstadt x Kruke/Kuke, *18.02.2010) [genetics-engine]: Zwei Ursachen. (1) DATEN-Voraussetzung: Die Mutter des A-Wurfs (prod e4898ccc 'Kruke', = die Kuke aus Ticket a50116bf/e0a0c304, *18.09.2009) hat in prod genotype=null. Die erwarteten Farbschlaege werden nur berechnet, we \ No newline at end of file diff --git a/gerbil-manager-web/src/pages/GerbilDetailPage.tsx b/gerbil-manager-web/src/pages/GerbilDetailPage.tsx index 093a649..cc0e4e0 100644 --- a/gerbil-manager-web/src/pages/GerbilDetailPage.tsx +++ b/gerbil-manager-web/src/pages/GerbilDetailPage.tsx @@ -503,13 +503,24 @@ export default function GerbilDetailPage() { '—' )} - {g.spottingType && ( - - {de.pages.gerbils.spottingTypes[ - g.spottingType as keyof typeof de.pages.gerbils.spottingTypes - ] || g.spottingType} - - )} + {/* Ticket 9f92c834 (CoCo): Scheckungsart bei jeder Schecke zeigen — auch wenn noch + nicht erfasst (dann Platzhalter), damit sichtbar ist, dass es eine Schecke ist und + welche Art. Schecke erkannt am Sp-Allel im Genotyp oder am Farbschlag-Namen. */} + {(() => { + const isSchecke = + /Sp/.test(g.genotype ?? '') || + (storedColorName?.toLowerCase().includes('schecke') ?? false); + if (!g.spottingType && !isSchecke) return null; + return ( + + {g.spottingType + ? de.pages.gerbils.spottingTypes[ + g.spottingType as keyof typeof de.pages.gerbils.spottingTypes + ] || g.spottingType + : de.pages.gerbils.spottingTypes.notRecorded} + + ); + })()} {showEnclosure && ( {lookup(enclosureName, g.enclosureId)} )} diff --git a/gerbil-manager-web/src/strings/de.ts b/gerbil-manager-web/src/strings/de.ts index 25053b1..e641344 100644 --- a/gerbil-manager-web/src/strings/de.ts +++ b/gerbil-manager-web/src/strings/de.ts @@ -204,6 +204,7 @@ export const de = { }, spottingTypes: { none: '— keine Scheckungsart —', + notRecorded: 'noch nicht erfasst', Punktschecke: 'Punktschecke', Kragenschecke: 'Kragenschecke', Ansatzschecke: 'Ansatzschecke', diff --git a/tools/import/conflict-decisions.json b/tools/import/conflict-decisions.json index e7d5575..93f7f78 100644 --- a/tools/import/conflict-decisions.json +++ b/tools/import/conflict-decisions.json @@ -345,7 +345,13 @@ "decision": "father is Danny von Privatzucht Maintal (Wurfchronik J-Wurf: 'Eltern: Danny + Jana'). Chart-position had wrong/age-impossible parents.", "father": "Danny von PZ Maintal", "mother": "Jana of little longnoses", - "source": "Züchterin 2026-06-22 — Ticket #31" + "source": "Züchterin 2026-06-22 — Ticket #31 + Tickets 06b6734d/e96db323/c092f8d2 (Merge Stub)", + "mergeExternalRefs": [ + [ + "stammbaum-jamiekleinenchaoten-27122010", + "Wurfchronik Teil 1_page_0022.md-page_0022_parent_jamie" + ] + ] }, { "name": "Silver von den kleinen Chaoten", @@ -466,11 +472,17 @@ "name": "Hiro of Golden Lights", "dob": "13.02.2012", "decision": "Eltern aus RennmausPro ergänzt: Wynn × Amidala", - "source": "Züchterin 2026-06-23 — Ticket f3ad5ec9 (Hiro-Ahnen aus RennmausPro)", + "source": "Züchterin 2026-06-23 — Ticket f3ad5ec9 (Hiro-Ahnen aus RennmausPro) + Ticket 44a9b677 (Merge Stub)", "father": "Wynn", "fatherDob": "16.05.2011", "mother": "Amidala", - "motherDob": "06.07.2011" + "motherDob": "06.07.2011", + "mergeExternalRefs": [ + [ + "stammbaum-hirogoldenlights-13022012", + "Wurfchronik Teil 1_page_0041.md-1603ce65-27a3-41bb-ad68-6c84c17b1ee0" + ] + ] }, { "name": "Akane", @@ -500,12 +512,16 @@ { "name": "Targa", "dob": "", - "decision": "Elternteil Caprice (Chevrolet Corvette of Topolino) und Zuchttier-Status per Breeder-Antwort eingetragen.", + "correctDob": "01.04.2019", "gender": "female", - "source": "Züchterin 2026-06-29 — Ticket 0877d9d6", - "mother": "Chevrolet Corvette of Topolino", + "farbschlag": "Silberagouti", "isResident": true, - "notes": "Tochter von Caprice (Chevrolet Corvette of Topolino)." + "deceased": true, + "dateOfDeath": "24.10.2022", + "receiver": "Nicole Nuzzo", + "notes": "Eigene Nachzucht aus Wurf R14 (*01.04.2019, South Dakota von den Kleinen Chaoten x Chevrolet Corvette of Topolino). Mit Tochter Druna an Nicole Nuzzo abgegeben; verstorben 24.10.2022 an Tumoren im Bauchraum.", + "decision": "Vater South Dakota + DOB/Farbe/Tod/Abnehmer aus Ticket ergänzt; Mutter-only-Override durch R14-addLitter ersetzt.", + "source": "Tickets 3269a722/1500f0cc/0877d9d6" }, { "name": "Mamono von den Kleinen Chaoten", @@ -960,14 +976,18 @@ { "externalRef": "Wurfchronik Teil 1_page_0013.md-g1000000-0000-0000-0000-000000000009", "isResident": true, - "decision": "Domi: als eigenes Zuchttier gekennzeichnet. externalRef statt name+dob, sonst würde die name-only-Regel einen zweiten gleichnamigen Domi mit erfassen.", - "source": "KI-Triage 2026-07-12 — Ticket 7c6d53ac-c353-4a6f-874f-96d363e732d0" + "decision": "Domi: als eigenes Zuchttier gekennzeichnet. externalRef statt name+dob, sonst würde die name-only-Regel einen zweiten gleichnamigen Domi mit erfassen. Mit Wurfbruder Dastan an dieselben Abnehmer (Susanne + Nina Thomas) abgegeben.", + "source": "KI-Triage 2026-07-12 — Ticket 7c6d53ac-c353-4a6f-874f-96d363e732d0 + Ticket 9325e316", + "receiver": "Susanne + Nina Thomas" }, { "name": "Kruke", "isResident": true, - "decision": "Als eigenes Zuchttier gekennzeichnet (Name und Geburtsdatum bleiben unverändert).", - "source": "KI-Triage 2026-07-12 — Ticket 34220307-23bc-420c-80f8-d273615caffd + b7016e5f-9985-4f17-a3f6-2048289958aa" + "decision": "'Kruke' war ein Wurfchronik-Tippfehler; korrekt 'Kuke' (rpro3). Bestandstier. Todesdatum 06.04.2011 (Ausbruch-Unfall, rpro3). Farbe/Genotyp (Anthrazit-Schecke vs. Schwarz) folgt separat via Ticket f89e95ad, sobald die Zuechterin Saphir/Platin geklaert hat. KEIN Merge mit dem Ahnen-Record 'Kuke von Marion Knoss' — der Merge nullte die Mutter-Verknuepfung aller Blacky-x-Kuke-Wuerfe.", + "source": "KI-Triage 2026-07-12 — Ticket 34220307-23bc-420c-80f8-d273615caffd + b7016e5f-9985-4f17-a3f6-2048289958aa + Tickets e0a0c304/a50116bf", + "externalRef": "decision-kruke", + "dateOfDeath": "06.04.2011", + "renameTo": "Kuke" }, { "name": "Genna", @@ -992,7 +1012,14 @@ "genotype": "aa c[chm]c[h] DD ee[f] Gg Pp spsp", "originBreeder": "Clan of little longnoses", "decision": "Jana-Daten aus RennmausPro: DOB 07.12.2009, Colourpoint-Kohlfuchsschimmel-Hell, Herkunft Clan of little longnoses.", - "source": "Zuechterin 2026-07-13 - Ticket 4e7e86bc" + "source": "Zuechterin 2026-07-13 - Ticket 4e7e86bc + Ticket 49548ac0 (Merge + rename)", + "renameTo": "Jana of little longnoses", + "mergeExternalRefs": [ + [ + "Wurfchronik Teil 1_page_0017.md-adb0649c-a56a-495e-9cce-4fa16505c1dd", + "stammbaum-janalittlelongnoses-07122009" + ] + ] }, { "name": "Danny von Maintaler PZ", @@ -1026,13 +1053,16 @@ "source": "Zuechterin - Ticket 32d57d3d" }, { - "name": "Blacky von PZ Seligenstadt", - "externalRef": "stammbaum-blackypzseligenstadt-23072009", - "father": "Snoopy", - "mother": "Bella", - "decision": "Eltern aus RennmausPro: Snoopy x Bella.", - "source": "Ticket cf6e2c99", - "motherDob": "01.01.2008" + "externalRef": "stammbaum-blackyprivatzuchtseligenstadt-23072009", + "mergeExternalRefs": [ + [ + "stammbaum-blackyprivatzuchtseligenstadt-23072009", + "stammbaum-blackypzseligenstadt-23072009" + ] + ], + "isResident": true, + "decision": "Zwei Schreibvarianten desselben Blacky (*23.07.2009) zusammengeführt; Keep = 'Blacky von Privatzucht Seligenstadt'. Zugekauftes Zuchttier (Nachzucht mit Kuke 'von den Kleinen Chaoten') -> Bestandstier, Extern-Badge entfernen. Eltern Snoopy x Bella aus gemeinsamem Geburtswurf.", + "source": "Ticket cf6e2c99 + Tickets 45576826/ff8d6bd7" }, { "name": "Dante", @@ -1143,13 +1173,6 @@ "genotype": "aa Cc[chm] DD ee[f] Gg Pp spsp", "source": "Ticket 395667eb: Farbschlag Kohlfuchsschimmel, hell (RennerPro Eddy, eigene Zucht *13.06.2011)" }, - { - "name": "Kuke", - "dob": "18.09.2009", - "genotype": "aa CC DD Ee GG Pp Spsp", - "source": "Ticket 21ce7734 (Schwarz-Schecke) + 17d2d455 (Herkunft Marion Knoss)", - "originBreeder": "Marion Knoss" - }, { "name": "Julien", "isResident": false, @@ -1158,7 +1181,13 @@ { "name": "Stacy", "deceased": true, - "source": "Ticket 6b676f17: verstorben" + "correctDob": "23.10.2012", + "genotype": "Aa CC D- EE GG pp Spsp", + "farbschlag": "Gold-Schecke", + "gender": "female", + "originBreeder": "Gelnhausener Privatzucht", + "decision": "Eindeutige Stacy = Gold-Schecke (rpro3 rid 79), Wurf mit Sammy (E2-Wurf 16.03.2013). Daten aus RennerPro: *23.10.2012, Fcode Aa CC D- EE GG pp Spsp, Herkunft Gelnhausener Privatzucht, Eltern Vati (Goldfuchs) x Mutti (Agouti-Schecke).", + "source": "Tickets 9bbfb6a0/f8d55b7a" }, { "externalRef": "stammbaum-rayavdbuntenfellnasen", @@ -1173,7 +1202,10 @@ { "name": "Merle", "originBreeder": "Clan of Black Forest", - "source": "Ticket a8f11ac0" + "source": "Ticket a8f11ac0 + Ticket 00715df9", + "externalRef": "decision-merle", + "genotype": "Aa c[chm]c[chm] D- ee GG P- Spsp", + "decision": "Gencode aus Merles eigenem Ticket a8f11ac0. Eltern Akane x Bonaparte bereits über addLitter verknüpft. Geburtsdatum in keiner lesbaren Quelle -> offen." }, { "name": "Gino", @@ -1194,14 +1226,25 @@ }, { "name": "Jacky v.d. Kleinen Chaoten", - "father": "Jack Jr von Privatzucht Maintal", - "mother": "Holly gen Lilly of Ulmer Strolche", - "source": "Ticket 918a9a3f/873aca5d" + "dob": "24.07.2013", + "father": "Jack von PZ Maintal", + "fatherDob": "20.03.2012", + "mother": "Holly gen. Lilly of Ulmer Strolche", + "motherDob": "23.09.2012", + "decision": "Vater 'Jack von Privatzucht Maintal', Mutter 'Holly gen. Lilly of Ulmer Strolche'; Jacky & Jack Jr Vollgeschwister (rpro3). Früher fälschlich Jack Jr als Vater / Paul+Akina als Eltern.", + "source": "Tickets 918a9a3f/873aca5d" }, { "name": "Schlaganfall Sommer 2019", - "renameTo": "Malou von den kleinen Chaoten", - "source": "Ticket 2769d3a5" + "dob": "10.09.2015", + "mergeExternalRefs": [ + [ + "stammbaum-maloukleinenchaoten-10092015", + "stammbaum-schlaganfallsommer2019-10092015" + ] + ], + "decision": "Duplikat: 'Malou von den Kleinen Chaoten' (korrekte Eltern Koji x Zoe via echtem I7-Wurf) und 'Schlaganfall Sommer 2019' (defekter Virtual-Wurf) sind dasselbe Tier *10.09.2015. Merge -> Malou behalten; der Zusatz 'früher Schlaganfall Sommer 2019' fällt weg.", + "source": "Ticket bbf2898b" }, { "name": "Drake Jr", @@ -1225,6 +1268,56 @@ "name": "Suen von Zooparadies (Gießen)", "genotype": "aa C- D- ee G- pp Spsp", "source": "Ticket f2433ab9 (RennerPro: Rotfuchs-Schecke; Eltern dort nicht erfasst)" + }, + { + "externalRef": "Wurfchronik Teil 1_page_0013.md-g1000000-0000-0000-0000-000000000011", + "isResident": false, + "notes": "Schwarz-Schecke; Tochter von Blacky x Kuke (E-Wurf 2010); verstorben 06.04.2011 (Ausbruch-Unfall). War nie Zuchttier. NICHT die Mutter des J5-Wurfs — das ist die spätere Eliza (*13.02.2014).", + "decision": "Zweite/2010er Eliza von der J5-Mutter getrennt; irreführende 'Mutter des J5-Wurfs'-Notiz entfernt.", + "source": "Ticket aa5238b6" + }, + { + "externalRef": "stammbaum-gwengensakuralittleangels-17122011", + "mergeExternalRefs": [ + [ + "gwengensakuralittleangels-17122011", + "page_0022_parent_sakura" + ] + ], + "originBreeder": "Clan of little Angels", + "isResident": true, + "decision": "Sakura (Wurfchronik-Elternteil A2/D2/H2, ohne DOB) und 'Gwen gen. Sakura of little Angels' (*17.12.2011, +05.01.2015, Eltern Chap of little Angels x Julietta gen. Becky of LennyLengo) sind dasselbe Tier. Gwen-Record kanonisch, Wurfchronik-Sakura einmergen. Herkunft Clan of little Angels.", + "source": "Ticket 7f9b3724" + }, + { + "name": "Marty v.d. Kleinen Chaoten", + "dob": "27.11.2014", + "renameTo": "Marty von den Kleinen Chaoten", + "mergeExternalRefs": [ + [ + "stammbaum-martyvdkleinenchaoten-27112014", + "430976e2-f0bb-5bc1-8875-496e94e7a0a9" + ], + [ + "stammbaum-martyvdkleinenchaoten-27112014", + "M4-Wurf_Socke_Marty" + ] + ], + "notes": "Hieß früher Socke, umbenannt in Marty von den Kleinen Chaoten.", + "decision": "Socke (Wurfchronik S.27, fälschlich Kind L3-Wurf/Vater Taro) und 'Socke (Marty)' (M4-Wurf) = kanonisches Stammbaum-Marty (*27.11.2014, M4-Wurf, Vater Milow). Beide Duplikate in Marty mergen; Sockes falsche L3/Taro-Verknüpfung fällt weg.", + "source": "Ticket 2c49eebf" + }, + { + "name": "Mino von den Kleinen Chaoten", + "dob": "26.02.2015", + "mergeExternalRefs": [ + [ + "stammbaum-minokleinenchaoten-26022015", + "Wurfchronik Teil 1_page_0051.md-868ca4ff-730c-43b9-a9a3-5cde654c607f" + ] + ], + "decision": "Leeres Mino-Stub (ohne DOB) in vollständigen Mino mergen; L6-/X6-Wurf (Mino×Misaki) hängen dadurch am vollständigen Mino.", + "source": "Ticket 44a9b677" } ], "addAnimals": [ @@ -1614,6 +1707,56 @@ "zucht": "Privatzüchterin Alice Langevel", "dob": "01.02.2010", "source": "RennerPro-Ahne (Tickets df0dae90 / 8293e573)" + }, + { + "name": "Druna", + "gender": "female", + "dob": "17.04.2021", + "isResident": false, + "farbschlag": "Silberagouti", + "receiver": "Nicole Nuzzo", + "notes": "Tochter von Targa x Marlin of Black Forest (Wurf H19, *17.04.2021). Mit Mutter Targa an Nicole Nuzzo abgegeben; dort am 29.07.2024 an einem Tumor im Bauchraum verstorben.", + "source": "Ticket 1500f0cc" + }, + { + "name": "Grisu of Ulmer Strolche", + "gender": "male", + "isResident": false, + "zucht": "Clan of Ulmer Strolche", + "decision": "Vater von Antares (rpro3), externer Ahn.", + "source": "Ticket d02c2a1b" + }, + { + "name": "Akuma of Ulmer Strolche", + "gender": "male", + "isResident": false, + "zucht": "Clan of Ulmer Strolche", + "decision": "Vater von Chagan (rpro3), externer Ahn.", + "source": "Ticket d02c2a1b" + }, + { + "name": "Bekter of Ulmer Strolche", + "gender": "female", + "isResident": false, + "zucht": "Clan of Ulmer Strolche", + "decision": "Mutter von Chagan (rpro3), externe Ahnin.", + "source": "Ticket d02c2a1b" + }, + { + "name": "Shika von den Kleinen Chaoten", + "gender": "female", + "isResident": true, + "zucht": "Zucht der kleinen Chaoten", + "notes": "Mutter von Ethan. Gencode lt. Stammbaum: aa Cc[chm] D- e[f]e[f] G- pp Spsp. Verstorben 01.09.2024.", + "source": "Ticket 5fe7c884" + }, + { + "name": "Bonnie gen. Amika von den Kleinen Chaoten", + "gender": "female", + "isResident": true, + "zucht": "Zucht der kleinen Chaoten", + "notes": "Mutter des N20-Wurfs (mit Kaleo gen. Halio); auch Mutter des A21-Wurfs. Eigene Eltern/Geburtsdatum unbekannt.", + "source": "Ticket 71e3e6a1" } ], "suppressExternalRefs": [ @@ -1646,7 +1789,8 @@ "Wurfchronik Teil 1_page_0054.md-6a1a1054-0001-4000-8000-000000000001", "Wurfchronik Teil 1_page_0054.md-6a1a1054-0002-4000-8000-000000000002", "Wurfchronik Teil 1_page_0054.md-6a1a1054-0003-4000-8000-000000000003", - "stammbaum-verpaarungchevroletcamarotopolino" + "stammbaum-verpaarungchevroletcamarotopolino", + "Stammbaum-virtual-a7c4a776-e7fc-5672-a004-8aaf5534db97" ], "_doc_suppress": "suppressExternalRefs: ExternalRefs von bestätigten Phantom-/OCR-Fehllesungs-Dubletten, die final aus dem Payload entfernt werden. Hier: 'Muga' (OCR-Fehllesung von 'Nduga' aus Wurfchronik Seite 0054, Ticket a9c0d0f9, von der Züchterin bestätigt) samt ihrem kinderlosen Phantom-Wurf 'Muga × Benjiro'. Die echten Kinder (Luna/Cleo/Izumi/Pinky) gehören bereits korrekt zum echten Wurf H13 (Nduga × Benjiro, 07.02.2018).", "addLitters": [ @@ -1857,6 +2001,41 @@ ], "showInChronicle": false, "source": "Ticket 8293e573 (Gwen/Sakuras Grosseltern)" + }, + { + "name": "R14-Wurf (Chevrolet Corvette x South Dakota)", + "mother": "Chevrolet Corvette of Topolino", + "father": "South Dakota von den Kleinen Chaoten", + "date": "01.04.2019", + "totalBorn": 4, + "children": [ + { + "name": "Targa" + }, + { + "name": "Tennessee von den Kleinen Chaoten", + "dob": "01.04.2019" + } + ], + "showInChronicle": true, + "notes": "R14 aus Wurfchronik (*01.04.2019): 4 Jungtiere (1M/3W). Frieda + Böckchen ohne eigenen Datensatz. Eltern South Dakota von den Kleinen Chaoten (Silberschimmel) x Chevrolet Corvette of Topolino.", + "source": "Tickets 3269a722/1500f0cc" + }, + { + "name": "H19-Wurf (Targa x Marlin of Black Forest)", + "mother": "Targa", + "father": "Marlin of Black Forest", + "date": "17.04.2021", + "totalBorn": 2, + "children": [ + { + "name": "Druna", + "dob": "17.04.2021" + } + ], + "showInChronicle": true, + "notes": "H19 aus Wurfchronik (*17./18.04.2021): 2 Weibchen. Überlebende Silberagouti-Tochter = Druna.", + "source": "Ticket 1500f0cc" } ], "litterParents": [ @@ -1900,6 +2079,63 @@ "mother": "Akina von den Kleinen Chaoten", "motherDob": "13.04.2013", "source": "Ticket f3dd4e7e (RennerPro: Kojis Mutter = Akina)" + }, + { + "litterName": "Wurf von Carlotta v.d. Rastatter Fellnasen + Blacky von Privatzucht Seligenstadt", + "date": "16.01.2010", + "father": "Grisu of Ulmer Strolche", + "mother": "Carlotta v.d. Rastatter Fellnasen", + "source": "Ticket d02c2a1b (rpro3: Antares = Grisu x Carlotta, nicht Blacky)" + }, + { + "litterName": "Wurf von Carlotta v.d. Rastatter Fellnasen + Blacky von Privatzucht Seligenstadt", + "date": "20.02.2011", + "father": "Akuma of Ulmer Strolche", + "mother": "Bekter of Ulmer Strolche", + "source": "Ticket d02c2a1b (rpro3: Chagan = Akuma x Bekter)" + }, + { + "litterName": "Wurf von Living Force's Vally + Living Force's Idefix", + "date": "05.04.2016", + "father": "Living Force's Nando", + "fatherDob": "07.03.2014", + "mother": "Living Force's Vally", + "motherDob": "01.11.2014", + "source": "Tickets d8e6f1a2/1be92139" + }, + { + "date": "09.07.2020", + "mother": "Shika von den Kleinen Chaoten", + "source": "Ticket 5fe7c884" + }, + { + "date": "19.04.2020", + "father": "Jeremy von den Kleinen Chaoten", + "fatherDob": "11.09.2016", + "mother": "Inory von den Kleinen Chaoten", + "motherDob": "07.07.2019", + "source": "Ticket 37cf2f40" + }, + { + "litterName": "N20-Wurf", + "date": "27.01.2022", + "mother": "Bonnie gen. Amika von den Kleinen Chaoten", + "source": "Ticket 71e3e6a1" + }, + { + "litterName": "M4-Wurf", + "date": "27.11.2014", + "mother": "Hina von den Kleinen Chaoten", + "source": "Ticket 2c49eebf" + }, + { + "litterName": "Wurf Hiro + Skarlett", + "date": "05.04.2015", + "father": "Hiro of Golden Lights", + "fatherDob": "13.02.2012", + "mother": "Skarlett von den Kleinen Chaoten", + "motherDob": "14.07.2013", + "source": "Ticket 44a9b677: April-Wurf hatte fälschlich Ahnin Skarlett *2009 als Mutter" } ], "_doc_litterParents": "litterParents: erzwingt Vater/Mutter eines BESTEHENDEN Wurfs (Match litterName+date), via name+dob. Für Namensvetter-/verstorbenes-Elternteil-Korrekturen, die der Resolver nicht selbst trifft.", @@ -1942,6 +2178,37 @@ "name": "Malina", "dob": "22.03.2010", "source": "Ticket df0dae90: kompletter Stammbaum aus RennerPro" + }, + { + "name": "Stacy", + "dob": "23.10.2012", + "source": "Tickets 9bbfb6a0/f8d55b7a: kompletter Stammbaum (Vati x Mutti) aus RennerPro" } - ] + ], + "litterChildren": [ + { + "date": "03.03.2011", + "matchChild": "Cotton", + "keep": [ + "Cotton", + "Mayu", + "Kiwi", + "Mio", + "Mo" + ], + "renameTo": "M-Wurf", + "note": "Ticket 4b9f49fb: Wurf hieß fälschlich \"Ungeklärt\" und hatte durch Merge einer Abnehmer-Sammelseite zu viele Jungtiere. Korrekt M-Wurf (Blacky x Charlett, 03.03.2011) mit den 5 benannten Jungtieren; Enzo/Cesar/Jack/Jones (andere Herkunft/Geburtsdaten) gelöst." + }, + { + "date": "21.06.2014", + "matchChild": "Kai-Uwe", + "keep": [ + "Pünktchen", + "Quebec v.d. Kleinen Chaoten", + "Kai-Uwe" + ], + "note": "Ticket 8d259edf: Gales (früher Drake Jr) Wurf hatte 9 fälschlich zugeordnete unbenannte Jungtiere. Wurfchronik: 3 überlebt = Pünktchen, Quebec, Kai-Uwe (aus Abgabeverträgen/Stammbaum bestätigt)." + } + ], + "_doc_litterChildren": "litterChildren: autoritative Kinder-Whitelist eines Wurfs (Match date+matchChild). Kinder außerhalb keep werden aus dem Wurf gelöst (LitterId=None); optional renameTo. Für Über-Merges von Abnehmer-Sammelseiten." } \ No newline at end of file diff --git a/tools/import/merge_and_resolve.py b/tools/import/merge_and_resolve.py index a4cb583..1d42036 100644 --- a/tools/import/merge_and_resolve.py +++ b/tools/import/merge_and_resolve.py @@ -3209,7 +3209,7 @@ def main(): _ovr_by_namedob = {} for d in _decisions: has_ovr = (("isResident" in d) or d.get("notes") or d.get("receiver") - or d.get("renameTo") or d.get("deceased") + or d.get("renameTo") or d.get("deceased") or d.get("dateOfDeath") or d.get("correctDob") or d.get("genotype") or d.get("farbschlag") or d.get("gender") or d.get("father") or d.get("mother") or d.get("originBreeder")) @@ -3274,6 +3274,10 @@ def main(): # erst HIER wird der Name überschrieben. rename_to = (d.get("renameTo") or "").strip() if rename_to and g.get("Name") != rename_to: + # Quell-Namen merken, damit spätere Eltern-Verknüpfungen, die noch den + # alten Namen referenzieren, weiter auflösen (siehe Namens-Index unten, + # Ticket e0a0c304 Kruke→Kuke: sonst verlieren Kukes Würfe die Mutter). + g["_pre_rename_name"] = g.get("Name") g["Name"] = rename_to applied = True # deceased: markiert ein bestehendes Tier als verstorben, falls noch @@ -3281,6 +3285,14 @@ def main(): if d.get("deceased") and g.get("Status") != "Deceased": g["Status"] = "Deceased" applied = True + # dateOfDeath: setzt das Todesdatum (falls noch keins) + Status Deceased. + # rpro3 hat keine Sterbespalte, daher gibt die Züchterin/das Ticket das Datum vor. + if d.get("dateOfDeath") and not g.get("DateOfDeath"): + _dod = parse_date(d["dateOfDeath"]) + if _dod: + g["DateOfDeath"] = _dod + g["Status"] = "Deceased" + applied = True if d.get("correctDob") and not g.get("DateOfBirth"): dob_iso = parse_date(d["correctDob"]) g["DateOfBirth"] = dob_iso @@ -3414,6 +3426,13 @@ def main(): # …and by the connector-folding canon key so abbreviation variants match # (e.g. „BlackFire v.d. Kleinen Chaoten“ vs „… von den …“ — ticket #30). keys.add(canon_name_key(g["Name"])) + # Umbenannte Tiere (renameTo) auch unter ihrem Quell-Namen indexieren, damit + # Eltern-Verknüpfungen, die noch den alten Namen referenzieren, weiter auflösen. + pre = g.get("_pre_rename_name") + if pre: + keys.add(normalize_name(pre)) + keys.add(normalize_name(get_call_name(pre))) + keys.add(canon_name_key(pre)) for k in keys: if k: gerbil_by_norm_name.setdefault(k, []).append(g) @@ -4140,6 +4159,42 @@ def main(): if _lo_applied: print(f"litterOrder: {_lo_applied} Jungtier-Reihenfolgen gesetzt") + # ── litterChildren: autoritative Kinder-Whitelist eines Wurfs (Mensch-Entscheidung) ── + # Für Fälle, in denen der Wurf-Merge fremde Tiere (z. B. Abnehmer-Sammelseiten der + # Wurfchronik) in einen datierten Wurf gezogen hat (Tickets 4b9f49fb M-Wurf, 8d259edf Gale). + # Match per date + `matchChild` (ein aktuell zugeordnetes Kind, disambiguiert gleich-datierte + # Würfe). Alle Kinder, deren Ruf-Name NICHT in `keep` steht, werden aus dem Wurf gelöst + # (LitterId=None) — sie verschwinden aus dem Wurf, bleiben aber als Tier erhalten. Optional + # `renameTo` setzt den kanonischen Wurf-Namen (z. B. 'Ungeklärt' -> 'M-Wurf'). + try: + with open(CONFLICT_DECISIONS_PATH, encoding="utf-8") as _lcfh: + _litter_children = (json.load(_lcfh).get("litterChildren") or []) + except Exception: + _litter_children = [] + _lc_removed = 0 + for lc in _litter_children: + lc_date = parse_date(lc["date"]) if lc.get("date") else None + match_child = normalize_name(get_call_name(lc.get("matchChild") or "")) + keep = {normalize_name(get_call_name(n)) for n in (lc.get("keep") or [])} + if not keep: + continue + for l in resolved_litters: + if lc_date and l.get("Date") != lc_date: + continue + kids = [g for g in resolved_gerbils if g.get("LitterId") == l["Id"]] + names = {normalize_name(get_call_name(g.get("Name") or "")) for g in kids} + if match_child and match_child not in names: + continue # falscher, nur zufällig gleich-datierter Wurf + for g in kids: + if normalize_name(get_call_name(g.get("Name") or "")) not in keep: + g["LitterId"] = None + _lc_removed += 1 + if lc.get("renameTo"): + l["Name"] = lc["renameTo"] + break + if _lc_removed: + print(f"litterChildren: {_lc_removed} fälschlich zugeordnete Jungtiere aus Würfen gelöst") + # ── isResident-Sweep (Ticket 381f7e51, freigegeben) ─────────────────────── # Entscheidung der Züchterin: „Nimm wirklich ALLE Tiere aus dem Bestand heraus, # MIT AUSNAHME der, die bereits ein ELTERNTIER sind." Datengetrieben + re-ingest- diff --git a/tools/import/output/review-report.md b/tools/import/output/review-report.md index 01b4818..a26a40a 100644 --- a/tools/import/output/review-report.md +++ b/tools/import/output/review-report.md @@ -6,13 +6,13 @@ _Automatisch erzeugt von `tools/import/extract.py` — **noch nichts in die Date - Rohe Tier-Einträge aus den Stammbäumen: **2548** - Nach Zusammenführung (eindeutige Tiere): **1042** - - davon mit Geburtsdatum: 683 + - davon mit Geburtsdatum: 684 - in mehreren Dateien gefunden (Dubletten zusammengeführt): 477 - Konflikte zur Klärung: **8** -- Mehrdeutige / unvollständige Einträge (ohne Name+Datum): **380** +- Mehrdeutige / unvollständige Einträge (ohne Name+Datum): **379** - Fotos zugeordnet: **435** - Würfe aus der Wurfchronik: **752** - - Tiere mit Wurf verknüpft: **270** (davon über Geburtsdatum **und** Eltern: 174, nur über Geburtsdatum: 96; mehrdeutig: 16) + - Tiere mit Wurf verknüpft: **271** (davon über Geburtsdatum **und** Eltern: 174, nur über Geburtsdatum: 97; mehrdeutig: 16) - Würfe mit Datenqualitäts-Hinweisen: 113 (+ 138 Zeilen mit abweichendem Spaltenschema) ## Zusammenführungs-Schlüssel @@ -45,7 +45,7 @@ Gleiches Tier (Name+Datum), aber widersprüchliche Angaben in verschiedenen Date ## Mehrdeutige / unvollständige Einträge -380 Einträge ohne sichere Name+Datum-Kombination (z. B. `Name1 & Name2`-Paarzellen der tiefsten Generation, oder Zellen ohne Datum). Diese werden NICHT automatisch zusammengeführt. +379 Einträge ohne sichere Name+Datum-Kombination (z. B. `Name1 & Name2`-Paarzellen der tiefsten Generation, oder Zellen ohne Datum). Diese werden NICHT automatisch zusammengeführt. - Oskar v.d. bunten Fellnase · Stammbaum von Akio Kids.xlsx - Raya v.d. bunten Fellnasen · Stammbaum von Akio Kids.xlsx diff --git a/tools/import/test_merge_resolve.py b/tools/import/test_merge_resolve.py index 18f312c..c207af7 100644 --- a/tools/import/test_merge_resolve.py +++ b/tools/import/test_merge_resolve.py @@ -596,6 +596,23 @@ if _os.path.exists(_resolved): _check_parents("#9 Beatrice", "Beatrice von den kleinen", "Dante", "Malina") _check_parents("#30 Theodore", "Theodore von den Kleinen", "BlackFire", "Katara") + # ── litterChildren: over-merged foreign animals removed from datierten Würfen ── + def _kids(lit_id): + return sorted(_G[k]["Name"] for k in _G if _G[k].get("LitterId") == lit_id) + _mw = next((l for l in _d["litters"] + if (l.get("Date") or "").startswith("2011-03-03") and "Cotton" in _kids(l["Id"])), None) + check("4b9f49fb M-Wurf: renamed from 'Ungeklärt' to 'M-Wurf'", + _mw is not None and _mw.get("Name") == "M-Wurf") + if _mw: + check("4b9f49fb M-Wurf: exactly the 5 correct kids", + _kids(_mw["Id"]) == ["Cotton", "Kiwi", "Mayu", "Mio", "Mo"]) + _gw = next((l for l in _d["litters"] + if (l.get("Date") or "").startswith("2014-06-21") and "Kai-Uwe" in _kids(l["Id"])), None) + check("8d259edf Gale-Wurf: no wrongly-attached 'Unbekannt' kids remain", + _gw is not None and "Unbekannt" not in _kids(_gw["Id"])) + if _gw: + check("8d259edf Gale-Wurf: 3 surviving named juveniles", len(_kids(_gw["Id"])) == 3) + # ── New ticket-triage fixes (non-genetics import cluster) ───────────────── # Akane: Roni is the FATHER (gender flipped male), mother = Fumi (stub). _check_parents("Akane (wrong-parents)", "Akane", "Roni", "Fumi") @@ -729,7 +746,8 @@ if _os.path.exists(_resolved): check("E-Wurf 2010: present", _el is not None) if _el: check("E-Wurf 2010: father is Blacky", _el.get("FatherId") is not None and _G[_el["FatherId"]]["Name"].startswith("Blacky")) - check("E-Wurf 2010: mother is Kruke", _el.get("MotherId") is not None and _G[_el["MotherId"]]["Name"] == "Kruke") + # Ticket e0a0c304: 'Kruke' war ein Tippfehler → korrekt 'Kuke'. + check("E-Wurf 2010: mother is Kuke", _el.get("MotherId") is not None and _G[_el["MotherId"]]["Name"] == "Kuke") check("Eliza: shares same litter with Eddy", _eliza.get("LitterId") == _eddy.get("LitterId")) # Zeus + Beatrice vs Eddy + Sheila same-date litters (2012-06-13)