feat: sort tasks by due time
ci / gitleaks (push) Successful in 8s
ci / docker (push) Successful in 3m35s

This commit is contained in:
2026-09-10 18:16:13 +08:00
parent ab8c0a5138
commit addd49363d
8 changed files with 576 additions and 58 deletions
+38 -9
View File
@@ -15,11 +15,40 @@ from sqlalchemy import (
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import TypeDecorator
from uuid_utils import uuid7
from .db import Base
class UTCDateTime(TypeDecorator):
"""Store absolute instants as UTC while preserving the existing SQL column types.
SQLite's native datetime processor preserves offsets present in historical text;
those aware values are converted by instant. Legacy naive values are necessarily
interpreted as the UTC convention previously used by this application.
"""
impl = DateTime(timezone=True)
cache_ok = True
def process_bind_param(self, value: datetime | None, dialect):
if value is None:
return None
if value.tzinfo is None:
normalized = value.replace(tzinfo=UTC)
else:
normalized = value.astimezone(UTC)
return normalized.replace(tzinfo=None) if dialect.name == "sqlite" else normalized
def process_result_value(self, value: datetime | None, dialect):
if value is None:
return None
if value.tzinfo is None:
return value.replace(tzinfo=UTC)
return value.astimezone(UTC)
def new_id() -> UUID:
return UUID(str(uuid7()))
@@ -104,13 +133,13 @@ class Task(Base):
description: Mapped[str] = mapped_column(Text, default="")
priority: Mapped[int] = mapped_column(Integer, default=0)
completed: Mapped[bool] = mapped_column(Boolean, default=False, index=True)
due_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
due_at: Mapped[datetime | None] = mapped_column(UTCDateTime(), nullable=True)
due_has_time: Mapped[bool] = mapped_column(Boolean, default=False)
version: Mapped[int] = mapped_column(Integer, default=1)
position: Mapped[int] = mapped_column(Integer, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)
deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
created_at: Mapped[datetime] = mapped_column(UTCDateTime(), default=utcnow)
updated_at: Mapped[datetime] = mapped_column(UTCDateTime(), default=utcnow, onupdate=utcnow)
deleted_at: Mapped[datetime | None] = mapped_column(UTCDateTime(), nullable=True)
external_id: Mapped[str | None] = mapped_column(String(255), nullable=True)
@@ -120,9 +149,9 @@ class RecurrenceTemplate(Base):
user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
task_id: Mapped[UUID] = mapped_column(ForeignKey("tasks.id", ondelete="CASCADE"), unique=True)
rrule: Mapped[str] = mapped_column(Text)
starts_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
ends_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
starts_at: Mapped[datetime] = mapped_column(UTCDateTime())
ends_at: Mapped[datetime | None] = mapped_column(UTCDateTime(), nullable=True)
created_at: Mapped[datetime] = mapped_column(UTCDateTime(), default=utcnow)
class RecurrenceException(Base):
@@ -130,9 +159,9 @@ class RecurrenceException(Base):
__table_args__ = (UniqueConstraint("template_id", "occurrence_at", name="uq_recurrence_exception"),)
id: Mapped[UUID] = mapped_column(primary_key=True, default=new_id)
template_id: Mapped[UUID] = mapped_column(ForeignKey("recurrence_templates.id", ondelete="CASCADE"), index=True)
occurrence_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
occurrence_at: Mapped[datetime] = mapped_column(UTCDateTime())
title: Mapped[str | None] = mapped_column(String(500), nullable=True)
due_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
due_at: Mapped[datetime | None] = mapped_column(UTCDateTime(), nullable=True)
completed: Mapped[bool] = mapped_column(Boolean, default=False)
deleted: Mapped[bool] = mapped_column(Boolean, default=False)