SmaFit-FastApi / models.py
JustParadis's picture
Deploy to Hugging Face without chroma_db
abf6602
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"})