from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.db.session import get_db from app.services.user_service import user_service from app.services.yahoo_finance import yahoo_finance_service from pydantic import BaseModel from typing import List, Optional from app.core.auth import get_current_user, get_authenticated_user, get_user_or_demo from app.models.database import User as DBUser router = APIRouter() class WatchlistItemSchema(BaseModel): symbol: str name: str exchange: str class WatchlistEnrichedSchema(WatchlistItemSchema): price: str change: str percentage: str isPositive: bool class UserProfileSchema(BaseModel): username: str full_name: str avatar_url: Optional[str] theme: Optional[str] = "dark" class ThemeUpdateSchema(BaseModel): theme: str class IndicatorsUpdateSchema(BaseModel): indicators: List[str] @router.get("/profile", response_model=UserProfileSchema) async def get_profile(user: DBUser = Depends(get_user_or_demo)): return { "username": user.username or user.email.split('@')[0], "full_name": user.full_name, "avatar_url": user.avatar_url, "theme": user.theme or "dark" } @router.put("/profile/theme") async def update_theme(data: ThemeUpdateSchema, db: AsyncSession = Depends(get_db), user: DBUser = Depends(get_authenticated_user)): user.theme = data.theme await db.commit() return {"status": "success", "theme": user.theme} @router.get("/watchlist", response_model=List[WatchlistEnrichedSchema]) async def get_watchlist(db: AsyncSession = Depends(get_db), user: DBUser = Depends(get_user_or_demo)): items = await user_service.get_watchlist(db, user.id) if not items: return [] # Fetch live data for these symbols symbols = [item.symbol for item in items] quotes = await yahoo_finance_service.get_quotes(symbols) quote_map = {q.symbol: q for q in quotes} results = [] for item in items: quote = quote_map.get(item.symbol) results.append({ "symbol": item.symbol, "name": item.name, "exchange": item.exchange, "price": quote.price if quote else "0.00", "change": quote.change if quote else "0.00", "percentage": quote.percentage if quote else "0.00%", "isPositive": quote.isPositive if quote else True }) return results @router.post("/watchlist") async def add_to_watchlist(item: WatchlistItemSchema, db: AsyncSession = Depends(get_db), user: DBUser = Depends(get_authenticated_user)): await user_service.add_to_watchlist( db, user.id, item.symbol, item.name, item.exchange ) return {"status": "success"} @router.delete("/watchlist/{symbol}") async def remove_from_watchlist(symbol: str, db: AsyncSession = Depends(get_db), user: DBUser = Depends(get_authenticated_user)): await user_service.remove_from_watchlist(db, user.id, symbol) return {"status": "success"} @router.get("/indicators", response_model=List[str]) async def get_indicators(db: AsyncSession = Depends(get_db), user: DBUser = Depends(get_user_or_demo)): return await user_service.get_indicators(db, user.id) @router.post("/indicators") async def update_indicators(data: IndicatorsUpdateSchema, db: AsyncSession = Depends(get_db), user: DBUser = Depends(get_authenticated_user)): await user_service.update_indicators(db, user.id, data.indicators) return {"status": "success"} from app.models.database import UserAgenticScan from sqlalchemy import select, desc class AgenticScanSchema(BaseModel): id: int symbol: str decision: str confidence: float recommendation: str created_at: str risk_warning: Optional[str] = None # We might want entry_price too if available entry_price: Optional[str] = None @router.get("/scans", response_model=List[AgenticScanSchema]) async def get_scans(db: AsyncSession = Depends(get_db), user: DBUser = Depends(get_user_or_demo)): result = await db.execute( select(UserAgenticScan) .where(UserAgenticScan.user_id == user.id) .order_by(desc(UserAgenticScan.created_at)) ) scans = result.scalars().all() return [ AgenticScanSchema( id=s.id, symbol=s.symbol, decision=s.decision, confidence=s.confidence, recommendation=s.recommendation, created_at=s.created_at.isoformat(), risk_warning=s.risk_warning, entry_price=s.entry_price ) for s in scans ]