ira/sim-search-api/app/api/routes/report.py

295 lines
8.4 KiB
Python

"""
Report routes for the sim-search API.
This module defines the routes for report generation and management.
"""
import os
from typing import Any, List, Optional
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session
from app.api.dependencies import get_current_active_user
from app.db.models import User, Report, Search
from app.db.session import get_db
from app.schemas.report import (
ReportCreate, ReportUpdate, Report as ReportSchema,
ReportList, ReportProgress, ReportDownload
)
from app.services.report_service import ReportService
router = APIRouter()
report_service = ReportService()
# Dictionary to store report generation progress
report_progress = {}
@router.post("/generate", response_model=ReportSchema)
async def generate_report(
report_in: ReportCreate,
background_tasks: BackgroundTasks,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
) -> Any:
"""
Generate a report from search results.
Args:
report_in: Report creation parameters
background_tasks: FastAPI background tasks
current_user: Current authenticated user
db: Database session
Returns:
Generated report
"""
try:
# Check if search_id is provided and exists
search = None
if report_in.search_id:
search = db.query(Search).filter(
Search.id == report_in.search_id,
Search.user_id == current_user.id
).first()
if not search:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Search not found",
)
# Create report record
title = report_in.title or f"Report: {report_in.query}"
report = Report(
user_id=current_user.id,
search_id=report_in.search_id,
title=title,
content="Report generation in progress...",
detail_level=report_in.detail_level or "standard",
query_type=report_in.query_type,
model_used=report_in.model,
)
db.add(report)
db.commit()
db.refresh(report)
# Initialize progress tracking
report_progress[report.id] = {
"progress": 0.0,
"status": "Initializing report generation...",
"current_chunk": 0,
"total_chunks": 0,
"current_report": "Report generation in progress...",
}
# Generate report in background
background_tasks.add_task(
report_service.generate_report_background,
report_id=report.id,
report_in=report_in,
search=search,
db=db,
progress_dict=report_progress,
)
return report
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error generating report: {str(e)}",
)
@router.get("/list", response_model=ReportList)
async def list_reports(
skip: int = 0,
limit: int = 100,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
) -> Any:
"""
Get a list of user's reports.
Args:
skip: Number of records to skip
limit: Maximum number of records to return
current_user: Current authenticated user
db: Database session
Returns:
List of reports
"""
reports = db.query(Report).filter(Report.user_id == current_user.id).order_by(
Report.created_at.desc()
).offset(skip).limit(limit).all()
total = db.query(Report).filter(Report.user_id == current_user.id).count()
return {"reports": reports, "total": total}
@router.get("/{report_id}", response_model=ReportSchema)
async def get_report(
report_id: str,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
) -> Any:
"""
Get a specific report.
Args:
report_id: ID of the report
current_user: Current authenticated user
db: Database session
Returns:
Report
Raises:
HTTPException: If the report is not found or doesn't belong to the user
"""
report = db.query(Report).filter(
Report.id == report_id, Report.user_id == current_user.id
).first()
if not report:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Report not found",
)
return report
@router.delete("/{report_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_report(
report_id: str,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
) -> None:
"""
Delete a report.
Args:
report_id: ID of the report to delete
current_user: Current authenticated user
db: Database session
Raises:
HTTPException: If the report is not found or doesn't belong to the user
"""
report = db.query(Report).filter(
Report.id == report_id, Report.user_id == current_user.id
).first()
if not report:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Report not found",
)
db.delete(report)
db.commit()
@router.get("/{report_id}/progress", response_model=ReportProgress)
async def get_report_progress(
report_id: str,
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
) -> Any:
"""
Get the progress of a report generation.
Args:
report_id: ID of the report
current_user: Current authenticated user
db: Database session
Returns:
Report generation progress
Raises:
HTTPException: If the report is not found or doesn't belong to the user
"""
# Check if report exists and belongs to user
report = db.query(Report).filter(
Report.id == report_id, Report.user_id == current_user.id
).first()
if not report:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Report not found",
)
# Get progress from progress dictionary
progress_data = report_progress.get(report_id, {
"progress": 1.0,
"status": "Report generation complete",
"current_chunk": 0,
"total_chunks": 0,
"current_report": None,
})
return {
"report_id": report_id,
"progress": progress_data.get("progress", 1.0),
"status": progress_data.get("status", "Report generation complete"),
"current_chunk": progress_data.get("current_chunk", 0),
"total_chunks": progress_data.get("total_chunks", 0),
"current_report": progress_data.get("current_report", None),
}
@router.get("/{report_id}/download")
async def download_report(
report_id: str,
format: str = "markdown",
current_user: User = Depends(get_current_active_user),
db: Session = Depends(get_db),
) -> Any:
"""
Download a report in the specified format.
Args:
report_id: ID of the report
format: Format of the report (markdown, html, pdf)
current_user: Current authenticated user
db: Database session
Returns:
Report file
Raises:
HTTPException: If the report is not found or doesn't belong to the user
"""
report = db.query(Report).filter(
Report.id == report_id, Report.user_id == current_user.id
).first()
if not report:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Report not found",
)
# Generate file in the requested format
try:
file_path = await report_service.generate_report_file(report, format)
# Return file
return FileResponse(
path=file_path,
filename=f"report_{report_id}.{format}",
media_type="application/octet-stream",
)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error generating report file: {str(e)}",
)