"""Signature model for electronic signatures.""" import enum from datetime import datetime from typing import Optional from sqlalchemy import String, Text, DateTime, Enum as SQLEnum, Integer, ForeignKey, JSON from sqlalchemy.orm import Mapped, mapped_column from app.core.database import Base class SignatureStatus(str, enum.Enum): """Signature status enumeration.""" PENDING = "pending" SIGNED = "signed" REJECTED = "rejected" EXPIRED = "expired" class SignatureRequest(Base): """Signature request model.""" __tablename__ = "signature_requests" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) contract_id: Mapped[int] = mapped_column(Integer, ForeignKey("contracts.id"), index=True) requester_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), index=True) signer_name: Mapped[str] = mapped_column(String(100)) signer_email: Mapped[str] = mapped_column(String(100)) status: Mapped[SignatureStatus] = mapped_column( SQLEnum(SignatureStatus), default=SignatureStatus.PENDING ) token: Mapped[str] = mapped_column(String(64), unique=True, index=True) expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) def __init__( self, contract_id: int, requester_id: int, signer_name: str, signer_email: str, token: str, expires_at: datetime, status: SignatureStatus = SignatureStatus.PENDING, **kwargs ): self.contract_id = contract_id self.requester_id = requester_id self.signer_name = signer_name self.signer_email = signer_email self.token = token self.expires_at = expires_at self.status = status self.created_at = datetime.utcnow() class Signature(Base): """Signature record model.""" __tablename__ = "signatures" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) request_id: Mapped[int] = mapped_column(Integer, ForeignKey("signature_requests.id"), index=True) signer_name: Mapped[str] = mapped_column(String(100)) signature_data: Mapped[str] = mapped_column(Text) # Base64 image or coordinates ip_address: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) user_agent: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) verification_hash: Mapped[str] = mapped_column(String(64)) # Document fingerprint signed_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) def __init__( self, request_id: int, signer_name: str, signature_data: str, verification_hash: str, ip_address: Optional[str] = None, user_agent: Optional[str] = None, **kwargs ): self.request_id = request_id self.signer_name = signer_name self.signature_data = signature_data self.verification_hash = verification_hash self.ip_address = ip_address self.user_agent = user_agent self.signed_at = datetime.utcnow() class SignatureAudit(Base): """Signature audit log model.""" __tablename__ = "signature_audits" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) signature_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("signatures.id"), nullable=True) request_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("signature_requests.id"), nullable=True) action: Mapped[str] = mapped_column(String(50)) details: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) ip_address: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) def __init__( self, action: str, signature_id: Optional[int] = None, request_id: Optional[int] = None, details: Optional[dict] = None, ip_address: Optional[str] = None, **kwargs ): self.action = action self.signature_id = signature_id self.request_id = request_id self.details = details self.ip_address = ip_address self.created_at = datetime.utcnow()