from fastapi import APIRouter, Cookie from app.db import auth_db from app.db.analytics import ( get_all_recommendations, get_cron_run_logs, get_cron_run_summary, get_observation_candidates, get_pipeline_run_detail, get_pipeline_runs, get_review_stats, get_screening_history, get_stats, ) from app.db.recommendation_queries import get_active_recommendations, get_active_recommendations_deduped from app.config.config_loader import get_signal_weights from app.web.shared import ( ObservationRequest, PushRulesRequest, WatchlistRequest, require_api_user_with_subscription, ) router = APIRouter() @router.get("/api/stats") async def api_stats(altcoin_session: str = Cookie(default="")): require_api_user_with_subscription(altcoin_session) return get_stats() @router.get("/api/recommendations") async def api_recommendations( limit: int = 50, offset: int = 0, decision_only: bool = False, version: str = "", paged: bool = False, compact: bool = False, altcoin_session: str = Cookie(default=""), ): require_api_user_with_subscription(altcoin_session) return get_all_recommendations(limit, decision_only=decision_only, version=version, offset=offset, with_meta=(paged or compact)) @router.get("/api/recommendations/active") async def api_recommendations_active( dedup: bool = True, actionable_only: bool = True, version: str = "", hours: float = 0, limit: int = 0, offset: int = 0, paged: bool = False, compact: bool = False, altcoin_session: str = Cookie(default=""), ): require_api_user_with_subscription(altcoin_session) if dedup: return get_active_recommendations_deduped( actionable_only=actionable_only, version=version, hours=hours, limit=limit, offset=offset, with_meta=(paged or compact), ) return get_active_recommendations(actionable_only=actionable_only) @router.get("/api/observations/active") async def api_observations_active( limit: int = 50, altcoin_session: str = Cookie(default=""), ): require_api_user_with_subscription(altcoin_session) return get_observation_candidates(limit=limit) @router.get("/api/personalization") async def api_personalization(altcoin_session: str = Cookie(default="")): user = require_api_user_with_subscription(altcoin_session) return { "watchlist": auth_db.get_watchlist_symbols(user["id"]), "observations": auth_db.get_saved_observations(user["id"]), "push_rules": auth_db.get_push_rules(user["id"]), } @router.post("/api/watchlist") async def api_add_watchlist(req: WatchlistRequest, altcoin_session: str = Cookie(default="")): user = require_api_user_with_subscription(altcoin_session) auth_db.add_watchlist_symbol(user["id"], req.symbol) return {"ok": True, "watchlist": auth_db.get_watchlist_symbols(user["id"])} @router.delete("/api/watchlist/{symbol}") async def api_remove_watchlist(symbol: str, altcoin_session: str = Cookie(default="")): user = require_api_user_with_subscription(altcoin_session) auth_db.remove_watchlist_symbol(user["id"], symbol) return {"ok": True, "watchlist": auth_db.get_watchlist_symbols(user["id"])} @router.post("/api/observations") async def api_save_observation(req: ObservationRequest, altcoin_session: str = Cookie(default="")): user = require_api_user_with_subscription(altcoin_session) auth_db.save_observation(user["id"], req.rec_id, req.note) return {"ok": True, "observations": auth_db.get_saved_observations(user["id"])} @router.delete("/api/observations/{rec_id}") async def api_remove_observation(rec_id: int, altcoin_session: str = Cookie(default="")): user = require_api_user_with_subscription(altcoin_session) auth_db.remove_observation(user["id"], rec_id) return {"ok": True, "observations": auth_db.get_saved_observations(user["id"])} @router.post("/api/push-rules") async def api_update_push_rules(req: PushRulesRequest, altcoin_session: str = Cookie(default="")): user = require_api_user_with_subscription(altcoin_session) rules = auth_db.update_push_rules(user["id"], req.dict()) return {"ok": True, "push_rules": rules} @router.get("/api/screening") async def api_screening(hours: int = 24, limit: int = 100, altcoin_session: str = Cookie(default="")): require_api_user_with_subscription(altcoin_session) return get_screening_history(hours, limit) @router.get("/api/review") async def api_review(altcoin_session: str = Cookie(default="")): require_api_user_with_subscription(altcoin_session) return get_review_stats() @router.get("/api/weights") async def api_weights(altcoin_session: str = Cookie(default="")): require_api_user_with_subscription(altcoin_session) return get_signal_weights() @router.get("/api/cron") async def api_cron(limit: int = 50, job_name: str = "", altcoin_session: str = Cookie(default="")): require_api_user_with_subscription(altcoin_session) return get_cron_run_logs(limit=limit, job_name=job_name or None) @router.get("/api/cron/summary") async def api_cron_summary(hours: int = 24, altcoin_session: str = Cookie(default="")): require_api_user_with_subscription(altcoin_session) return get_cron_run_summary(hours=hours) @router.get("/api/pipeline/runs") async def api_pipeline_runs(limit: int = 30, hours: int = 24, altcoin_session: str = Cookie(default="")): require_api_user_with_subscription(altcoin_session) return get_pipeline_runs(limit=limit, hours=hours) @router.get("/api/pipeline/runs/{run_id}") async def api_pipeline_run_detail(run_id: int, altcoin_session: str = Cookie(default="")): require_api_user_with_subscription(altcoin_session) detail = get_pipeline_run_detail(run_id) if not detail: return {"error": "pipeline run not found", "run_id": run_id} return detail