""" Bug Tracker Models - ISOLATED MODULE No dependencies on main Elmeg models. Can be removed by: deleting this file + routes file + removing router import from main.py """ from datetime import datetime from typing import Optional, List from sqlmodel import SQLModel, Field, Relationship from enum import Enum class TicketType(str, Enum): BUG = "bug" FEATURE = "feature" QUESTION = "question" OTHER = "other" class TicketStatus(str, Enum): OPEN = "open" IN_PROGRESS = "in_progress" RESOLVED = "resolved" CLOSED = "closed" class TicketPriority(str, Enum): LOW = "low" MEDIUM = "medium" HIGH = "high" CRITICAL = "critical" class Ticket(SQLModel, table=True): """ Support ticket - fully decoupled from User model. Stores reporter info as strings, not FKs. """ id: Optional[int] = Field(default=None, primary_key=True) ticket_number: str = Field(unique=True, index=True) # ELM-001 type: TicketType = Field(default=TicketType.BUG) status: TicketStatus = Field(default=TicketStatus.OPEN) priority: TicketPriority = Field(default=TicketPriority.MEDIUM) title: str = Field(max_length=200) description: str = Field(default="") # Reporter info - stored as strings, not FK reporter_email: str = Field(index=True) reporter_name: Optional[str] = None reporter_user_id: Optional[int] = None # Reference only, not FK # Assignment - stored as strings assigned_to_email: Optional[str] = None assigned_to_name: Optional[str] = None is_public: bool = Field(default=False) upvotes: int = Field(default=0) # Environment info browser: Optional[str] = None os: Optional[str] = None page_url: Optional[str] = None created_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow) resolved_at: Optional[datetime] = None # Relationships (within ticket system only) comments: List["TicketComment"] = Relationship(back_populates="ticket") class TicketComment(SQLModel, table=True): """Comment on a ticket - no FK to User""" id: Optional[int] = Field(default=None, primary_key=True) ticket_id: int = Field(foreign_key="ticket.id") # Author info - stored as strings author_email: str author_name: str author_user_id: Optional[int] = None # Reference only content: str is_internal: bool = Field(default=False) # Admin-only visibility created_at: datetime = Field(default_factory=datetime.utcnow) # Relationship ticket: Optional[Ticket] = Relationship(back_populates="comments") # ============ Schemas ============ class TicketCreate(SQLModel): type: TicketType = TicketType.BUG priority: TicketPriority = TicketPriority.MEDIUM title: str description: str = "" reporter_email: Optional[str] = None reporter_name: Optional[str] = None browser: Optional[str] = None os: Optional[str] = None page_url: Optional[str] = None class TicketUpdate(SQLModel): status: Optional[TicketStatus] = None priority: Optional[TicketPriority] = None assigned_to_email: Optional[str] = None assigned_to_name: Optional[str] = None is_public: Optional[bool] = None class TicketCommentCreate(SQLModel): content: str class TicketRead(SQLModel): id: int ticket_number: str type: TicketType status: TicketStatus priority: TicketPriority title: str description: str reporter_email: str reporter_name: Optional[str] is_public: bool upvotes: int created_at: datetime updated_at: datetime resolved_at: Optional[datetime] class TicketCommentRead(SQLModel): id: int author_name: str content: str is_internal: bool created_at: datetime