flask_template_codex/app/tasks/models.py
2026-08-08 02:53:18 +02:00

70 lines
2.4 KiB
Python

from datetime import datetime, timezone
from typing import Any
from sqlalchemy import CheckConstraint, ForeignKey, JSON, String, Text
from sqlalchemy.ext.mutable import MutableList
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.extensions import db
TASK_STATUSES = ("not_started", "in_progress", "done")
TASK_PRIORITIES = ("urgent", "important", "normal")
class Task(db.Model):
__table_args__ = (
CheckConstraint(
"status IN ('not_started', 'in_progress', 'done')",
name="ck_task_status",
),
CheckConstraint(
"priority IN ('urgent', 'important', 'normal')",
name="ck_task_priority",
),
)
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(200))
description: Mapped[str | None] = mapped_column(Text)
status: Mapped[str] = mapped_column(String(20), default="not_started")
priority: Mapped[str] = mapped_column(String(20), default="normal")
created_at: Mapped[datetime] = mapped_column(
default=lambda: datetime.now(timezone.utc)
)
due_date: Mapped[datetime | None]
completed_at: Mapped[datetime | None]
user_id: Mapped[int] = mapped_column(
ForeignKey("user.id", ondelete="CASCADE"), index=True
)
# Stored now so Phase 3 can add timers and chunking without reshaping CRUD.
timer_seconds: Mapped[int] = mapped_column(default=0)
context: Mapped[str | None] = mapped_column(String(100))
chunk_sessions: Mapped[list[Any]] = mapped_column(
MutableList.as_mutable(JSON), default=list
)
subtasks: Mapped[list["Subtask"]] = relationship(
back_populates="task",
cascade="all, delete-orphan",
order_by="Subtask.id",
)
@property
def progress_percentage(self) -> int:
if not self.subtasks:
return 100 if self.status == "done" else 0
completed = sum(subtask.completed for subtask in self.subtasks)
return round(completed / len(self.subtasks) * 100)
class Subtask(db.Model):
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(100))
completed: Mapped[bool] = mapped_column(default=False)
task_id: Mapped[int] = mapped_column(
ForeignKey("task.id", ondelete="CASCADE"), index=True
)
task: Mapped[Task] = relationship(back_populates="subtasks")