from __future__ import annotations from datetime import datetime from typing import Any import uuid from sqlalchemy import ( DateTime, ForeignKey, Index, Integer, JSON, String, UniqueConstraint, ) from sqlalchemy.orm import Mapped, mapped_column from govoplan_core.db.base import Base, TimestampMixin def new_uuid() -> str: return str(uuid.uuid4()) class VotingBallotRevision(Base, TimestampMixin): __tablename__ = "voting_ballot_revisions" __table_args__ = ( UniqueConstraint( "tenant_id", "ballot_id", "revision", name="uq_voting_ballot_revision" ), Index("ix_voting_ballot_current", "tenant_id", "ballot_id", "superseded_at"), Index("ix_voting_ballot_catalogue", "tenant_id", "state", "recorded_at"), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) ballot_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) revision: Mapped[int] = mapped_column(Integer, nullable=False) previous_revision_id: Mapped[str | None] = mapped_column( ForeignKey("voting_ballot_revisions.id", ondelete="RESTRICT"), nullable=True, index=True, ) state: Mapped[str] = mapped_column(String(30), nullable=False, index=True) assurance_profile: Mapped[str] = mapped_column( String(40), nullable=False, index=True ) method: Mapped[str] = mapped_column(String(40), nullable=False, index=True) definition_sha256: Mapped[str | None] = mapped_column( String(64), nullable=True, index=True ) electorate_sha256: Mapped[str | None] = mapped_column( String(64), nullable=True, index=True ) recorded_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, index=True ) superseded_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, index=True ) payload: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False) created_by: Mapped[str | None] = mapped_column( String(255), nullable=True, index=True ) class VotingCastRecord(Base, TimestampMixin): __tablename__ = "voting_cast_records" __table_args__ = ( UniqueConstraint( "tenant_id", "ballot_id", "elector_id", "generation", name="uq_voting_cast_generation", ), UniqueConstraint( "tenant_id", "ballot_id", "idempotency_key", name="uq_voting_cast_idempotency", ), Index( "ix_voting_cast_active", "tenant_id", "ballot_id", "elector_id", "superseded_at", ), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) ballot_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) definition_sha256: Mapped[str] = mapped_column(String(64), nullable=False) elector_id: Mapped[str] = mapped_column(String(255), nullable=False, index=True) generation: Mapped[int] = mapped_column(Integer, nullable=False) selections: Mapped[list[str]] = mapped_column(JSON, nullable=False) weight: Mapped[int] = mapped_column(Integer, nullable=False, default=1) cast_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, index=True ) superseded_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True, index=True ) idempotency_key: Mapped[str] = mapped_column(String(160), nullable=False) receipt_sha256: Mapped[str] = mapped_column(String(64), nullable=False, index=True) actor_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True) class VotingLifecycleEvent(Base, TimestampMixin): __tablename__ = "voting_lifecycle_events" __table_args__ = ( UniqueConstraint( "tenant_id", "ballot_id", "sequence", name="uq_voting_event_sequence" ), Index("ix_voting_event_history", "tenant_id", "ballot_id", "sequence"), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) ballot_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) sequence: Mapped[int] = mapped_column(Integer, nullable=False) event_type: Mapped[str] = mapped_column(String(60), nullable=False, index=True) recorded_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, index=True ) actor_id: Mapped[str | None] = mapped_column(String(255), nullable=True, index=True) payload: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False, default=dict) class VotingCommandReplay(Base, TimestampMixin): __tablename__ = "voting_command_replays" __table_args__ = ( UniqueConstraint( "tenant_id", "operation", "idempotency_key", name="uq_voting_command_replay" ), ) id: Mapped[str] = mapped_column(String(36), primary_key=True, default=new_uuid) tenant_id: Mapped[str] = mapped_column(String(36), nullable=False, index=True) operation: Mapped[str] = mapped_column(String(60), nullable=False, index=True) idempotency_key: Mapped[str] = mapped_column(String(160), nullable=False) request_sha256: Mapped[str] = mapped_column(String(64), nullable=False) response: Mapped[dict[str, Any]] = mapped_column(JSON, nullable=False) __all__ = [ "VotingBallotRevision", "VotingCastRecord", "VotingCommandReplay", "VotingLifecycleEvent", ]