from flask_sqlalchemy import SQLAlchemy from flask_login import UserMixin from datetime import datetime, date db = SQLAlchemy() class User(UserMixin, db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) password = db.Column(db.String(200), nullable=False) name = db.Column(db.String(100)) age = db.Column(db.Integer) gender = db.Column(db.String(10)) height_cm = db.Column(db.Float) weight_kg = db.Column(db.Float) activity_level = db.Column(db.String(20), default='moderate') target_daily_calories = db.Column(db.Integer, default=2000) created_at = db.Column(db.DateTime, default=datetime.utcnow) # Relationships meals = db.relationship('Meal', backref='user', lazy=True, cascade='all, delete-orphan') weight_logs = db.relationship('WeightLog', backref='user', lazy=True, cascade='all, delete-orphan') water_logs = db.relationship('WaterLog', backref='user', lazy=True, cascade='all, delete-orphan') meal_plans = db.relationship('MealPlan', backref='user', lazy=True, cascade='all, delete-orphan') goals = db.relationship('UserGoal', backref='user', uselist=False, cascade='all, delete-orphan') class FoodItem(db.Model): __tablename__ = 'food_items' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(200), nullable=False) name_tagalog = db.Column(db.String(200)) category = db.Column(db.String(50)) calories = db.Column(db.Float, nullable=False) protein_g = db.Column(db.Float, default=0) carbs_g = db.Column(db.Float, default=0) fat_g = db.Column(db.Float, default=0) fiber_g = db.Column(db.Float, default=0) sugar_g = db.Column(db.Float, default=0) sodium_mg = db.Column(db.Float, default=0) serving_size_g = db.Column(db.Float, default=100) serving_description = db.Column(db.String(100)) source = db.Column(db.String(20), default='manual') # 'manual', 'api', 'filipino' is_filipino = db.Column(db.Boolean, default=False) is_favorite = db.Column(db.Boolean, default=False) api_data = db.Column(db.Text) last_updated = db.Column(db.DateTime, default=datetime.utcnow) # Relationships meal_foods = db.relationship('MealFood', backref='food', lazy=True) planned_foods = db.relationship('PlannedFood', backref='food', lazy=True) class Meal(db.Model): __tablename__ = 'meals' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) date = db.Column(db.Date, nullable=False, default=date.today) meal_type = db.Column(db.String(20), nullable=False) # breakfast, lunch, dinner, snack time = db.Column(db.Time) notes = db.Column(db.Text) # Relationships foods = db.relationship('MealFood', backref='meal', lazy=True, cascade='all, delete-orphan') def calculate_totals(self): """Calculate total nutrition for this meal""" totals = { 'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0 } for mf in self.foods: totals['calories'] += mf.calories_consumed totals['protein'] += mf.protein_consumed totals['carbs'] += mf.carbs_consumed totals['fat'] += mf.fat_consumed return totals class MealFood(db.Model): __tablename__ = 'meal_foods' id = db.Column(db.Integer, primary_key=True) meal_id = db.Column(db.Integer, db.ForeignKey('meals.id'), nullable=False) food_id = db.Column(db.Integer, db.ForeignKey('food_items.id'), nullable=False) quantity = db.Column(db.Float, nullable=False, default=1.0) # servings quantity_grams = db.Column(db.Float) calories_consumed = db.Column(db.Float) protein_consumed = db.Column(db.Float) carbs_consumed = db.Column(db.Float) fat_consumed = db.Column(db.Float) def calculate_nutrition(self): """Calculate nutrition based on quantity""" self.calories_consumed = self.food.calories * self.quantity self.protein_consumed = self.food.protein_g * self.quantity self.carbs_consumed = self.food.carbs_g * self.quantity self.fat_consumed = self.food.fat_g * self.quantity if self.food.serving_size_g: self.quantity_grams = self.food.serving_size_g * self.quantity class WaterLog(db.Model): __tablename__ = 'water_logs' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) date = db.Column(db.Date, nullable=False, default=date.today) amount_ml = db.Column(db.Integer, nullable=False) time = db.Column(db.Time, default=datetime.now().time) class WeightLog(db.Model): __tablename__ = 'weight_logs' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) date = db.Column(db.Date, nullable=False, unique=True, default=date.today) weight_kg = db.Column(db.Float, nullable=False) body_fat_percentage = db.Column(db.Float) notes = db.Column(db.Text) time = db.Column(db.Time, default=datetime.now().time) class MealPlan(db.Model): __tablename__ = 'meal_plans' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) date = db.Column(db.Date, nullable=False) meal_type = db.Column(db.String(20), nullable=False) is_completed = db.Column(db.Boolean, default=False) notes = db.Column(db.Text) # Relationships foods = db.relationship('PlannedFood', backref='meal_plan', lazy=True, cascade='all, delete-orphan') def calculate_totals(self): """Calculate total nutrition for this planned meal""" totals = { 'calories': 0, 'protein': 0, 'carbs': 0, 'fat': 0 } for pf in self.foods: totals['calories'] += pf.food.calories * pf.quantity totals['protein'] += pf.food.protein_g * pf.quantity totals['carbs'] += pf.food.carbs_g * pf.quantity totals['fat'] += pf.food.fat_g * pf.quantity return totals class PlannedFood(db.Model): __tablename__ = 'planned_foods' id = db.Column(db.Integer, primary_key=True) meal_plan_id = db.Column(db.Integer, db.ForeignKey('meal_plans.id'), nullable=False) food_id = db.Column(db.Integer, db.ForeignKey('food_items.id'), nullable=False) quantity = db.Column(db.Float, nullable=False, default=1.0) class UserGoal(db.Model): __tablename__ = 'user_goals' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False, unique=True) goal_type = db.Column(db.String(20), default='recomp') # weight_loss, muscle_gain, recomp target_weight_kg = db.Column(db.Float) weekly_goal_kg = db.Column(db.Float, default=0.5) target_protein_g = db.Column(db.Integer, default=150) target_carbs_g = db.Column(db.Integer, default=200) target_fat_g = db.Column(db.Integer, default=60) target_water_ml = db.Column(db.Integer, default=2000) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) class DailySummary(db.Model): __tablename__ = 'daily_summary' id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) date = db.Column(db.Date, nullable=False, unique=True) total_calories = db.Column(db.Float, default=0) total_protein_g = db.Column(db.Float, default=0) total_carbs_g = db.Column(db.Float, default=0) total_fat_g = db.Column(db.Float, default=0) total_water_ml = db.Column(db.Integer, default=0) calories_remaining = db.Column(db.Float) weight_kg = db.Column(db.Float) notes = db.Column(db.Text) class APICache(db.Model): __tablename__ = 'api_cache' id = db.Column(db.Integer, primary_key=True) query = db.Column(db.String(200), nullable=False, unique=True) api_source = db.Column(db.String(50)) response_json = db.Column(db.Text) cached_at = db.Column(db.DateTime, default=datetime.utcnow)