from fastapi import APIRouter, Depends, Header, HTTPException from sqlalchemy.orm import Session from app.database import get_db from app.models.db_models import Device, StockPosition from app.models.schemas import StockPositionCreate, StockPositionResponse router = APIRouter(prefix="/portfolio", tags=["portfolio"]) def _get_device(x_device_token: str = Header(...), db: Session = Depends(get_db)) -> Device: device = db.query(Device).filter(Device.apns_token == x_device_token).first() if not device: raise HTTPException(status_code=404, detail="Device not registered. Call /devices/register first.") return device @router.get("", response_model=list[StockPositionResponse]) def get_portfolio(device: Device = Depends(_get_device), db: Session = Depends(get_db)): return db.query(StockPosition).filter(StockPosition.device_id == device.id).all() @router.post("", response_model=list[StockPositionResponse]) def set_portfolio( positions: list[StockPositionCreate], device: Device = Depends(_get_device), db: Session = Depends(get_db), ): """Full replace — client sends complete list of stock holdings.""" # Delete all existing for this device db.query(StockPosition).filter(StockPosition.device_id == device.id).delete() new_positions = [] for p in positions: sp = StockPosition( device_id=device.id, ticker=p.ticker, shares=p.shares, cost_basis=p.cost_basis, ) db.add(sp) new_positions.append(sp) db.commit() for sp in new_positions: db.refresh(sp) return new_positions @router.delete("/{ticker}", status_code=204) def delete_ticker( ticker: str, device: Device = Depends(_get_device), db: Session = Depends(get_db), ): ticker = ticker.upper() deleted = ( db.query(StockPosition) .filter(StockPosition.device_id == device.id, StockPosition.ticker == ticker) .delete() ) if not deleted: raise HTTPException(status_code=404, detail=f"Ticker {ticker} not in portfolio") db.commit()