|
|
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 []
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
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
|
|
|
]
|
|
|
|