from datetime import datetime from sqlalchemy import Boolean, DateTime, Float, ForeignKey, Integer, String, Text, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.base import Base class ChatSession(Base): __tablename__ = "chat_sessions" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) title: Mapped[str] = mapped_column(String(255), default="Новый чат") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) messages: Mapped[list["Message"]] = relationship( back_populates="session", cascade="all, delete-orphan", order_by="Message.created_at" ) class Message(Base): __tablename__ = "messages" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) session_id: Mapped[int] = mapped_column(ForeignKey("chat_sessions.id", ondelete="CASCADE"), index=True) role: Mapped[str] = mapped_column(String(32)) content: Mapped[str] = mapped_column(Text, default="") tool_calls_json: Mapped[str | None] = mapped_column(Text, nullable=True) tool_call_id: Mapped[str | None] = mapped_column(String(64), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) session: Mapped["ChatSession"] = relationship(back_populates="messages") class PomodoroCycle(Base): __tablename__ = "pomodoro_cycles" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) work_duration_min: Mapped[int] = mapped_column(Integer, default=25) short_break_min: Mapped[int] = mapped_column(Integer, default=5) long_break_min: Mapped[int] = mapped_column(Integer, default=15) sessions_until_long_break: Mapped[int] = mapped_column(Integer, default=4) completed_work_sessions: Mapped[int] = mapped_column(Integer, default=0) task_note: Mapped[str] = mapped_column(Text, default="") auto_advance: Mapped[bool] = mapped_column(Boolean, default=True) chat_notify_seq: Mapped[int] = mapped_column(Integer, default=0) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class PomodoroSession(Base): __tablename__ = "pomodoro_sessions" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) status: Mapped[str] = mapped_column(String(32), default="idle") phase: Mapped[str] = mapped_column(String(32), default="work") duration_min: Mapped[int] = mapped_column(Integer, default=25) task_note: Mapped[str] = mapped_column(Text, default="") result: Mapped[str | None] = mapped_column(Text, nullable=True) completed: Mapped[bool] = mapped_column(Boolean, default=False) completion_notified: Mapped[bool] = mapped_column(Boolean, default=False) started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) paused_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) elapsed_seconds: Mapped[int] = mapped_column(Integer, default=0) finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) class TaigaProject(Base): __tablename__ = "taiga_projects" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) taiga_id: Mapped[int] = mapped_column(Integer, unique=True, index=True) name: Mapped[str] = mapped_column(String(255)) slug: Mapped[str] = mapped_column(String(255), unique=True, index=True) synced_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) class ProjectBinding(Base): __tablename__ = "project_bindings" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) taiga_slug: Mapped[str] = mapped_column(String(255), unique=True, index=True) gitea_owner: Mapped[str] = mapped_column(String(255), default="") gitea_repo: Mapped[str] = mapped_column(String(255), default="") default_branch: Mapped[str] = mapped_column(String(64), default="main") updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class UserProfile(Base): __tablename__ = "user_profile" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) data_json: Mapped[str] = mapped_column(Text, default="{}") updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class MemoryFact(Base): __tablename__ = "memory_facts" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) category: Mapped[str] = mapped_column(String(64), default="fact", index=True) content: Mapped[str] = mapped_column(Text) source: Mapped[str] = mapped_column(String(32), default="user") session_id: Mapped[int | None] = mapped_column( ForeignKey("chat_sessions.id", ondelete="SET NULL"), nullable=True, index=True ) importance: Mapped[int] = mapped_column(Integer, default=3) active: Mapped[bool] = mapped_column(Boolean, default=True, index=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class SessionSummary(Base): __tablename__ = "session_summaries" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) session_id: Mapped[int] = mapped_column( ForeignKey("chat_sessions.id", ondelete="CASCADE"), unique=True, index=True ) summary: Mapped[str] = mapped_column(Text, default="") message_count: Mapped[int] = mapped_column(Integer, default=0) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class FitnessProfile(Base): __tablename__ = "fitness_profiles" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) sex: Mapped[str] = mapped_column(String(16), default="male") age: Mapped[int] = mapped_column(Integer, default=30) height_cm: Mapped[float] = mapped_column(Float, default=170.0) weight_kg: Mapped[float] = mapped_column(Float, default=70.0) activity_level: Mapped[str] = mapped_column(String(32), default="moderate") goal: Mapped[str] = mapped_column(String(32), default="maintain") target_weight_kg: Mapped[float | None] = mapped_column(Float, nullable=True) weekly_workouts: Mapped[int] = mapped_column(Integer, default=3) calorie_target: Mapped[float] = mapped_column(Float, default=2000.0) protein_g: Mapped[float] = mapped_column(Float, default=140.0) fat_g: Mapped[float] = mapped_column(Float, default=65.0) carbs_g: Mapped[float] = mapped_column(Float, default=200.0) water_l: Mapped[float] = mapped_column(Float, default=2.5) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class BodyMetric(Base): __tablename__ = "body_metrics" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) recorded_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) weight_kg: Mapped[float] = mapped_column(Float) body_fat_pct: Mapped[float | None] = mapped_column(Float, nullable=True) chest_cm: Mapped[float | None] = mapped_column(Float, nullable=True) waist_cm: Mapped[float | None] = mapped_column(Float, nullable=True) notes: Mapped[str] = mapped_column(Text, default="") class FoodLog(Base): __tablename__ = "food_logs" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) logged_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) meal_type: Mapped[str] = mapped_column(String(32), default="snack") description: Mapped[str] = mapped_column(Text, default="") calories: Mapped[float] = mapped_column(Float, default=0) protein_g: Mapped[float] = mapped_column(Float, default=0) fat_g: Mapped[float] = mapped_column(Float, default=0) carbs_g: Mapped[float] = mapped_column(Float, default=0) source: Mapped[str] = mapped_column(String(32), default="llm") estimated: Mapped[bool] = mapped_column(Boolean, default=True) class WaterLog(Base): __tablename__ = "water_logs" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) logged_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) amount_ml: Mapped[int] = mapped_column(Integer) class WorkoutLog(Base): __tablename__ = "workout_logs" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) logged_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) title: Mapped[str] = mapped_column(String(255), default="Тренировка") notes: Mapped[str] = mapped_column(Text, default="") duration_min: Mapped[int | None] = mapped_column(Integer, nullable=True) exercises_json: Mapped[str] = mapped_column(Text, default="[]") class FitnessReminder(Base): __tablename__ = "fitness_reminders" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) kind: Mapped[str] = mapped_column(String(32)) hour: Mapped[int] = mapped_column(Integer, default=12) minute: Mapped[int] = mapped_column(Integer, default=0) interval_hours: Mapped[int | None] = mapped_column(Integer, nullable=True) enabled: Mapped[bool] = mapped_column(Boolean, default=True) last_fired_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) class ShoppingList(Base): __tablename__ = "shopping_lists" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(255), unique=True, index=True) sort_order: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) items: Mapped[list["ShoppingListItem"]] = relationship( back_populates="shopping_list", cascade="all, delete-orphan", order_by="ShoppingListItem.sort_order, ShoppingListItem.id", ) class ShoppingListItem(Base): __tablename__ = "shopping_list_items" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) list_id: Mapped[int] = mapped_column( ForeignKey("shopping_lists.id", ondelete="CASCADE"), index=True ) text: Mapped[str] = mapped_column(String(500)) quantity: Mapped[float | None] = mapped_column(Float, nullable=True) unit: Mapped[str] = mapped_column(String(64), default="") checked: Mapped[bool] = mapped_column(Boolean, default=False, index=True) sort_order: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) shopping_list: Mapped["ShoppingList"] = relationship(back_populates="items") class AssistantState(Base): __tablename__ = "assistant_state" key: Mapped[str] = mapped_column(String(128), primary_key=True) value: Mapped[str] = mapped_column(Text, default="") updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now() ) class WorkItem(Base): __tablename__ = "work_items" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) taiga_slug: Mapped[str] = mapped_column(String(255), index=True) taiga_project_id: Mapped[int] = mapped_column(Integer) taiga_story_id: Mapped[int] = mapped_column(Integer) taiga_story_ref: Mapped[int] = mapped_column(Integer, index=True) gitea_owner: Mapped[str] = mapped_column(String(255), default="") gitea_repo: Mapped[str] = mapped_column(String(255), default="") gitea_issue_number: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True) suggested_branch: Mapped[str] = mapped_column(String(255), default="") raw_text: Mapped[str] = mapped_column(Text, default="") title: Mapped[str] = mapped_column(String(500), default="") status: Mapped[str] = mapped_column(String(32), default="open") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) closed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)