"""Reports API endpoints for background report generation.""" from typing import Optional from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel router = APIRouter() class ReportRequest(BaseModel): """Request model for report generation.""" report_type: str # 'performance', 'trades', 'tax', 'backtest' format: str = "pdf" # 'pdf' or 'csv' year: Optional[int] = None # For tax reports method: Optional[str] = "fifo" # For tax reports: 'fifo', 'lifo' class ExportRequest(BaseModel): """Request model for data export.""" export_type: str # 'orders', 'positions' @router.post("/generate") async def generate_report(request: ReportRequest): """Generate a report in the background. This endpoint queues a report generation task and returns immediately. Use /api/tasks/{task_id} to monitor progress. Supported report types: - performance: Portfolio performance report - trades: Trade history export - tax: Tax report with capital gains calculation Returns: Task ID for monitoring """ try: from src.worker.tasks import generate_report_task params = { "format": request.format, } if request.report_type == "tax": from datetime import datetime params["year"] = request.year or datetime.now().year params["method"] = request.method task = generate_report_task.delay( report_type=request.report_type, params=params ) return { "task_id": task.id, "status": "queued", "report_type": request.report_type, "format": request.format } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.post("/export") async def export_data(request: ExportRequest): """Export data in the background. This endpoint queues a data export task and returns immediately. Use /api/tasks/{task_id} to monitor progress. Supported export types: - orders: Order history - positions: Current/historical positions Returns: Task ID for monitoring """ try: from src.worker.tasks import export_data_task task = export_data_task.delay( export_type=request.export_type, params={} ) return { "task_id": task.id, "status": "queued", "export_type": request.export_type } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/list") async def list_reports(): """List available reports in the reports directory.""" try: from pathlib import Path import os reports_dir = Path(os.path.expanduser("~/.local/share/crypto_trader/reports")) if not reports_dir.exists(): return {"reports": []} reports = [] for f in reports_dir.iterdir(): if f.is_file(): stat = f.stat() reports.append({ "name": f.name, "path": str(f), "size": stat.st_size, "created": stat.st_mtime, "type": f.suffix.lstrip(".") }) # Sort by creation time, newest first reports.sort(key=lambda x: x["created"], reverse=True) return {"reports": reports} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.delete("/{filename}") async def delete_report(filename: str): """Delete a generated report.""" try: from pathlib import Path import os reports_dir = Path(os.path.expanduser("~/.local/share/crypto_trader/reports")) filepath = reports_dir / filename if not filepath.exists(): raise HTTPException(status_code=404, detail="Report not found") # Security check: ensure the file is actually in reports dir if not str(filepath.resolve()).startswith(str(reports_dir.resolve())): raise HTTPException(status_code=403, detail="Access denied") filepath.unlink() return {"status": "deleted", "filename": filename} except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=str(e))