Files
HabitForge/backend/models.py
T
2026-07-30 23:25:20 -04:00

180 lines
5.7 KiB
Python

from sqlalchemy import Column, Integer, String, Date, Boolean, ForeignKey, Text, Float, DateTime
from sqlalchemy.orm import relationship
from .database import Base
from pydantic import BaseModel
from typing import Optional, List
from datetime import date
# --- SQLAlchemy Models ---
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
challenges = relationship("Challenge", back_populates="owner")
health_metrics = relationship("DailyHealthMetrics", back_populates="user")
workouts = relationship("WorkoutSession", back_populates="user")
class Challenge(Base):
__tablename__ = "challenges"
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
period_type = Column(String) # 'week', 'month', 'year'
daily_target = Column(Integer)
start_date = Column(Date)
end_date = Column(Date)
created_at = Column(Date, default=date.today)
display_order = Column(Integer, default=0)
user_id = Column(Integer, ForeignKey("users.id")) # New FK
unit_type = Column(String, default="reps") # New: 'reps', 'minutes', 'pages', 'km'
rest_days = Column(String, nullable=True) # New: comma-separated list of days
icon = Column(String, default="fa-dumbbell") # New
image_url = Column(String, nullable=True) # New
description = Column(String, nullable=True) # New
frequency_count = Column(Integer, nullable=True) # New
selected_days = Column(String, nullable=True) # New
owner = relationship("User", back_populates="challenges") # New Rel
trackings = relationship("Tracking", back_populates="challenge", cascade="all, delete-orphan")
class Tracking(Base):
__tablename__ = "tracking"
id = Column(Integer, primary_key=True, index=True)
challenge_id = Column(Integer, ForeignKey("challenges.id"))
date = Column(Date)
reps = Column(Integer, default=0)
completed = Column(Boolean, default=False)
notes = Column(Text, nullable=True)
challenge = relationship("Challenge", back_populates="trackings")
class DailyHealthMetrics(Base):
__tablename__ = "daily_health_metrics"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"))
date = Column(Date, index=True)
step_count = Column(Integer, default=0)
distance_meters = Column(Float, default=0.0)
calories_burned = Column(Float, default=0.0)
sleep_duration_minutes = Column(Integer, default=0)
deep_sleep_minutes = Column(Integer, default=0)
light_sleep_minutes = Column(Integer, default=0) # New
rem_sleep_minutes = Column(Integer, default=0) # New
awake_duration_minutes = Column(Integer, default=0)
avg_heart_rate = Column(Integer, nullable=True)
min_heart_rate = Column(Integer, nullable=True)
max_heart_rate = Column(Integer, nullable=True)
avg_spo2 = Column(Float, nullable=True)
pai_score = Column(Float, default=0.0)
weight = Column(Float, nullable=True) # New
resting_heart_rate = Column(Integer, nullable=True) # New
hrv = Column(Float, nullable=True) # New
user = relationship("User", back_populates="health_metrics")
class WorkoutSession(Base):
__tablename__ = "workout_sessions"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("users.id"))
start_time = Column(DateTime)
end_time = Column(DateTime)
activity_type = Column(String)
calories = Column(Float, default=0.0)
avg_hr = Column(Integer, nullable=True)
user = relationship("User", back_populates="workouts")
# --- Pydantic Schemas ---
class TrackingBase(BaseModel):
date: date
reps: int
completed: bool = False
notes: Optional[str] = None
class TrackingCreate(TrackingBase):
challenge_id: int
class TrackingResponse(TrackingBase):
id: int
challenge_id: int
class Config:
from_attributes = True
class ChallengeBase(BaseModel):
name: str
period_type: str
daily_target: int
start_date: date
end_date: Optional[date] = None
unit_type: Optional[str] = "reps"
rest_days: Optional[str] = None
icon: Optional[str] = "fa-dumbbell"
image_url: Optional[str] = None
description: Optional[str] = None
frequency_count: Optional[int] = None # For Monthly frequency (X times per month)
selected_days: Optional[str] = None # For Weekly frequency (Mon,Wed,Fri)
class ChallengeCreate(ChallengeBase):
pass
class ChallengeResponse(ChallengeBase):
id: int
created_at: date
display_order: int = 0
trackings: List[TrackingResponse] = []
class Config:
from_attributes = True
class DailyHealthMetricsBase(BaseModel):
date: date
step_count: Optional[int] = 0
distance_meters: Optional[float] = 0.0
calories_burned: Optional[float] = 0.0
sleep_duration_minutes: Optional[int] = 0
deep_sleep_minutes: Optional[int] = 0
light_sleep_minutes: Optional[int] = 0
rem_sleep_minutes: Optional[int] = 0
awake_duration_minutes: Optional[int] = 0
avg_heart_rate: Optional[int] = None
min_heart_rate: Optional[int] = None
max_heart_rate: Optional[int] = None
avg_spo2: Optional[float] = None
pai_score: Optional[float] = 0.0
weight: Optional[float] = None
resting_heart_rate: Optional[int] = None
hrv: Optional[float] = None
class DailyHealthMetricsResponse(DailyHealthMetricsBase):
id: int
user_id: int
class Config:
from_attributes = True
# Auth Schemas
class UserCreate(BaseModel):
username: str
password: str
class UserResponse(BaseModel):
id: int
username: str
class Config:
from_attributes = True
class Token(BaseModel):
access_token: str
token_type: str