""" CalorieEntry service for managing calorie tracking operations """ from datetime import datetime, date, timedelta from typing import Optional, List, Dict, Any, Union from sqlalchemy import func from sqlalchemy.orm import Session, joinedload from app import models, schemas def get(db: Session, entry_id: int) -> Optional[models.CalorieEntry]: """ Get a calorie entry by ID """ return ( db.query(models.CalorieEntry) .filter(models.CalorieEntry.id == entry_id) .options(joinedload(models.CalorieEntry.food)) .first() ) def get_multi_by_user( db: Session, *, user_id: int, skip: int = 0, limit: int = 100, start_date: Optional[date] = None, end_date: Optional[date] = None, meal_type: Optional[str] = None, ) -> List[models.CalorieEntry]: """ Get multiple calorie entries for a specific user with optional filters """ query = ( db.query(models.CalorieEntry) .filter(models.CalorieEntry.user_id == user_id) .options(joinedload(models.CalorieEntry.food)) ) if start_date: query = query.filter( func.date(models.CalorieEntry.consumed_at) >= start_date ) if end_date: query = query.filter( func.date(models.CalorieEntry.consumed_at) <= end_date ) if meal_type: query = query.filter(models.CalorieEntry.meal_type == meal_type) return ( query.order_by(models.CalorieEntry.consumed_at.desc()) .offset(skip) .limit(limit) .all() ) def get_daily_summary( db: Session, *, user_id: int, date_: date ) -> Dict[str, Any]: """ Get a summary of calories for a specific date """ start_datetime = datetime.combine(date_, datetime.min.time()) end_datetime = datetime.combine(date_, datetime.max.time()) entries = ( db.query(models.CalorieEntry) .join(models.Food, models.CalorieEntry.food_id == models.Food.id) .filter(models.CalorieEntry.user_id == user_id) .filter(models.CalorieEntry.consumed_at >= start_datetime) .filter(models.CalorieEntry.consumed_at <= end_datetime) .options(joinedload(models.CalorieEntry.food)) .all() ) # Calculate total calories and nutrients total_calories = 0.0 total_protein = 0.0 total_carbs = 0.0 total_fat = 0.0 total_fiber = 0.0 meal_summary = {} for entry in entries: # Calculate calories for this entry calories = entry.quantity_g / 100 * entry.food.calories_per_100g total_calories += calories # Calculate nutrients if available if entry.food.protein_g: total_protein += entry.quantity_g / 100 * entry.food.protein_g if entry.food.carbs_g: total_carbs += entry.quantity_g / 100 * entry.food.carbs_g if entry.food.fat_g: total_fat += entry.quantity_g / 100 * entry.food.fat_g if entry.food.fiber_g: total_fiber += entry.quantity_g / 100 * entry.food.fiber_g # Add to meal summary meal_type = entry.meal_type or "Other" if meal_type not in meal_summary: meal_summary[meal_type] = 0.0 meal_summary[meal_type] += calories # Get user's target calories user = db.query(models.User).filter(models.User.id == user_id).first() target_calories = user.target_calories if user else 2000.0 return { "date": date_, "total_calories": round(total_calories, 1), "target_calories": target_calories, "remaining_calories": round(target_calories - total_calories, 1), "total_protein_g": round(total_protein, 1), "total_carbs_g": round(total_carbs, 1), "total_fat_g": round(total_fat, 1), "total_fiber_g": round(total_fiber, 1), "meal_summary": {k: round(v, 1) for k, v in meal_summary.items()}, "entry_count": len(entries), } def create( db: Session, *, obj_in: schemas.CalorieEntryCreate, user_id: int ) -> models.CalorieEntry: """ Create a new calorie entry """ db_obj = models.CalorieEntry( user_id=user_id, food_id=obj_in.food_id, quantity_g=obj_in.quantity_g, meal_type=obj_in.meal_type, notes=obj_in.notes, consumed_at=obj_in.consumed_at or datetime.utcnow(), ) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def update( db: Session, *, db_obj: models.CalorieEntry, obj_in: Union[schemas.CalorieEntryUpdate, Dict[str, Any]] ) -> models.CalorieEntry: """ Update a calorie entry """ if isinstance(obj_in, dict): update_data = obj_in else: update_data = obj_in.model_dump(exclude_unset=True) for field in update_data: if hasattr(db_obj, field): setattr(db_obj, field, update_data[field]) db.add(db_obj) db.commit() db.refresh(db_obj) return db_obj def delete(db: Session, *, entry_id: int) -> models.CalorieEntry: """ Delete a calorie entry """ entry = db.query(models.CalorieEntry).filter(models.CalorieEntry.id == entry_id).first() db.delete(entry) db.commit() return entry def get_weekly_summary( db: Session, *, user_id: int, end_date: date = None ) -> List[Dict[str, Any]]: """ Get a weekly summary of calories """ if end_date is None: end_date = date.today() start_date = end_date - timedelta(days=6) # Last 7 days summaries = [] current_date = start_date while current_date <= end_date: daily_summary = get_daily_summary(db, user_id=user_id, date_=current_date) summaries.append(daily_summary) current_date += timedelta(days=1) return summaries