from fastapi import APIRouter, Depends, HTTPException, status, Response, BackgroundTasks from sqlalchemy.orm import Session from typing import List, Optional import logging from datetime import datetime from core.database import get_db, SessionLocal from models import db_models from models.schemas import CanvasCreateRequest, CanvasEditRequest, CanvasResponse from services.canvas_service import canvas_service from api.auth import get_current_user from api.websocket_routes import manager router = APIRouter(prefix="/api/canvas", tags=["Canvas - Collaborative Editing"]) logger = logging.getLogger(__name__) async def run_canvas_generation(canvas_id: int, request: CanvasCreateRequest, user_id: int): """Background task for canvas summary generation""" db = SessionLocal() connection_id = f"user_{user_id}" try: db_canvas = db.query(db_models.Canvas).filter(db_models.Canvas.id == canvas_id).first() if not db_canvas: return # Call AI service content = await canvas_service.generate_canvas_summary( file_key=request.file_key, text_input=request.text_input ) if not content: raise Exception("AI failed to generate canvas content") db_canvas.text = content db_canvas.status = "completed" db.commit() # Notify via WebSocket await manager.send_result(connection_id, { "type": "canvas", "id": db_canvas.id, "status": "completed", "title": db_canvas.title }) except Exception as e: logger.error(f"Background canvas generation failed: {e}") db_canvas = db.query(db_models.Canvas).filter(db_models.Canvas.id == canvas_id).first() if db_canvas: db_canvas.status = "failed" db_canvas.error_message = str(e) db.commit() await manager.send_error(connection_id, f"Canvas generation failed: {str(e)}") finally: db.close() @router.post("/create", response_model=CanvasResponse) async def create_canvas( request: CanvasCreateRequest, background_tasks: BackgroundTasks, current_user: db_models.User = Depends(get_current_user), db: Session = Depends(get_db) ): """ Initiates Canvas creation in the background. """ source_id = None if request.file_key: source = db.query(db_models.Source).filter( db_models.Source.s3_key == request.file_key, db_models.Source.user_id == current_user.id ).first() if not source: raise HTTPException(status_code=403, detail="Not authorized to access this file") source_id = source.id # Create initial record file_base = request.file_key.split('/')[-1].rsplit('.', 1)[0] if request.file_key else None # Priority: 1. File-based name, 2. User Title (if not default 'string'), 3. Default timestamp if file_base: title = f"Canvas-{file_base}" elif request.title and request.title != "string": title = request.title else: title = f"Canvas {datetime.now().strftime('%Y-%m-%d %H:%M')}" db_canvas = db_models.Canvas( title=title, user_id=current_user.id, source_id=source_id, status="processing" ) db.add(db_canvas) db.commit() db.refresh(db_canvas) # Offload to background task background_tasks.add_task(run_canvas_generation, db_canvas.id, request, current_user.id) return db_canvas @router.get("/", response_model=List[CanvasResponse]) async def list_canvases( current_user: db_models.User = Depends(get_current_user), db: Session = Depends(get_db) ): """List all canvases for the current user.""" return db.query(db_models.Canvas).filter(db_models.Canvas.user_id == current_user.id).all() @router.get("/{canvas_id}", response_model=CanvasResponse) async def get_canvas( canvas_id: int, current_user: db_models.User = Depends(get_current_user), db: Session = Depends(get_db) ): """Get a specific canvas by ID.""" canvas = db.query(db_models.Canvas).filter( db_models.Canvas.id == canvas_id, db_models.Canvas.user_id == current_user.id ).first() if not canvas: raise HTTPException(status_code=404, detail="Canvas not found") return canvas @router.put("/{canvas_id}", response_model=CanvasResponse) async def update_canvas_text( canvas_id: int, request: CanvasEditRequest, current_user: db_models.User = Depends(get_current_user), db: Session = Depends(get_db) ): """ Manually update the text of an existing canvas. """ db_canvas = db.query(db_models.Canvas).filter( db_models.Canvas.id == canvas_id, db_models.Canvas.user_id == current_user.id ).first() if not db_canvas: raise HTTPException(status_code=404, detail="Canvas not found") try: db_canvas.text = request.text db_canvas.status = "completed" db.commit() db.refresh(db_canvas) return db_canvas except Exception as e: logger.error(f"Error updating canvas: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.delete("/{canvas_id}") async def delete_canvas( canvas_id: int, current_user: db_models.User = Depends(get_current_user), db: Session = Depends(get_db) ): """Delete a canvas.""" db_canvas = db.query(db_models.Canvas).filter( db_models.Canvas.id == canvas_id, db_models.Canvas.user_id == current_user.id ).first() if not db_canvas: raise HTTPException(status_code=404, detail="Canvas not found") db.delete(db_canvas) db.commit() return {"message": "Canvas deleted successfully"}