99 lines
3.7 KiB
Python
99 lines
3.7 KiB
Python
from fastapi import APIRouter
|
|
from fastapi import HTTPException
|
|
|
|
from app.services.repository import repo
|
|
from app.services.recipe_search import recipe_search
|
|
from app.services.store import JsonStore
|
|
|
|
|
|
router = APIRouter(prefix="/api", tags=["dashboard"])
|
|
store = JsonStore()
|
|
|
|
|
|
def usable_results(items):
|
|
return [entry for entry in items if str(entry.get("status") or "suggested") != "rejected"]
|
|
|
|
|
|
@router.get("/health")
|
|
async def health():
|
|
return {"status": "ok", "backend": repo.backend}
|
|
|
|
|
|
@router.get("/dashboard/summary")
|
|
async def summary():
|
|
profile = repo.get_profile()
|
|
suggestions = repo.list_suggestions()
|
|
plans = repo.list_meal_plans()
|
|
failed_searches = repo.recent_failed_searches(limit=6)
|
|
counts = {
|
|
"suggested": sum(1 for item in suggestions if item.get("status") == "suggested"),
|
|
"approved": sum(1 for item in suggestions if item.get("status") == "approved"),
|
|
"saved": sum(1 for item in suggestions if item.get("status") == "saved"),
|
|
"rejected": sum(1 for item in suggestions if item.get("status") == "rejected"),
|
|
"plans": len(plans),
|
|
"failed_searches": len(repo.list_failed_searches()),
|
|
}
|
|
return {
|
|
"backend": repo.backend,
|
|
"profile": profile,
|
|
"counts": counts,
|
|
"recent_suggestions": repo.recent_suggestions(),
|
|
"recent_plans": list(reversed(plans[-4:])),
|
|
"recent_failed_searches": failed_searches,
|
|
}
|
|
|
|
|
|
@router.get("/failed-searches")
|
|
async def failed_searches():
|
|
return {"status": "ok", "items": repo.list_failed_searches()}
|
|
|
|
|
|
@router.get("/failed-searches/{failed_search_id}/results")
|
|
async def failed_search_results(failed_search_id: str):
|
|
item = next((entry for entry in repo.list_failed_searches() if str(entry.get("id")) == str(failed_search_id)), None)
|
|
if not item:
|
|
raise HTTPException(status_code=404, detail="Failed search not found.")
|
|
query = str(item.get("query") or "").strip()
|
|
if not query:
|
|
raise HTTPException(status_code=400, detail="Failed search query is empty.")
|
|
all_results = recipe_search.search(query, limit=10)
|
|
results = usable_results(all_results)
|
|
updates = {
|
|
"last_checked_at": store.now(),
|
|
"updated_at": store.now(),
|
|
"result_count": len(results),
|
|
"sample_titles": [str(entry.get("title") or "") for entry in results[:4]],
|
|
}
|
|
if results:
|
|
updates["status"] = "resolved-with-recipes"
|
|
updates["resolved_at"] = str(item.get("resolved_at") or store.now())
|
|
saved = repo.update_failed_search(str(item.get("id")), updates) or {**item, **updates}
|
|
return {"status": "ok", "item": saved, "results": results}
|
|
|
|
|
|
@router.post("/failed-searches/refresh")
|
|
async def refresh_failed_searches():
|
|
items = repo.list_failed_searches()
|
|
refreshed = []
|
|
for item in items:
|
|
query = str(item.get("query") or "").strip()
|
|
status = str(item.get("status") or "open")
|
|
if not query or status != "open":
|
|
refreshed.append(item)
|
|
continue
|
|
all_results = recipe_search.search(query, limit=10)
|
|
results = usable_results(all_results)
|
|
preview_titles = [str(entry.get("title") or "") for entry in results[:4] if str(entry.get("title") or "").strip()]
|
|
result_count = len(results)
|
|
updates = {
|
|
"last_checked_at": store.now(),
|
|
"result_count": result_count,
|
|
"sample_titles": preview_titles,
|
|
}
|
|
if result_count > 0:
|
|
updates["status"] = "resolved-with-recipes"
|
|
updates["resolved_at"] = store.now()
|
|
saved = repo.update_failed_search(str(item.get("id")), updates) or {**item, **updates}
|
|
refreshed.append(saved)
|
|
return {"status": "ok", "items": refreshed}
|