Spaces:
Sleeping
Sleeping
| from typing import Optional, List | |
| from datetime import datetime, date, timezone | |
| from enum import Enum | |
| from sqlmodel import Field, SQLModel, Column | |
| from sqlalchemy import UniqueConstraint, JSON, Text | |
| from sqlalchemy.sql import func | |
| import uuid | |
| # --------------------------------------------------------------------------- | |
| # Helpers | |
| # --------------------------------------------------------------------------- | |
| def now_utc() -> datetime: | |
| return datetime.now(timezone.utc) | |
| def new_uuid() -> str: | |
| return str(uuid.uuid4()) | |
| # --------------------------------------------------------------------------- | |
| # Enums | |
| # --------------------------------------------------------------------------- | |
| class AuthProvider(str, Enum): | |
| email = "email" | |
| google = "google" | |
| class GoalEnum(str, Enum): | |
| weight_loss = "weight_loss" | |
| muscle_gain = "muscle_gain" | |
| maintain = "maintain" | |
| class GenderEnum(str, Enum): | |
| male = "male" | |
| female = "female" | |
| class SkillLevelEnum(str, Enum): | |
| beginner = "beginner" | |
| intermediate = "intermediate" | |
| advanced = "advanced" | |
| class IntensityEnum(str, Enum): | |
| low = "low" | |
| medium = "medium" | |
| high = "high" | |
| class DifficultyLevelEnum(str, Enum): | |
| level_1 = "level_1" | |
| level_2 = "level_2" | |
| level_3 = "level_3" | |
| class ExerciseTypeEnum(str, Enum): | |
| push_up = "push_up" | |
| sit_up = "sit_up" | |
| crunch = "crunch" | |
| knee_push_up = "knee_push_up" | |
| wall_push_up = "wall_push_up" | |
| decline_push_up = "decline_push_up" | |
| knuckle_push_up = "knuckle_push_up" | |
| class DayTypeEnum(str, Enum): | |
| workout_completed = "workout_completed" | |
| rest_day = "rest_day" | |
| missed = "missed" | |
| class ChatRoleEnum(str, Enum): | |
| user = "user" | |
| system = "system" | |
| assistant = "assistant" | |
| class ExerciseCategory(str, Enum): | |
| strength = "strength" | |
| cardio = "cardio" | |
| flexibility = "flexibility" | |
| balance = "balance" | |
| recovery = "recovery" | |
| class ExerciseDifficulty(str, Enum): | |
| beginner = "beginner" | |
| intermediate = "intermediate" | |
| advanced = "advanced" | |
| # --------------------------------------------------------------------------- | |
| # Section 1: Auth & Profile | |
| # --------------------------------------------------------------------------- | |
| class User(SQLModel, table=True): | |
| __tablename__ = "user" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| username: str = Field(unique=True, index=True) | |
| email: str = Field(unique=True, index=True) | |
| password: Optional[str] = Field(default=None) | |
| is_admin: bool = Field(default=False) | |
| authProvider: str = Field(default=AuthProvider.email, sa_column_kwargs={"name": "auth_provider"}) | |
| googleId: Optional[str] = Field(default=None, unique=True, sa_column_kwargs={"name": "google_id"}) | |
| photoUrl: Optional[str] = Field(default=None, sa_column_kwargs={"name": "photo_url"}) | |
| notificationEnabled: bool = Field(default=True, sa_column_kwargs={"name": "notification_enabled"}) | |
| deletedAt: Optional[datetime] = Field(default=None, sa_column_kwargs={"name": "deleted_at"}) | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| updatedAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "updated_at"}) | |
| class UserStats(SQLModel, table=True): | |
| __tablename__ = "userstats" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| user_id: str = Field(foreign_key="user.id", unique=True, index=True) | |
| currentStreak: int = Field(default=0, sa_column_kwargs={"name": "current_streak"}) | |
| longestStreak: int = Field(default=0, sa_column_kwargs={"name": "longest_streak"}) | |
| lastActiveDate: Optional[date] = Field(default=None, sa_column_kwargs={"name": "last_active_date"}) | |
| totalPushUps: int = Field(default=0, sa_column_kwargs={"name": "total_push_ups"}) | |
| totalSitUps: int = Field(default=0, sa_column_kwargs={"name": "total_sit_ups"}) | |
| updatedAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "updated_at"}) | |
| class PasswordResetToken(SQLModel, table=True): | |
| __tablename__ = "passwordresettoken" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| user_id: str = Field(foreign_key="user.id", index=True) | |
| token: str = Field(index=True) | |
| expiresAt: datetime = Field(sa_column_kwargs={"name": "expires_at"}) | |
| usedAt: Optional[datetime] = Field(default=None, sa_column_kwargs={"name": "used_at"}) | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| # --------------------------------------------------------------------------- | |
| # Section 2: Onboarding & Exercise Plan | |
| # --------------------------------------------------------------------------- | |
| class UserFitnessProfile(SQLModel, table=True): | |
| __tablename__ = "userfitnessprofile" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| user_id: str = Field(foreign_key="user.id", unique=True, index=True) | |
| goal: GoalEnum = Field() | |
| age: int | |
| gender: GenderEnum = Field() | |
| height: float | |
| weight: float | |
| skillLevel: SkillLevelEnum = Field(sa_column_kwargs={"name": "skill_level"}) | |
| intensity: IntensityEnum = Field() | |
| equipment: List[str] = Field(default=[], sa_column=Column("equipment", JSON)) | |
| fcsScoreRaw: int = Field(default=0, sa_column_kwargs={"name": "fcs_score_raw"}) | |
| difficultyLevel: DifficultyLevelEnum = Field(sa_column_kwargs={"name": "difficulty_level"}) | |
| bmr: float = Field(default=0.0) | |
| tdee: float = Field(default=0.0) | |
| target_daily_kcal: float = Field(default=0.0) | |
| macros_json: dict = Field(default={}, sa_column=Column(JSON)) | |
| difficulty_gate_applied: bool = Field(default=False) | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| updatedAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "updated_at"}) | |
| class ExercisePlan(SQLModel, table=True): | |
| __tablename__ = "exerciseplan" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| user_id: str = Field(foreign_key="user.id", index=True) | |
| fitness_profile_id: str = Field(foreign_key="userfitnessprofile.id") | |
| is_active: bool = Field(default=True) | |
| goal: GoalEnum = Field() | |
| days_per_week: int | |
| start_date: date | |
| difficulty_level: DifficultyLevelEnum = Field() | |
| applied_constraints: List[str] = Field(default=[], sa_column=Column("applied_constraints", JSON)) | |
| previous_plan_id: Optional[str] = Field(default=None, foreign_key="exerciseplan.id") | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| updatedAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "updated_at"}) | |
| class PlanDay(SQLModel, table=True): | |
| __tablename__ = "plan_day" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| plan_id: str = Field(foreign_key="exerciseplan.id", index=True) | |
| day_of_week: int = Field(ge=0, le=6) # 0=Monday, 6=Sunday | |
| is_rest_day: bool = Field(default=False) | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| class PlanDayExercise(SQLModel, table=True): | |
| __tablename__ = "plan_day_exercise" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| plan_day_id: str = Field(foreign_key="plan_day.id", index=True) | |
| exercise_id: uuid.UUID = Field(foreign_key="exercise.id") | |
| order: int | |
| target_sets: int | |
| target_reps: Optional[int] = Field(default=None) | |
| target_duration_seconds: Optional[int] = Field(default=None) | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| class Exercise(SQLModel, table=True): | |
| __tablename__ = "exercise" | |
| id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) | |
| # Identity | |
| name: str = Field(max_length=255, index=True) | |
| slug: str = Field(max_length=255, unique=True, index=True) # untuk URL/search | |
| description: str | |
| # Categorization | |
| category: ExerciseCategory | |
| muscleGroups: List[str] = Field(sa_column=Column("muscle_groups", JSON)) # primary muscles | |
| secondaryMuscles: List[str] = Field(default=[], sa_column=Column("secondary_muscles", JSON)) | |
| equipmentRequired: List[str] = Field(default=[], sa_column=Column("equipment_required", JSON)) | |
| # Gating | |
| difficulty: ExerciseDifficulty | |
| # Guidance | |
| instructions: List[str] = Field(sa_column=Column(JSON)) # step by step | |
| tips: List[str] = Field(default=[], sa_column=Column(JSON)) # common mistakes | |
| # Media | |
| imageUrl: Optional[str] = Field(default=None, sa_column_kwargs={"name": "image_url"}) | |
| videoUrl: Optional[str] = Field(default=None, sa_column_kwargs={"name": "video_url"}) | |
| # Meta | |
| isActive: bool = Field(default=True, sa_column_kwargs={"name": "is_active"}) | |
| createdAt: datetime = Field( | |
| default_factory=now_utc, | |
| sa_column_kwargs={"server_default": func.now(), "name": "created_at"} | |
| ) | |
| updatedAt: datetime = Field( | |
| default_factory=now_utc, | |
| sa_column_kwargs={ | |
| "server_default": func.now(), | |
| "onupdate": func.now(), | |
| "name": "updated_at" | |
| } | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Section 3: Workout Tracking | |
| # --------------------------------------------------------------------------- | |
| class WorkoutSession(SQLModel, table=True): | |
| __tablename__ = "workoutsession" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| user_id: str = Field(foreign_key="user.id", index=True) | |
| plan_id: Optional[str] = Field(default=None, foreign_key="exerciseplan.id") | |
| date: date | |
| duration_seconds: int = Field(default=0) | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| class ExerciseLog(SQLModel, table=True): | |
| """One row = one set performed in a session.""" | |
| __tablename__ = "exerciselog" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| session_id: str = Field(foreign_key="workoutsession.id", index=True) | |
| exercise_id: uuid.UUID = Field(foreign_key="exercise.id") | |
| set_number: int | |
| reps_completed: int | |
| is_manual_input: bool = Field(default=False) | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| class DailyLog(SQLModel, table=True): | |
| """Source of truth for streak calculation. UNIQUE(user_id, date).""" | |
| __tablename__ = "dailylog" | |
| __table_args__ = (UniqueConstraint("user_id", "date", name="uq_dailylog_user_date"),) | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| user_id: str = Field(foreign_key="user.id", index=True) | |
| date: date | |
| day_type: str = Field() | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| # --------------------------------------------------------------------------- | |
| # Section 5: Chatbot | |
| # --------------------------------------------------------------------------- | |
| class ChatSession(SQLModel, table=True): | |
| __tablename__ = "chatsession" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| user_id: str = Field(foreign_key="user.id", index=True) | |
| title: str = Field(default="New Chat") | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |
| updatedAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "updated_at"}) | |
| class ChatMessage(SQLModel, table=True): | |
| __tablename__ = "chatmessage" | |
| id: str = Field(default_factory=new_uuid, primary_key=True) | |
| session_id: str = Field(foreign_key="chatsession.id", index=True) | |
| role: str = Field() | |
| text: str = Field(sa_column=Column(Text)) | |
| sources: Optional[dict] = Field(default=None, sa_column=Column(JSON)) | |
| createdAt: datetime = Field(default_factory=now_utc, sa_column_kwargs={"name": "created_at"}) | |