reminder-bot/bot/db/models.py

69 lines
3.0 KiB
Python

"""ORM models for database tables."""
from datetime import datetime, time
from typing import Optional
from sqlalchemy import BigInteger, String, Boolean, Integer, DateTime, Time, Index, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from bot.db.base import Base
class User(Base):
"""User model - represents Telegram users."""
__tablename__ = "users"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
tg_user_id: Mapped[int] = mapped_column(BigInteger, unique=True, nullable=False, index=True)
username: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
first_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
last_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, nullable=False)
updated_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False
)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
# Relationship
reminders: Mapped[list["Reminder"]] = relationship("Reminder", back_populates="user", cascade="all, delete-orphan")
def __repr__(self) -> str:
return f"<User(id={self.id}, tg_user_id={self.tg_user_id}, username={self.username})>"
class Reminder(Base):
"""Reminder model - represents recurring reminders."""
__tablename__ = "reminders"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
text: Mapped[str] = mapped_column(String(1000), nullable=False)
days_interval: Mapped[int] = mapped_column(Integer, nullable=False)
time_of_day: Mapped[time] = mapped_column(Time, nullable=False)
next_run_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, index=True)
last_done_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, nullable=False)
updated_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False
)
# Optional fields for statistics
snooze_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
total_done_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
# Relationship
user: Mapped["User"] = relationship("User", back_populates="reminders")
# Composite index for scheduler queries
__table_args__ = (
Index("ix_reminders_active_next_run", "is_active", "next_run_at"),
)
def __repr__(self) -> str:
return (
f"<Reminder(id={self.id}, user_id={self.user_id}, text='{self.text[:30]}...', "
f"interval={self.days_interval}, active={self.is_active})>"
)