""" Application tracking models - Kanban board for job applications. """ import uuid from datetime import datetime from enum import Enum from sqlalchemy import DateTime, ForeignKey, Integer, String, Text, func from sqlalchemy.dialects.postgresql import JSONB, UUID from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base class ApplicationStatus(str, Enum): WISHLIST = "wishlist" APPLYING = "applying" APPLIED = "applied" SCREENING = "screening" INTERVIEWING = "interviewing" OFFER = "offer" ACCEPTED = "accepted" REJECTED = "rejected" WITHDRAWN = "withdrawn" GHOSTED = "ghosted" class InterviewType(str, Enum): PHONE_SCREEN = "phone_screen" TECHNICAL = "technical" BEHAVIORAL = "behavioral" SYSTEM_DESIGN = "system_design" ONSITE = "onsite" HIRING_MANAGER = "hiring_manager" TAKE_HOME = "take_home" FINAL = "final" OTHER = "other" class Application(Base): __tablename__ = "applications" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) job_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="SET NULL"), nullable=True, index=True) custom_job_title: Mapped[str | None] = mapped_column(String(512), nullable=True) custom_company_name: Mapped[str | None] = mapped_column(String(256), nullable=True) custom_job_url: Mapped[str | None] = mapped_column(String(2048), nullable=True) status: Mapped[str] = mapped_column(String(30), default=ApplicationStatus.WISHLIST, nullable=False, index=True) priority: Mapped[int] = mapped_column(Integer, default=0) applied_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) response_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) resume_version_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), nullable=True) cover_letter: Mapped[str | None] = mapped_column(Text, nullable=True) referral_contact: Mapped[str | None] = mapped_column(String(256), nullable=True) referral_notes: Mapped[str | None] = mapped_column(Text, nullable=True) offered_salary: Mapped[int | None] = mapped_column(Integer, nullable=True) offered_equity: Mapped[str | None] = mapped_column(String(100), nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) tags: Mapped[list | None] = mapped_column(JSONB, nullable=True) extra_data: Mapped[dict | None] = mapped_column("metadata", JSONB, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) user: Mapped["User"] = relationship(back_populates="applications") job: Mapped["Job"] = relationship(back_populates="applications") events: Mapped[list["ApplicationEvent"]] = relationship(back_populates="application", cascade="all, delete-orphan") interviews: Mapped[list["Interview"]] = relationship(back_populates="application", cascade="all, delete-orphan") contacts: Mapped[list["Contact"]] = relationship(back_populates="application") class ApplicationEvent(Base): """Timeline events for an application.""" __tablename__ = "application_events" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) application_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("applications.id", ondelete="CASCADE"), nullable=False, index=True) event_type: Mapped[str] = mapped_column(String(50), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) old_value: Mapped[str | None] = mapped_column(String(100), nullable=True) new_value: Mapped[str | None] = mapped_column(String(100), nullable=True) extra_data: Mapped[dict | None] = mapped_column("metadata", JSONB, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) application: Mapped["Application"] = relationship(back_populates="events") class Interview(Base): __tablename__ = "interviews" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) application_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("applications.id", ondelete="CASCADE"), nullable=False, index=True) interview_type: Mapped[str] = mapped_column(String(30), nullable=False) scheduled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) duration_minutes: Mapped[int | None] = mapped_column(Integer, nullable=True) location: Mapped[str | None] = mapped_column(String(512), nullable=True) interviewer_names: Mapped[list | None] = mapped_column(JSONB, nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) prep_notes: Mapped[str | None] = mapped_column(Text, nullable=True) outcome: Mapped[str | None] = mapped_column(String(30), nullable=True) feedback: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) application: Mapped["Application"] = relationship(back_populates="interviews") class Contact(Base): """Contacts associated with applications (recruiters, hiring managers).""" __tablename__ = "contacts" id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) application_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("applications.id", ondelete="SET NULL"), nullable=True, index=True) user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) name: Mapped[str] = mapped_column(String(256), nullable=False) role: Mapped[str | None] = mapped_column(String(100), nullable=True) company: Mapped[str | None] = mapped_column(String(256), nullable=True) email: Mapped[str | None] = mapped_column(String(320), nullable=True) phone: Mapped[str | None] = mapped_column(String(30), nullable=True) linkedin_url: Mapped[str | None] = mapped_column(String(512), nullable=True) notes: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) application: Mapped["Application"] = relationship(back_populates="contacts")