| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- """Utilities for sketch operations, including automatic timestamp updates."""
- from functools import wraps
- from datetime import datetime
- from typing import Callable
- from uuid import UUID
- from fastapi import BackgroundTasks
- from sqlalchemy.orm import Session
- from flowsint_core.core.models import Sketch, Investigation
- def update_sketch_last_modified(db: Session, sketch_id: str | UUID) -> None:
- """
- Update the last_updated_at timestamp for a sketch and its parent investigation.
- This function is designed to be run as a background task to avoid
- blocking the response. It updates both the sketch's and its parent
- investigation's last_updated_at fields to the current time.
- Args:
- db: SQLAlchemy database session
- sketch_id: The ID of the sketch to update
- """
- try:
- sketch = db.query(Sketch).filter(Sketch.id == sketch_id).first()
- if sketch:
- current_time = datetime.now()
- # Update sketch timestamp
- sketch.last_updated_at = current_time
- # Update parent investigation timestamp if it exists
- if sketch.investigation_id:
- investigation = db.query(Investigation).filter(
- Investigation.id == sketch.investigation_id
- ).first()
- if investigation:
- investigation.last_updated_at = current_time
- db.commit()
- except Exception as e:
- # Log error but don't raise to avoid disrupting background task
- print(f"Error updating sketch/investigation timestamp for {sketch_id}: {e}")
- db.rollback()
- def update_sketch_timestamp(func: Callable) -> Callable:
- """
- Decorator to automatically update sketch's last_updated_at timestamp.
- This decorator:
- 1. Extracts the sketch_id from route parameters
- 2. Schedules a background task to update last_updated_at
- 3. Returns the response immediately (non-blocking)
- Usage:
- @router.post("/{sketch_id}/nodes/add")
- @update_sketch_timestamp
- def add_node(
- sketch_id: str,
- node: NodeInput,
- background_tasks: BackgroundTasks,
- db: Session = Depends(get_db),
- ...
- ):
- # Your route logic here
- pass
- Requirements:
- - Route must have a 'sketch_id' parameter (path or query)
- - Route must have 'background_tasks: BackgroundTasks' parameter
- - Route must have 'db: Session' parameter
- """
- @wraps(func)
- async def async_wrapper(*args, **kwargs):
- # Extract required dependencies from kwargs
- sketch_id = kwargs.get("sketch_id")
- background_tasks: BackgroundTasks = kwargs.get("background_tasks")
- db: Session = kwargs.get("db")
- if not sketch_id:
- raise ValueError("sketch_id parameter is required for @update_sketch_timestamp")
- if not background_tasks:
- raise ValueError("background_tasks parameter is required for @update_sketch_timestamp")
- if not db:
- raise ValueError("db parameter is required for @update_sketch_timestamp")
- # Schedule the timestamp update as a background task
- background_tasks.add_task(update_sketch_last_modified, db, sketch_id)
- # Execute the original route function
- return await func(*args, **kwargs)
- @wraps(func)
- def sync_wrapper(*args, **kwargs):
- # Extract required dependencies from kwargs
- sketch_id = kwargs.get("sketch_id")
- background_tasks: BackgroundTasks = kwargs.get("background_tasks")
- db: Session = kwargs.get("db")
- if not sketch_id:
- raise ValueError("sketch_id parameter is required for @update_sketch_timestamp")
- if not background_tasks:
- raise ValueError("background_tasks parameter is required for @update_sketch_timestamp")
- if not db:
- raise ValueError("db parameter is required for @update_sketch_timestamp")
- # Schedule the timestamp update as a background task
- background_tasks.add_task(update_sketch_last_modified, db, sketch_id)
- # Execute the original route function
- return func(*args, **kwargs)
- # Return the appropriate wrapper based on whether the function is async
- import inspect
- if inspect.iscoroutinefunction(func):
- return async_wrapper
- else:
- return sync_wrapper
|