""" Project model for individual projects and engagements. Tracks client projects, internal products, infrastructure work, and development tools. """ from datetime import date, datetime from typing import TYPE_CHECKING, Optional from sqlalchemy import DATE, ForeignKey, Index, Numeric, String, Text from sqlalchemy.orm import Mapped, mapped_column, relationship from .base import Base, TimestampMixin, UUIDMixin if TYPE_CHECKING: from .client import Client from .pending_task import PendingTask from .session import Session class Project(Base, UUIDMixin, TimestampMixin): """ Project model representing individual projects and engagements. Tracks client projects, internal products, infrastructure work, websites, development tools, and documentation projects. Each project belongs to a client and has status, priority, and time tracking. Attributes: client_id: Foreign key to clients table name: Project name slug: URL-safe slug (directory name) category: Project category status: Current status (complete, working, blocked, pending, critical, deferred) priority: Priority level (critical, high, medium, low) description: Project description started_date: Date project started target_completion_date: Target completion date completed_date: Actual completion date estimated_hours: Estimated hours for completion actual_hours: Actual hours spent gitea_repo_url: Gitea repository URL if applicable notes: Additional notes about the project client: Relationship to Client model """ __tablename__ = "projects" # Foreign keys client_id: Mapped[str] = mapped_column( String(36), ForeignKey("clients.id", ondelete="CASCADE"), nullable=False, doc="Foreign key to clients table" ) # Project identification name: Mapped[str] = mapped_column( String(255), nullable=False, doc="Project name" ) slug: Mapped[Optional[str]] = mapped_column( String(255), unique=True, doc="URL-safe slug (directory name like 'dataforth-dos')" ) # Categorization category: Mapped[Optional[str]] = mapped_column( String(50), doc="Project category: client_project, internal_product, infrastructure, website, development_tool, documentation" ) status: Mapped[str] = mapped_column( String(50), default="working", server_default="working", doc="Status: complete, working, blocked, pending, critical, deferred" ) priority: Mapped[Optional[str]] = mapped_column( String(20), doc="Priority level: critical, high, medium, low" ) # Description description: Mapped[Optional[str]] = mapped_column( Text, doc="Project description" ) # Timeline started_date: Mapped[Optional[date]] = mapped_column( DATE, doc="Date project started" ) target_completion_date: Mapped[Optional[date]] = mapped_column( DATE, doc="Target completion date" ) completed_date: Mapped[Optional[date]] = mapped_column( DATE, doc="Actual completion date" ) # Time tracking estimated_hours: Mapped[Optional[float]] = mapped_column( Numeric(10, 2), doc="Estimated hours for completion" ) actual_hours: Mapped[Optional[float]] = mapped_column( Numeric(10, 2), doc="Actual hours spent" ) # Repository gitea_repo_url: Mapped[Optional[str]] = mapped_column( String(500), doc="Gitea repository URL if applicable" ) # Notes notes: Mapped[Optional[str]] = mapped_column( Text, doc="Additional notes about the project" ) # Relationships client: Mapped["Client"] = relationship( "Client", back_populates="projects", doc="Relationship to Client model" ) sessions: Mapped[list["Session"]] = relationship( "Session", back_populates="project", doc="Sessions associated with this project" ) pending_tasks: Mapped[list["PendingTask"]] = relationship( "PendingTask", back_populates="project", doc="Pending tasks associated with this project" ) # Indexes __table_args__ = ( Index("idx_projects_client", "client_id"), Index("idx_projects_status", "status"), Index("idx_projects_slug", "slug"), ) def __repr__(self) -> str: """String representation of the project.""" return f""