mirror of
https://github.com/reconurge/flowsint.git
synced 2026-05-01 19:58:59 -05:00
120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
"""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
|