mirror of
https://github.com/sstent/foodplanner.git
synced 2026-01-26 11:41:39 +00:00
137 lines
5.1 KiB
Python
137 lines
5.1 KiB
Python
from fastapi import APIRouter, Depends, Query, Request
|
|
from starlette.responses import HTMLResponse
|
|
from sqlalchemy.orm import Session
|
|
from datetime import date, timedelta
|
|
from typing import List
|
|
from app.database import get_db, TrackedDay, TrackedMeal, calculate_day_nutrition_tracked, WeightLog
|
|
|
|
router = APIRouter(tags=["charts"])
|
|
|
|
@router.get("/charts", response_class=HTMLResponse)
|
|
async def charts_page(request: Request, person: str = "Sarah", db: Session = Depends(get_db)):
|
|
"""Render the charts page"""
|
|
from main import templates
|
|
return templates.TemplateResponse("charts.html", {
|
|
"request": request,
|
|
"person": person
|
|
})
|
|
|
|
@router.get("/api/charts", response_model=List[dict])
|
|
async def get_charts_data(
|
|
person: str = Query(..., description="Person name (e.g., Sarah)"),
|
|
days: int = Query(7, description="Number of past days to fetch data for", ge=1, le=30),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""
|
|
Get daily calorie data for the last N days for a person.
|
|
Returns list of {"date": "YYYY-MM-DD", "calories": float} sorted by date descending.
|
|
"""
|
|
|
|
end_date = date.today()
|
|
start_date = end_date - timedelta(days=days - 1)
|
|
|
|
tracked_days = db.query(TrackedDay).filter(
|
|
TrackedDay.person == person,
|
|
TrackedDay.date >= start_date,
|
|
TrackedDay.date <= end_date
|
|
).order_by(TrackedDay.date.desc()).all()
|
|
|
|
chart_data = []
|
|
# Fetch all tracked days and weight logs for the period
|
|
tracked_days_map = {
|
|
d.date: d for d in db.query(TrackedDay).filter(
|
|
TrackedDay.person == person,
|
|
TrackedDay.date >= start_date,
|
|
TrackedDay.date <= end_date
|
|
).all()
|
|
}
|
|
|
|
# Sort logs desc
|
|
weight_logs_map = {
|
|
w.date: w for w in db.query(WeightLog).filter(
|
|
WeightLog.date >= start_date,
|
|
WeightLog.date <= end_date
|
|
).order_by(WeightLog.date.desc()).all()
|
|
}
|
|
|
|
# Get last weight BEFORE start_date (for initial carry forward)
|
|
last_historical_weight_log = db.query(WeightLog).filter(
|
|
WeightLog.date < start_date
|
|
).order_by(WeightLog.date.desc()).first()
|
|
|
|
last_historical_weight_val = last_historical_weight_log.weight * 2.20462 if last_historical_weight_log else None
|
|
|
|
# Find the most recent weight available (either in range or history)
|
|
# This is for "Today" (end_date)
|
|
latest_weight_val = last_historical_weight_val
|
|
|
|
# Check if we have newer weights in the map
|
|
# Values in weight_logs_map are WeightLog objects.
|
|
# Find the one with max date <= end_date. Since map key is date, we can check.
|
|
# But filtering the map is tedious. Let's just iterate.
|
|
# Actually, we already have `weight_logs_map` (in range).
|
|
# If the range has weights, the newest one is the "latest" known weight relevant to the end of chart.
|
|
if weight_logs_map:
|
|
# Get max date
|
|
max_date = max(weight_logs_map.keys())
|
|
latest_weight_val = weight_logs_map[max_date].weight * 2.20462
|
|
|
|
chart_data = []
|
|
|
|
# Iterate dates. Note: i=0 is end_date (Today), i=days-1 is start_date (Oldest)
|
|
for i in range(days):
|
|
current_date = end_date - timedelta(days=i)
|
|
|
|
tracked_day = tracked_days_map.get(current_date)
|
|
weight_log = weight_logs_map.get(current_date)
|
|
|
|
calories = 0
|
|
protein = 0
|
|
fat = 0
|
|
net_carbs = 0
|
|
|
|
# Calculate nutrition
|
|
if tracked_day:
|
|
tracked_meals = db.query(TrackedMeal).filter(
|
|
TrackedMeal.tracked_day_id == tracked_day.id
|
|
).all()
|
|
day_totals = calculate_day_nutrition_tracked(tracked_meals, db)
|
|
calories = round(day_totals.get("calories", 0), 2)
|
|
protein = round(day_totals.get("protein", 0), 2)
|
|
fat = round(day_totals.get("fat", 0), 2)
|
|
net_carbs = round(day_totals.get("net_carbs", 0), 2)
|
|
|
|
weight_lbs = None
|
|
is_real = False
|
|
|
|
if weight_log:
|
|
weight_lbs = round(weight_log.weight * 2.20462, 2)
|
|
is_real = True
|
|
|
|
# Logic for Start and End Points (to ensure line connects across view)
|
|
|
|
# If this is the Oldest date in view (start_date) and no real weight
|
|
if i == days - 1 and weight_lbs is None:
|
|
# Use historical weight if available (to start the line)
|
|
if last_historical_weight_val is not None:
|
|
weight_lbs = round(last_historical_weight_val, 2)
|
|
# is_real remains False (inferred)
|
|
|
|
# If this is the Newest date in view (end_date/Today) and no real weight
|
|
if i == 0 and weight_lbs is None:
|
|
# Use latest known weight (to end the line)
|
|
if latest_weight_val is not None:
|
|
weight_lbs = round(latest_weight_val, 2)
|
|
# is_real remains False (inferred)
|
|
|
|
chart_data.append({
|
|
"date": current_date.isoformat(),
|
|
"calories": calories,
|
|
"protein": protein,
|
|
"fat": fat,
|
|
"net_carbs": net_carbs,
|
|
"weight_lbs": weight_lbs,
|
|
"weight_is_real": is_real
|
|
})
|
|
|
|
return chart_data |