"""Report model for user-generated moderation reports.""" from datetime import datetime from typing import Optional import sqlalchemy as sa from sqlalchemy import ( Column, DateTime, Enum, ForeignKey, Integer, String, Text, ) from sqlalchemy.dialects.postgresql import TIMESTAMPTZ from sqlalchemy.orm import relationship from app.database import Base class ReportStatus: """Valid statuses for a Report.""" OPEN = "open" IN_REVIEW = "in_review" RESOLVED = "resolved" DISMISSED = "dismissed" class Resolution: """Possible resolution outcomes for a Report.""" ACTION_TAKEN = "action_taken" NO_ACTION = "no_action" class TargetType: """Types of entities that can be reported.""" USER = "user" WORLD = "world" ASSET = "asset" CHAT_MESSAGE = "chat_message" class ReasonCategory: """Categories for why something was reported.""" HARASSMENT = "harassment" HATE_SPEECH = "hate_speech" SEXUAL_CONTENT = "sexual_content" VIOLENCE = "violence" SPAM = "spam" SELF_HARM = "self_harm" OTHER = "other" class Report(Base): __tablename__ = "reports" id = Column(Integer, primary_key=True, index=True) reporter_id = Column( Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, ) target_type = Column( Enum( TargetType.USER, TargetType.WORLD, TargetType.ASSET, TargetType.CHAT_MESSAGE, name="target_type", create_type=False, ), nullable=False, ) target_id = Column(String(64), nullable=False) reason_category = Column( Enum( ReasonCategory.HARASSMENT, ReasonCategory.HATE_SPEECH, ReasonCategory.SEXUAL_CONTENT, ReasonCategory.VIOLENCE, ReasonCategory.SPAM, ReasonCategory.SELF_HARM, ReasonCategory.OTHER, name="reason_category", create_type=False, ), nullable=False, ) details = Column(Text, nullable=True) status = Column( Enum( ReportStatus.OPEN, ReportStatus.IN_REVIEW, ReportStatus.RESOLVED, ReportStatus.DISMISSED, name="report_status", create_type=False, ), nullable=False, default=ReportStatus.OPEN, ) resolved_by = Column( Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, ) resolution = Column( Enum( Resolution.ACTION_TAKEN, Resolution.NO_ACTION, name="resolution", create_type=False, ), nullable=True, ) created_at = Column( TIMESTAMPTZ, nullable=False, server_default=sa.func.now(), ) updated_at = Column( TIMESTAMPTZ, nullable=False, server_default=sa.func.now(), onupdate=sa.func.now(), ) resolved_at = Column(TIMESTAMPTZ, nullable=True) # Relationships reporter = relationship("User", foreign_keys=[reporter_id]) resolver = relationship("User", foreign_keys=[resolved_by]) moderation_actions = relationship( "ModerationAction", back_populates="report", cascade="all, delete-orphan", ) appeals = relationship( "Appeal", back_populates="report", cascade="all, delete-orphan", )