feat(deploy): TrueNAS Custom-App + Auto-Deploy, plus aufgelaufene Arbeit
Deployment: - custom-app.compose.yaml: self-contained Compose fuer TrueNAS "Custom App" (absolute Host-Bind-Pfade, postgres:18, pull_policy always, Port 8090) - scripts/truenas-deploy.sh: Host-Skript create/redeploy via midclt (App bleibt unter Apps sichtbar) inkl. Image-Pull + Health-Check - ci.yml Deploy-Job: laeuft auf ubuntu-latest-Runner, kopiert Deploy-Dateien per SSH auf den NAS-Host und triggert truenas-deploy.sh (statt runs-on goldeye) - compose.yaml/.env.example: postgres:18 (Locale-Match zur Quell-DB), Port 8090 - .gitignore: .agents/, tools/rag/, deploy/truenas/.env (Secrets/Scratch) Aufgelaufene Feature-Arbeit (verified/Freeze, Migrationen, Import-Triage): - GerbilOverride/VerifiedGerbil-Endpoints + GerbilSnapshotService + Tests - EF-Migrationen (ShowInChronicle, Stillborn, BirthOrder, ManualFlag, DSGVO) - Frontend VerifizierteTierePage + verified-API + e2e-Spec - diverse Import-/Triage-Skripte und -Tests Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -30,5 +30,10 @@ namespace GerbilManagerWebAPI.Models
|
||||
/// notes). Written by the Python merge_and_resolve step and surfaced read-only
|
||||
/// ("Datenherkunft"). Null = manually-added contact / no import data.</summary>
|
||||
public string? Provenance { get; set; }
|
||||
|
||||
/// <summary>true = manually created in the UI; false = produced by an import path. The ingest
|
||||
/// re-import wipe deletes ONLY IsManual=false contacts, so a manually-added contact (e.g. the
|
||||
/// Herkunft/Abnehmer of a manual animal) is never wiped. All import paths leave this false.</summary>
|
||||
public bool IsManual { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,16 @@ namespace GerbilManagerWebAPI.Models
|
||||
/// imported animals. See hive/agents/god/OWNERSHIP-residency.md. Gridify-filterable.</summary>
|
||||
public bool IsResident { get; set; } = true;
|
||||
public bool IsCastrated { get; set; }
|
||||
|
||||
/// <summary>true = manually created in the UI (POST /gerbils); false = produced by an import
|
||||
/// path. The ingest re-import wipe deletes ONLY IsManual=false rows, so manually-added animals
|
||||
/// (and their sub-records) are never wiped or overwritten. All import paths leave this false.</summary>
|
||||
public bool IsManual { get; set; }
|
||||
|
||||
/// <summary>Reihenfolge der Jungtiere innerhalb ihres Wurfs (aus der Wurfchronik, sortiert nach
|
||||
/// Geburtsgewicht). null = unbekannt → alphabetische Sortierung als Fallback. Wird im Import aus
|
||||
/// conflict-decisions.json (`litterOrder`) gesetzt; von der Wurf- und Akten-Anzeige genutzt.</summary>
|
||||
public int? BirthOrder { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Shared normalisation for the separator-insensitive name search.</summary>
|
||||
|
||||
62
GerbilManagerWebAPI/Models/GerbilOverride.cs
Normal file
62
GerbilManagerWebAPI/Models/GerbilOverride.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace GerbilManagerWebAPI.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// VERIFIED/PROTECTED OVERRIDE: authoritative, hand-curated per-gerbil field values. Decoupled
|
||||
/// from the rest of the model on purpose — <see cref="GerbilId"/> is a plain Guid column (NOT an
|
||||
/// enforced foreign key, no navigation property), so the import re-ingest wipe
|
||||
/// (<c>IngestResolvedService</c>) can delete and reload gerbils without deleting or breaking
|
||||
/// override rows. After every ingest the override is re-applied on top of the fresh import (the
|
||||
/// "freeze"), so the breeder's curated values are never lost. Matched to the (deterministic)
|
||||
/// gerbil id. Same ingest-surviving pattern as <see cref="Feedback"/>.
|
||||
///
|
||||
/// Two flavours, both stored here:
|
||||
/// - <see cref="IsVerified"/> = true → "vollständig korrekt": the whole own-field freeze set is
|
||||
/// pinned, and a full Akte snapshot (<see cref="SnapshotJson"/>) is kept for the drift report,
|
||||
/// export and regression test.
|
||||
/// - <see cref="IsVerified"/> = false → "geschützt": created by a manual edit; only the changed
|
||||
/// fields are pinned (per-field) so untouched fields keep receiving import improvements.
|
||||
/// </summary>
|
||||
public class GerbilOverride
|
||||
{
|
||||
[Key]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>Loose reference (no FK) to the gerbil this override curates.</summary>
|
||||
public Guid GerbilId { get; set; }
|
||||
|
||||
/// <summary>Captured gerbil name — keeps the row human-readable and survives an ingest wipe.</summary>
|
||||
public string? EntityName { get; set; }
|
||||
|
||||
/// <summary>true = "vollständig korrekt" (full freeze + certified); false = "geschützt" (per-field edit).</summary>
|
||||
public bool IsVerified { get; set; }
|
||||
|
||||
/// <summary>Frozen own-field values as a JSON object (field name → value). Verified rows carry
|
||||
/// the full freeze set; protected rows carry only the manually-changed fields. Re-applied on
|
||||
/// top of the fresh import after every ingest.</summary>
|
||||
public required string OverrideJson { get; set; }
|
||||
|
||||
/// <summary>Full human-readable Akte snapshot (self + parents + own litters/children) captured
|
||||
/// at verify time — feeds the drift report, export and regression test. null for protected-only rows.</summary>
|
||||
public string? SnapshotJson { get; set; }
|
||||
|
||||
/// <summary>Raw-import Akte snapshot captured during the LAST ingest, BEFORE the freeze was
|
||||
/// applied — lets the UI show where the raw import disagrees with the golden state. null until
|
||||
/// the first ingest after verifying.</summary>
|
||||
public string? LastImportSnapshotJson { get; set; }
|
||||
|
||||
/// <summary>Cached diff (golden ↔ last raw import) as a JSON array, computed at ingest time.
|
||||
/// null/empty = no drift / not yet computed.</summary>
|
||||
public string? LastImportDiffJson { get; set; }
|
||||
|
||||
/// <summary>Optional free-text note the breeder attached when verifying.</summary>
|
||||
public string? Note { get; set; }
|
||||
|
||||
/// <summary>When the row was marked "vollständig korrekt"; null for protected-only rows.</summary>
|
||||
public DateTimeOffset? VerifiedAt { get; set; }
|
||||
|
||||
/// <summary>When the override was last created/updated.</summary>
|
||||
public DateTimeOffset UpdatedAt { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,11 @@ namespace GerbilManagerWebAPI.Models
|
||||
/// Null = unbekannt (Altdaten). Must be ≤ TotalBorn if both are set.</summary>
|
||||
public int? DeathsWithin8Weeks { get; set; }
|
||||
|
||||
/// <summary>Totgeburten (TG): tot geborene Jungtiere dieses Wurfs. Getrennt von
|
||||
/// DeathsWithin8Weeks (in den ersten 8 Wochen verstorben). Aus der xlsx-Wurfchronik
|
||||
/// (litters.json breakdown) importiert. Null = unbekannt/nicht erfasst.</summary>
|
||||
public int? Stillborn { get; set; }
|
||||
|
||||
public Guid? FatherId { get; set; }
|
||||
public Gerbil? Father { get; set; }
|
||||
public Guid? MotherId { get; set; }
|
||||
@@ -45,5 +50,17 @@ namespace GerbilManagerWebAPI.Models
|
||||
/// Written by the Python merge_and_resolve step and surfaced read-only ("Datenherkunft").
|
||||
/// Null = manually-added litter / no import data.</summary>
|
||||
public string? Provenance { get; set; }
|
||||
|
||||
/// <summary>Steuert, ob dieser Wurf in der Wurfchronik (Würfe-Liste) erscheint.
|
||||
/// true = normal sichtbar. false = aus der Wurfchronik ausgeblendet, aber weiterhin
|
||||
/// auf der Tier-Detailseite des Elterntiers sichtbar — z. B. ein extern (bei einer
|
||||
/// anderen Zucht) entstandener Wurf eines nicht-residenten Tieres, der nur zur
|
||||
/// Dokumentation der Abstammung gehört. Default true (alle bestehenden Würfe).</summary>
|
||||
public bool ShowInChronicle { get; set; } = true;
|
||||
|
||||
/// <summary>true = manually created in the UI; false = produced by an import path. The ingest
|
||||
/// re-import wipe deletes ONLY IsManual=false litters, so a manually-added litter (e.g. the
|
||||
/// birth litter of a manual animal) is never wiped. All import paths leave this false.</summary>
|
||||
public bool IsManual { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user