""" Pydantic schemas for Compliance Template Rules — profilbasierte Empfehlungs-Regeln mit Versionierung, Approval-Workflow und Tenant-Overrides. """ from datetime import datetime from typing import Any, Optional from pydantic import BaseModel, Field # ----- Bedingungs-Strukturen ----- class RuleClause(BaseModel): """Eine einzelne Klausel innerhalb einer Bedingung.""" field: str op: str # "eq" | "neq" | "gte" | "lte" | "gt" | "lt" | "in" | "not_in" | "exists" value: Any = None class RuleCondition(BaseModel): """Strukturierte Bedingung — kombiniert mehrere Klauseln per AND ('all') oder OR ('any').""" kind: str = "all" # "all" oder "any" clauses: list[RuleClause] = Field(default_factory=list) # ----- Rule (Hülle) ----- class RuleCreate(BaseModel): rule_key: str document_type: str title: str class RuleResponse(BaseModel): id: str rule_key: str document_type: str title: str current_version_id: Optional[str] created_at: datetime updated_at: Optional[datetime] # ----- Version ----- class RuleVersionCreate(BaseModel): """Neuer Draft (entweder ganz neu oder fork einer Live-Version).""" rule_id: str classification: str # required | recommended | optional conditions: RuleCondition source_citation: str rationale: Optional[str] = None created_by: Optional[str] = None class RuleVersionUpdate(BaseModel): """Edit eines Drafts — nur erlaubt während status='draft'.""" classification: Optional[str] = None conditions: Optional[RuleCondition] = None source_citation: Optional[str] = None rationale: Optional[str] = None change_summary: Optional[str] = None class RuleVersionResponse(BaseModel): id: str rule_id: str version_number: int status: str is_live: bool classification: str conditions: dict[str, Any] source_citation: str rationale: Optional[str] change_summary: Optional[str] created_by: Optional[str] submitted_by: Optional[str] submitted_at: Optional[datetime] approved_by: Optional[str] approved_at: Optional[datetime] published_by: Optional[str] published_at: Optional[datetime] rejected_by: Optional[str] rejected_at: Optional[datetime] rejection_reason: Optional[str] created_at: datetime updated_at: Optional[datetime] # ----- Lifecycle-Actions ----- class SubmitForReviewRequest(BaseModel): change_summary: str submitter: Optional[str] = None comment: Optional[str] = None class ApprovalActionRequest(BaseModel): approver: Optional[str] = None comment: Optional[str] = None class RejectActionRequest(BaseModel): rejector: Optional[str] = None rejection_reason: str comment: Optional[str] = None class ApprovalHistoryEntry(BaseModel): id: str version_id: str action: str approver: Optional[str] comment: Optional[str] created_at: datetime # ----- Tenant-Overrides ----- class OverrideCreate(BaseModel): rule_id: str override_classification: Optional[str] = None # null = deaktiviert reason: str created_by: Optional[str] = None class OverrideResponse(BaseModel): id: str tenant_id: str rule_id: str override_classification: Optional[str] reason: str created_by: Optional[str] created_at: datetime updated_at: Optional[datetime] # ----- Recommendation ----- class RecommendationRequest(BaseModel): """Input fürs Recommend: Profil-Antworten + optional Compliance-Tiefe.""" profile: dict[str, Any] compliance_depth_level: Optional[str] = None # "L1"|"L2"|"L3"|"L4" class RecommendedItem(BaseModel): document_type: str title: str rule_id: str rule_key: str classification: str # required | recommended | optional (nach Override-Anwendung) base_classification: str # ohne Override source_citation: str reason: str # menschenlesbare Begründung override_applied: bool class RecommendationResult(BaseModel): required: list[RecommendedItem] recommended: list[RecommendedItem] optional: list[RecommendedItem] profile_used: dict[str, Any] __all__ = [ "RuleClause", "RuleCondition", "RuleCreate", "RuleResponse", "RuleVersionCreate", "RuleVersionUpdate", "RuleVersionResponse", "SubmitForReviewRequest", "ApprovalActionRequest", "RejectActionRequest", "ApprovalHistoryEntry", "OverrideCreate", "OverrideResponse", "RecommendationRequest", "RecommendedItem", "RecommendationResult", ]