180 lines
5.7 KiB
Python
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
|