from __future__ import annotations import enum import uuid from datetime import datetime, time from sqlalchemy import JSON, DateTime, Enum, ForeignKey, Integer, String, Text, Time, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column, relationship from .database import Base class SourceType(str, enum.Enum): official_record = "official_record" user = "user" manual_import = "manual_import" class ModerationStatus(str, enum.Enum): pending = "pending" approved = "approved" rejected = "rejected" class BaitKind(str, enum.Enum): bait = "bait" lure = "lure" unknown = "unknown" class ImportStatus(str, enum.Enum): running = "running" success = "success" partial = "partial" failed = "failed" class Fish(Base): __tablename__ = "fish" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) slug: Mapped[str] = mapped_column(String(100), unique=True) name_ru: Mapped[str] = mapped_column(String(200), unique=True) trophy_weight_g: Mapped[int | None] class Waterbody(Base): __tablename__ = "waterbody" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) slug: Mapped[str] = mapped_column(String(100), unique=True) name_ru: Mapped[str] = mapped_column(String(200), unique=True) unlock_level: Mapped[int | None] class Bait(Base): __tablename__ = "bait" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) name: Mapped[str] = mapped_column(String(200)) normalized_name: Mapped[str] = mapped_column(String(200), unique=True) kind: Mapped[BaitKind] = mapped_column(Enum(BaitKind)) class Spot(Base): __tablename__ = "spot" __table_args__ = (UniqueConstraint("waterbody_id", "x", "y"),) id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) waterbody_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("waterbody.id")) x: Mapped[int] y: Mapped[int] description: Mapped[str | None] = mapped_column(Text) waterbody: Mapped[Waterbody] = relationship() class CatchReport(Base): __tablename__ = "catch_report" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) fish_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("fish.id")) spot_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("spot.id")) waterbody_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("waterbody.id")) bait_id: Mapped[uuid.UUID | None] = mapped_column(ForeignKey("bait.id")) weight_g: Mapped[int] fishing_method: Mapped[str | None] = mapped_column(String(50)) rig_type: Mapped[str | None] = mapped_column(String(100)) retrieve_method: Mapped[str | None] = mapped_column(String(100)) retrieve_speed: Mapped[int | None] game_time: Mapped[time | None] = mapped_column(Time) caught_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) reported_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) player_name: Mapped[str | None] = mapped_column(String(100)) source_type: Mapped[SourceType] = mapped_column(Enum(SourceType)) source_url: Mapped[str | None] = mapped_column(Text) source_external_id: Mapped[str | None] = mapped_column(String(64), unique=True) source_confidence: Mapped[int] moderation_status: Mapped[ModerationStatus] = mapped_column(Enum(ModerationStatus)) screenshot_key: Mapped[str | None] = mapped_column(Text) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) raw_payload: Mapped[dict | None] = mapped_column(JSON) fish: Mapped[Fish] = relationship() spot: Mapped[Spot | None] = relationship() waterbody: Mapped[Waterbody] = relationship() bait: Mapped[Bait | None] = relationship() class OfficialRecordImport(Base): __tablename__ = "official_record_import" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) status: Mapped[ImportStatus] = mapped_column(Enum(ImportStatus)) source_url: Mapped[str] = mapped_column(Text) rows_seen: Mapped[int] = mapped_column(default=0) rows_created: Mapped[int] = mapped_column(default=0) rows_updated: Mapped[int] = mapped_column(default=0) error_summary: Mapped[str | None] = mapped_column(Text) response_status: Mapped[int | None] response_etag: Mapped[str | None] = mapped_column(String(500)) response_last_modified: Mapped[str | None] = mapped_column(String(500)) response_content_type: Mapped[str | None] = mapped_column(String(200)) response_bytes: Mapped[int | None] not_modified: Mapped[bool] = mapped_column(default=False) class ModerationEvent(Base): __tablename__ = "moderation_event" id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4) catch_report_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("catch_report.id")) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) previous_status: Mapped[ModerationStatus] = mapped_column(Enum(ModerationStatus, name="moderationstatus", create_type=False)) new_status: Mapped[ModerationStatus] = mapped_column(Enum(ModerationStatus, name="moderationstatus", create_type=False)) moderator: Mapped[str] = mapped_column(String(100)) reason: Mapped[str | None] = mapped_column(Text) catch_report: Mapped[CatchReport] = relationship()