reminder-bot/bot/db/models.py

69 lines
3.0 KiB
Python
Raw Permalink Normal View History

2025-12-19 11:19:54 +01:00
"""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})>"
)