From c271f1b41fdb939a0a1c5890ff47a128548c2add Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 27 Mar 2026 22:49:59 -0400 Subject: [PATCH] feat: add recurrence_rule and recurrence_next_spawn_at columns to notes --- alembic/versions/0033_add_task_recurrence.py | 30 ++++++++++++++++++++ src/fabledassistant/models/note.py | 12 +++++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 alembic/versions/0033_add_task_recurrence.py diff --git a/alembic/versions/0033_add_task_recurrence.py b/alembic/versions/0033_add_task_recurrence.py new file mode 100644 index 0000000..dcf533f --- /dev/null +++ b/alembic/versions/0033_add_task_recurrence.py @@ -0,0 +1,30 @@ +"""Add recurrence_rule and recurrence_next_spawn_at to notes.""" + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects.postgresql import JSONB + +revision = "0033" +down_revision = "0032" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column("notes", sa.Column("recurrence_rule", JSONB(), nullable=True)) + op.add_column( + "notes", + sa.Column("recurrence_next_spawn_at", sa.DateTime(timezone=True), nullable=True), + ) + op.create_index( + "ix_notes_recurrence_next_spawn_at", + "notes", + ["recurrence_next_spawn_at"], + postgresql_where=sa.text("recurrence_next_spawn_at IS NOT NULL"), + ) + + +def downgrade() -> None: + op.drop_index("ix_notes_recurrence_next_spawn_at", table_name="notes") + op.drop_column("notes", "recurrence_next_spawn_at") + op.drop_column("notes", "recurrence_rule") diff --git a/src/fabledassistant/models/note.py b/src/fabledassistant/models/note.py index 4fa6843..47635b2 100644 --- a/src/fabledassistant/models/note.py +++ b/src/fabledassistant/models/note.py @@ -2,7 +2,7 @@ import enum from datetime import date, datetime from sqlalchemy import Date, DateTime, ForeignKey, Index, Integer, Text -from sqlalchemy.dialects.postgresql import ARRAY +from sqlalchemy.dialects.postgresql import ARRAY, JSONB from sqlalchemy.orm import Mapped, mapped_column from fabledassistant.models import Base @@ -47,6 +47,10 @@ class Note(Base, TimestampMixin): due_date: Mapped[date | None] = mapped_column(Date, nullable=True) started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) + recurrence_rule: Mapped[dict | None] = mapped_column(JSONB, nullable=True) + recurrence_next_spawn_at: Mapped[datetime | None] = mapped_column( + DateTime(timezone=True), nullable=True + ) __table_args__ = ( Index("ix_notes_tags", "tags", postgresql_using="gin"), @@ -75,6 +79,12 @@ class Note(Base, TimestampMixin): "due_date": self.due_date.isoformat() if self.due_date else None, "started_at": self.started_at.isoformat() if self.started_at else None, "completed_at": self.completed_at.isoformat() if self.completed_at else None, + "recurrence_rule": self.recurrence_rule, + "recurrence_next_spawn_at": ( + self.recurrence_next_spawn_at.isoformat() + if self.recurrence_next_spawn_at + else None + ), "is_task": self.is_task, "created_at": self.created_at.isoformat(), "updated_at": self.updated_at.isoformat(),