feat(import): RPRO3-Dubletten-Entscheidungen der Züchterin als Override
Manuelle "gleich/verschieden?"-Entscheidungen aus den rpro3-import-Tickets fließen jetzt in den RennmausPro-III-Import ein: - Rpro3Decisions (Modell + JSON-Loader): same (Force-Merge), different (Force-Split), fields (Farbe/DOB/Herkunft/resident/Notiz). Schlüssel = rid. - Rpro3Dedup.Run(animals, decisions): honoriert die Overrides nach dem automatischen Dedup (eine rid zieht ihren Cluster mit). - Rpro3ImportService: lädt Import/Rpro3/rpro3-decisions.json, wendet Feld-Overrides im Plan-Builder an. - rpro3-decisions.json: 14 bestätigte Entscheidungen (Eiji, Momo, Samuel, Female, Kennedy, Fegur, Tuli, Bura, Mister X, Zoey, Max, Akiro, Merlin, Snickers). - tools/import/rpro3_lookup.py: Triage-Helfer (Farbe/Gencode/Eltern/ Nachzucht+Partner aus _rpro3.db) für die Rückfragen an die Züchterin. - 4 neue Tests (Force-Merge/Force-Split/Loader); Suite 268 grün. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,73 @@ namespace GerbilManager.Tests
|
||||
Assert.Equal(2, r.Placeholders.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dedup_force_merges_variants_declared_same_by_breeder()
|
||||
{
|
||||
// Zwei Varianten, die der Auto-Dedup wegen Farbkonflikt getrennt lässt …
|
||||
var animals = new List<Rpro3Animal>
|
||||
{
|
||||
Ext("u1", "Akiro", new DateOnly(2008, 2, 5), "Polarfuchs", "Privatzucht"), // Variante B
|
||||
Ext("u2", "Akiro", new DateOnly(2008, 2, 6), "Polarfuchs, hell", "unbekannt"), // Variante C
|
||||
};
|
||||
// ohne Entscheidung: getrennt (Farbe + DOB unterschiedlich → Konflikt)
|
||||
Assert.Empty(Rpro3Dedup.Run(animals).MergeClusters);
|
||||
|
||||
// … werden per „Same"-Entscheidung der Züchterin zusammengelegt.
|
||||
var decisions = new Rpro3Decisions
|
||||
{
|
||||
Decisions = { new Rpro3Decision { Name = "Akiro", Same = { new() { "u1", "u2" } } } }
|
||||
};
|
||||
var r = Rpro3Dedup.Run(animals, decisions);
|
||||
Assert.Single(r.MergeClusters);
|
||||
Assert.Equal(2, r.MergeClusters.Values.First().Count);
|
||||
Assert.Equal(r.RidToRoot["u1"], r.RidToRoot["u2"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dedup_force_splits_variants_declared_different_by_breeder()
|
||||
{
|
||||
// Zwei Datensätze, die der Auto-Dedup zusammenlegen würde (kompatibel) …
|
||||
var animals = new List<Rpro3Animal>
|
||||
{
|
||||
Ext("u1", "Max", new DateOnly(2013, 2, 1), "Marder", "Clan A"),
|
||||
Ext("u2", "Max", new DateOnly(2013, 2, 1), "Marder", "Clan A"),
|
||||
};
|
||||
Assert.Single(Rpro3Dedup.Run(animals).MergeClusters);
|
||||
|
||||
// … bleiben durch „Different" getrennt.
|
||||
var decisions = new Rpro3Decisions
|
||||
{
|
||||
Decisions = { new Rpro3Decision { Name = "Max", Different = { new() { "u1" }, new() { "u2" } } } }
|
||||
};
|
||||
var r = Rpro3Dedup.Run(animals, decisions);
|
||||
Assert.Empty(r.MergeClusters);
|
||||
Assert.NotEqual(r.RidToRoot["u1"], r.RidToRoot["u2"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rpro3Decisions_load_returns_empty_when_file_missing()
|
||||
{
|
||||
var d = Rpro3Decisions.Load(Path.Combine(Path.GetTempPath(), "does-not-exist-" + Guid.NewGuid().ToString("N") + ".json"));
|
||||
Assert.Empty(d.Decisions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rpro3Decisions_roundtrips_through_json()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), "rpro3-dec-" + Guid.NewGuid().ToString("N") + ".json");
|
||||
File.WriteAllText(path,
|
||||
"{\"decisions\":[{\"name\":\"Bura\",\"same\":[[\"228\",\"u2128\"]],\"fields\":{\"228\":{\"origin\":\"Sarah Wörz\"}}}]}");
|
||||
try
|
||||
{
|
||||
var d = Rpro3Decisions.Load(path);
|
||||
Assert.Single(d.Decisions);
|
||||
Assert.Equal("Sarah Wörz", d.BuildFieldIndex()["228"].Origin);
|
||||
Assert.Equal(new[] { "228", "u2128" }, d.SameGroups().First());
|
||||
}
|
||||
finally { File.Delete(path); }
|
||||
}
|
||||
|
||||
// ── Integration: Reader + Execute gegen eine Mini-RPRO3-DB ────
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user