diff --git a/.gitignore b/.gitignore index 9586b09..16b3345 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ yarn-error.log dist/ build/ .output/ +frontend/catalyst-ui-kit 2/ # Docker postgres_data/ diff --git a/backend/app/ai/nutrition.py b/backend/app/ai/nutrition.py index c148559..fc92b25 100644 --- a/backend/app/ai/nutrition.py +++ b/backend/app/ai/nutrition.py @@ -61,9 +61,7 @@ class NutritionModule(dspy.Module): pred = self.extract(description=description) # Assertion: Check Macro Consistency - calc_cals = ( - (pred.nutritional_info.protein * 4) + (pred.nutritional_info.carbs * 4) + (pred.nutritional_info.fats * 9) - ) + # calc_cals calculation removed as dspy.Suggest is disabled # dspy.Suggest is not available in dspy>=3.1.0 # dspy.Suggest( @@ -78,9 +76,7 @@ class NutritionModule(dspy.Module): pred = self.analyze_image(image=image, description=description) # Assertion: Check Macro Consistency - calc_cals = ( - (pred.nutritional_info.protein * 4) + (pred.nutritional_info.carbs * 4) + (pred.nutritional_info.fats * 9) - ) + # calc_cals calculation removed as dspy.Suggest is disabled # dspy.Suggest is not available in dspy>=3.1.0 # dspy.Suggest( diff --git a/backend/app/api/v1/endpoints/nutrition.py b/backend/app/api/v1/endpoints/nutrition.py index dd7c3fe..61aa4da 100644 --- a/backend/app/api/v1/endpoints/nutrition.py +++ b/backend/app/api/v1/endpoints/nutrition.py @@ -1,7 +1,6 @@ -import litellm -import dspy from typing import Any +import litellm from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile from pydantic import BaseModel from sqlmodel import Session @@ -70,3 +69,25 @@ def log_food( session.commit() session.refresh(food_log) return food_log + + +@router.get("/logs", response_model=list[FoodLog]) +def read_logs( + current_user: deps.CurrentUser, + session: Session = Depends(deps.get_session), + skip: int = 0, + limit: int = 100, +) -> Any: + """ + Get food logs for current user. + """ + from sqlmodel import select + + statement = ( + select(FoodLog) + .where(FoodLog.user_id == current_user.id) + .order_by(FoodLog.timestamp.desc()) + .offset(skip) + .limit(limit) + ) + return session.exec(statement).all() diff --git a/backend/app/api/v1/endpoints/users.py b/backend/app/api/v1/endpoints/users.py index caca4e2..5d58219 100644 --- a/backend/app/api/v1/endpoints/users.py +++ b/backend/app/api/v1/endpoints/users.py @@ -6,7 +6,7 @@ from sqlmodel import Session, select from app.api import deps from app.core import security from app.models.user import User -from app.schemas.user import UserCreate, UserRead +from app.schemas.user import UserCreate, UserRead, UserUpdate router = APIRouter() @@ -36,3 +36,31 @@ def create_user( session.commit() session.refresh(user) return user + + +@router.get("/me", response_model=UserRead) +def read_user_me( + current_user: deps.CurrentUser, +) -> Any: + """ + Get current user. + """ + return current_user + + +@router.put("/me", response_model=UserRead) +def update_user_me( + *, + session: Session = Depends(deps.get_session), + user_in: UserUpdate, + current_user: deps.CurrentUser, +) -> Any: + """ + Update own user. + """ + user_data = user_in.model_dump(exclude_unset=True) + current_user.sqlmodel_update(user_data) + session.add(current_user) + session.commit() + session.refresh(current_user) + return current_user diff --git a/backend/app/models/user.py b/backend/app/models/user.py index 5bd0bc3..2205236 100644 --- a/backend/app/models/user.py +++ b/backend/app/models/user.py @@ -9,4 +9,14 @@ class User(SQLModel, table=True): username: str = Field(index=True, unique=True) email: str = Field(index=True, unique=True) password_hash: str + + # Profile Info + firstname: Optional[str] = None + lastname: Optional[str] = None + age: Optional[int] = None + gender: Optional[str] = None + height: Optional[float] = None + weight: Optional[float] = None + unit_preference: str = Field(default="metric") # "metric" or "imperial" + created_at: datetime = Field(default_factory=datetime.utcnow) diff --git a/backend/app/schemas/user.py b/backend/app/schemas/user.py index f50425f..b8f1ddf 100644 --- a/backend/app/schemas/user.py +++ b/backend/app/schemas/user.py @@ -14,9 +14,23 @@ class UserCreate(UserBase): class UserRead(UserBase): id: int + firstname: Optional[str] = None + lastname: Optional[str] = None + age: Optional[int] = None + gender: Optional[str] = None + height: Optional[float] = None + weight: Optional[float] = None + unit_preference: str = "metric" class UserUpdate(SQLModel): email: Optional[str] = None username: Optional[str] = None password: Optional[str] = None + firstname: Optional[str] = None + lastname: Optional[str] = None + age: Optional[int] = None + gender: Optional[str] = None + height: Optional[float] = None + weight: Optional[float] = None + unit_preference: Optional[str] = None diff --git a/backend/migrate_user_table.py b/backend/migrate_user_table.py new file mode 100644 index 0000000..9ed98e0 --- /dev/null +++ b/backend/migrate_user_table.py @@ -0,0 +1,32 @@ +from sqlmodel import Session, text + +from app.db import engine + + +def migrate(): + print("Starting migration...") + with Session(engine) as session: + columns = [ + ("firstname", "VARCHAR"), + ("lastname", "VARCHAR"), + ("age", "INTEGER"), + ("gender", "VARCHAR"), + ("height", "FLOAT"), + ("weight", "FLOAT"), + ("unit_preference", "VARCHAR DEFAULT 'metric'"), + ] + + for col, type_ in columns: + try: + print(f"Adding {col}...") + # Using "user" with quotes to avoid reserved keyword issues, though SQLModel usually handles it + session.exec(text(f'ALTER TABLE "user" ADD COLUMN IF NOT EXISTS {col} {type_}')) + session.commit() + print(f"Added {col}") + except Exception as e: + print(f"Error adding {col}: {e}") + session.rollback() + + +if __name__ == "__main__": + migrate() diff --git a/frontend/index.html b/frontend/index.html index c20fbd3..072a57e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -8,6 +8,6 @@
- +