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