LinkDesk/backend/routers/data_consistency.py

187 lines
6.0 KiB
Python

"""
Data Consistency API endpoints for validating and monitoring task aggregation consistency.
"""
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List, Optional, Dict, Any
from database import get_db
from models.user import User, UserRole
from services.data_consistency import create_data_consistency_service
from utils.auth import get_current_user_from_token, _get_user_from_db
router = APIRouter()
def get_current_user_with_db(
token_data: dict = Depends(get_current_user_from_token),
db: Session = Depends(get_db)
):
"""Get current user with proper database dependency."""
return _get_user_from_db(db, token_data["user_id"])
def require_admin_or_coordinator(
token_data: dict = Depends(get_current_user_from_token),
db: Session = Depends(get_db)
):
"""Require admin or coordinator role for consistency operations."""
current_user = _get_user_from_db(db, token_data["user_id"])
if current_user.role not in [UserRole.COORDINATOR] and not current_user.is_admin:
raise HTTPException(
status_code=403,
detail="Admin or Coordinator role required for consistency operations"
)
return current_user
@router.get("/validate/{entity_type}/{entity_id}")
async def validate_entity_consistency(
entity_type: str,
entity_id: int,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user_with_db)
):
"""
Validate task aggregation consistency for a specific shot or asset.
Args:
entity_type: 'shot' or 'asset'
entity_id: ID of the shot or asset
"""
if entity_type not in ['shot', 'asset']:
raise HTTPException(
status_code=400,
detail="entity_type must be 'shot' or 'asset'"
)
consistency_service = create_data_consistency_service(db)
try:
result = consistency_service.validate_task_aggregation_consistency(entity_id, entity_type)
return result
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=f"Validation failed: {str(e)}")
@router.post("/validate/bulk")
async def validate_bulk_consistency(
entity_ids: List[int],
entity_type: str,
db: Session = Depends(get_db),
current_user: User = Depends(require_admin_or_coordinator)
):
"""
Validate task aggregation consistency for multiple shots or assets.
Args:
entity_ids: List of shot or asset IDs
entity_type: 'shot' or 'asset'
"""
if entity_type not in ['shot', 'asset']:
raise HTTPException(
status_code=400,
detail="entity_type must be 'shot' or 'asset'"
)
if len(entity_ids) > 100:
raise HTTPException(
status_code=400,
detail="Maximum 100 entities can be validated at once"
)
consistency_service = create_data_consistency_service(db)
try:
result = consistency_service.validate_bulk_consistency(entity_ids, entity_type)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=f"Bulk validation failed: {str(e)}")
@router.get("/report")
async def get_consistency_report(
project_id: Optional[int] = Query(None, description="Filter by project ID"),
db: Session = Depends(get_db),
current_user: User = Depends(require_admin_or_coordinator)
):
"""
Generate a comprehensive consistency report for shots and assets.
Args:
project_id: Optional project ID to filter by
"""
consistency_service = create_data_consistency_service(db)
try:
result = consistency_service.get_consistency_report(project_id)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=f"Report generation failed: {str(e)}")
@router.post("/propagate/{task_id}")
async def propagate_task_update(
task_id: int,
old_status: Optional[str] = None,
new_status: Optional[str] = None,
db: Session = Depends(get_db),
current_user: User = Depends(require_admin_or_coordinator)
):
"""
Manually propagate a task update and validate consistency.
Args:
task_id: ID of the task to propagate
old_status: Previous task status (optional)
new_status: New task status (optional)
"""
consistency_service = create_data_consistency_service(db)
try:
result = consistency_service.propagate_task_update(task_id, old_status, new_status)
return result
except Exception as e:
raise HTTPException(status_code=500, detail=f"Task propagation failed: {str(e)}")
@router.get("/health")
async def consistency_health_check(
project_id: Optional[int] = Query(None, description="Filter by project ID"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user_with_db)
):
"""
Quick health check for data consistency across the system.
Args:
project_id: Optional project ID to filter by
"""
consistency_service = create_data_consistency_service(db)
try:
report = consistency_service.get_consistency_report(project_id)
# Extract key health metrics
summary = report['summary']
health_status = "healthy" if summary['consistency_percentage'] >= 95 else "degraded" if summary['consistency_percentage'] >= 80 else "unhealthy"
return {
'status': health_status,
'consistency_percentage': summary['consistency_percentage'],
'total_entities': summary['total_entities'],
'valid_entities': summary['valid_entities'],
'invalid_entities': summary['invalid_entities'],
'total_inconsistencies': summary['total_inconsistencies'],
'project_id': project_id,
'timestamp': report['report_timestamp']
}
except Exception as e:
return {
'status': 'error',
'error': str(e),
'timestamp': None
}