Source code for todowrite.core.models.phase

"""
Phase model.

This module contains the Phase SQLAlchemy model.
"""

from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from todowrite.core.models.goal import Goal
    from todowrite.core.models.interface_contract import InterfaceContract
    from todowrite.core.models.label import Label
    from todowrite.core.models.step import Step

from sqlalchemy import (
    TIMESTAMP,
    Integer,
    String,
    Text,
)
from sqlalchemy.orm import (
    Mapped,
    mapped_column,
    relationship,
)

from todowrite.core.associations import (
    goals_phases,
    interface_contracts_phases,
    phases_labels,
    phases_steps,
)
from todowrite.core.models.base import Base
from todowrite.core.timestamp_mixins import (
    TimestampMixin,
)


[docs] class Phase(Base, TimestampMixin): """ToDoWrite Phase model for hierarchical task management.""" __tablename__ = "phases" # Primary key convention id: Mapped[int] = mapped_column( Integer, primary_key=True, autoincrement=True, nullable=False ) # Model fields title: Mapped[str] = mapped_column(String, nullable=False) description: Mapped[str | None] = mapped_column(Text) status: Mapped[str] = mapped_column(String, default="planned") progress: Mapped[int | None] = mapped_column(Integer) started_on: Mapped[datetime | None] = mapped_column( TIMESTAMP, nullable=True ) ended_on: Mapped[datetime | None] = mapped_column(TIMESTAMP, nullable=True) # Metadata fields owner: Mapped[str | None] = mapped_column(String) severity: Mapped[str | None] = mapped_column(String) work_type: Mapped[str | None] = mapped_column(String) assignee: Mapped[str | None] = mapped_column(String) # JSON fields for complex data extra_data: Mapped[str | None] = mapped_column(Text) # Relationships labels: Mapped[list[Label]] = relationship( "Label", secondary=phases_labels, back_populates="phases" ) # belongs_to :goals (through goals_phases) goals: Mapped[list[Goal]] = relationship( "Goal", secondary=goals_phases, back_populates="phases" ) # has_many :steps (through phases_steps) steps: Mapped[list[Step]] = relationship( "Step", secondary=phases_steps, back_populates="phases" ) # belongs_to :interface_contracts (through interface_contracts_phases) interface_contracts: Mapped[list[InterfaceContract]] = relationship( "InterfaceContract", secondary=interface_contracts_phases, back_populates="phases", )
# Note: self.steps relationship is already provided by the SQLAlchemy relationship # TODO: Phase-level aggregation properties can be added later # Phase.tasks, Phase.commands - maintaining clean architecture