v0.2 - 19 features: comparator, allergies, gamification, shopping list, achievements, stats, profile, share, bottom nav
This commit is contained in:
87
backend/app/routers/stats.py
Normal file
87
backend/app/routers/stats.py
Normal file
@@ -0,0 +1,87 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func, desc, asc
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from app.database import get_db
|
||||
from app.models.user import User
|
||||
from app.models.scan import Scan
|
||||
from app.utils.security import get_current_user
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["stats"])
|
||||
|
||||
@router.get("/stats")
|
||||
async def get_stats(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
||||
now = datetime.now(timezone.utc)
|
||||
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
# Total scans
|
||||
total_res = await db.execute(select(func.count(Scan.id)).where(Scan.user_id == user.id))
|
||||
total_scans = total_res.scalar() or 0
|
||||
|
||||
# Average score
|
||||
avg_res = await db.execute(select(func.avg(Scan.score)).where(Scan.user_id == user.id))
|
||||
avg_score = round(avg_res.scalar() or 0, 1)
|
||||
|
||||
# Monthly scans
|
||||
monthly_res = await db.execute(
|
||||
select(func.count(Scan.id)).where(Scan.user_id == user.id, Scan.scanned_at >= month_start)
|
||||
)
|
||||
monthly_scans = monthly_res.scalar() or 0
|
||||
|
||||
# Top 10 best this month
|
||||
best_res = await db.execute(
|
||||
select(Scan).where(Scan.user_id == user.id, Scan.scanned_at >= month_start)
|
||||
.order_by(desc(Scan.score)).limit(10)
|
||||
)
|
||||
best = [{"id": s.id, "product_name": s.product_name, "brand": s.brand, "score": s.score,
|
||||
"scanned_at": s.scanned_at.isoformat() if s.scanned_at else None} for s in best_res.scalars().all()]
|
||||
|
||||
# Top 10 worst this month
|
||||
worst_res = await db.execute(
|
||||
select(Scan).where(Scan.user_id == user.id, Scan.scanned_at >= month_start)
|
||||
.order_by(asc(Scan.score)).limit(10)
|
||||
)
|
||||
worst = [{"id": s.id, "product_name": s.product_name, "brand": s.brand, "score": s.score,
|
||||
"scanned_at": s.scanned_at.isoformat() if s.scanned_at else None} for s in worst_res.scalars().all()]
|
||||
|
||||
return {
|
||||
"total_scans": total_scans,
|
||||
"avg_score": avg_score,
|
||||
"monthly_scans": monthly_scans,
|
||||
"best": best,
|
||||
"worst": worst,
|
||||
}
|
||||
|
||||
@router.get("/stats/evolution")
|
||||
async def get_evolution(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
||||
now = datetime.now(timezone.utc)
|
||||
three_months_ago = now - timedelta(days=90)
|
||||
|
||||
result = await db.execute(
|
||||
select(Scan.scanned_at, Scan.score)
|
||||
.where(Scan.user_id == user.id, Scan.scanned_at >= three_months_ago)
|
||||
.order_by(Scan.scanned_at)
|
||||
)
|
||||
scans = result.all()
|
||||
|
||||
# Group by week
|
||||
weeks = {}
|
||||
for scanned_at, score in scans:
|
||||
# ISO week
|
||||
week_key = scanned_at.strftime("%Y-W%W")
|
||||
week_start = scanned_at - timedelta(days=scanned_at.weekday())
|
||||
if week_key not in weeks:
|
||||
weeks[week_key] = {"week": week_start.strftime("%d/%m"), "scores": [], "count": 0}
|
||||
weeks[week_key]["scores"].append(score)
|
||||
weeks[week_key]["count"] += 1
|
||||
|
||||
evolution = []
|
||||
for key in sorted(weeks.keys()):
|
||||
w = weeks[key]
|
||||
evolution.append({
|
||||
"week": w["week"],
|
||||
"avg_score": round(sum(w["scores"]) / len(w["scores"]), 1),
|
||||
"count": w["count"],
|
||||
})
|
||||
|
||||
return {"evolution": evolution}
|
||||
Reference in New Issue
Block a user