Files
GerbilManager/GerbilManagerWebAPI/Models/Gerbil.cs
Gulum 45b8533f18
Some checks failed
CI / Backend Tests (.NET) (push) Successful in 1m11s
CI / Frontend Tests (Node/Vite) (push) Failing after 4m59s
CI / Docker Build & Push (push) Has been skipped
CI / Deploy auf TrueNAS (Custom App) (push) Has been skipped
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>
2026-07-19 09:19:11 +02:00

117 lines
5.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.ComponentModel.DataAnnotations;
namespace GerbilManagerWebAPI.Models
{
/// <summary>
/// A gerbil (Rennmaus). Pedigree is traversed via Litter → Father/Mother (no redundant
/// parent FKs here). Genotype is one compact frozen-contract string (e.g.
/// "Aa CC Dd EE GG Pp Spsp rere", "?" wildcards allowed); never SQL-filtered.
/// </summary>
public class Gerbil
{
[Key]
public Guid Id { get; set; }
public required string Name { get; set; }
public Gender Gender { get; set; }
public GerbilStatus Status { get; set; } = GerbilStatus.Breeding;
// Birth litter (the litter this gerbil was born in).
public Guid? LitterId { get; set; }
public Litter? Litter { get; set; }
// Contacts: Herkunft (origin) and Abnehmer (receiver, when given away).
public Guid? OriginContactId { get; set; }
public Contact? OriginContact { get; set; }
public Guid? ReceiverContactId { get; set; }
public Contact? ReceiverContact { get; set; }
// Current home.
public Guid? EnclosureId { get; set; }
public Enclosure? Enclosure { get; set; }
// Farbschlag.
public Guid? ColorVarietyId { get; set; }
public ColorVariety? ColorVariety { get; set; }
public DateOnly? DateOfBirth { get; set; }
public DateOnly? DateOfDeath { get; set; }
public string? CauseOfDeath { get; set; }
public DateOnly? GoHomeDate { get; set; }
/// <summary>Compact genotype string (frozen GEN-1 contract). Null = not genotyped.</summary>
public string? Genotype { get; set; }
public string? SpottingType { get; set; }
public string? Notes { get; set; }
// Provenance (for the FEAT-8 spreadsheet import).
public string? ImportSource { get; set; }
public string? ExternalRef { get; set; }
/// <summary>Data-provenance / traceability for the import: a JSON object describing
/// WHICH source information produced this entry (sourceFiles, mergedRecordCount,
/// fromWurfchronik, parentMethod/parentConfidence, notes). Written by the Python
/// merge_and_resolve step and surfaced read-only in the Rennmausakte
/// ("Nachverfolgungsinformationen"). Null = manually-added animal / no import data.</summary>
public string? Provenance { get; set; }
/// <summary>Raw import payload preserved verbatim (rawGenotype + unmappedTokens like
/// the Uw locus / WFNZ markers) so nothing from the spreadsheets is lost. JSON text.</summary>
public string? RawImportData { get; set; }
/// <summary>Breeder/Herkunft (cattery/Zucht) as free text — set by the FEAT-8 import from
/// the source Zucht (imported animals have no OriginContact). Gridify-filterable; the
/// distinct values back the Tiere "Herkunft" dropdown (GET /gerbils/breeders).</summary>
public string? OriginBreeder { get; set; }
/// <summary>Separator-insensitive search key: Name lowercased with whitespace/.-_ stripped.
/// Kept in sync automatically on save (see ApplicationContext.SaveChanges). Gridify-filterable
/// so "clan kleine chaoten" matches "Clan-Kleine-Chaoten" (client strips separators too).</summary>
public string? NameSearch { get; set; }
/// <summary>FEAT-14: Charakterbogen trait labels (opaque to the backend — the
/// {key,label} vocabulary lives frontend-side). Stored as a JSON text column.</summary>
public List<string> CharacterTraits { get; set; } = new();
/// <summary>FEAT-14: free-text character note; feeds the AI Verkaufstext.</summary>
public string? CharacterNote { get; set; }
/// <summary>GEN-3b: hearing/deaf phenotype flag (NOT a genotype locus — it's the
/// downstream effect of high white load / Sp×Sls). null = not stated, true = deaf
/// (dea/taub), false = hearing (Dea/hörend). Set by the FEAT-8 import from the
/// after-spsp deafness annotation; see hive/agents/god/GENETIK-notation.md.</summary>
public bool? IsDeaf { get; set; }
/// <summary>Residency/ownership (ORIGIN, distinct from Abgabe location): true = part of
/// the Clan-kleine-Chaoten Bestand, false = external pedigree ancestor (bred elsewhere).
/// Rule (a) Zuchtname matches the Clan kennel, OR (b) it's a parent of a Clan offspring.
/// Defaults true (manually-added animals are own stock); the FEAT-8 import classifies
/// 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>
public static class GerbilSearch
{
public static string Normalize(string? name)
{
if (string.IsNullOrEmpty(name)) return "";
var chars = name.ToLowerInvariant()
.Where(c => !char.IsWhiteSpace(c) && c != '-' && c != '.' && c != '_');
return new string(chars.ToArray());
}
}
}