Initial commit: HabitForge - habit tracking application with FastAPI backend, React frontend, and Zepp integration
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
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
|
||||
Reference in New Issue
Block a user