emotion-detection-api / app /validators.py
HimAJ's picture
upload 32 files for the ml
1e4fc28 verified
"""
Request validation utilities.
"""
import os
from typing import Tuple, Optional
from werkzeug.utils import secure_filename
from PIL import Image
def validate_image_file(file, max_size: int, allowed_extensions: tuple) -> Tuple[bool, Optional[str], Optional[str]]:
"""
Validate uploaded image file.
Args:
file: FileStorage object from Flask
max_size: Maximum file size in bytes
allowed_extensions: Tuple of allowed extensions (e.g., (".jpg", ".png"))
Returns:
Tuple of (is_valid, error_message, sanitized_filename)
If valid: (True, None, filename)
If invalid: (False, error_message, None)
"""
if not file or not file.filename:
return False, "No file provided", None
# Check filename
filename = secure_filename(file.filename)
if not filename:
return False, "Invalid filename", None
# Check extension
ext = os.path.splitext(filename)[1].lower()
if ext not in allowed_extensions:
return False, f"Unsupported file type. Allowed: {', '.join(allowed_extensions)}", None
# Check file size (if available)
try:
file.seek(0, os.SEEK_END)
file_size = file.tell()
file.seek(0) # Reset to beginning
if file_size > max_size:
max_mb = max_size / (1024 * 1024)
return False, f"File too large. Maximum size: {max_mb:.1f}MB", None
if file_size == 0:
return False, "File is empty", None
except Exception:
# If we can't check size, continue (will be caught by MAX_CONTENT_LENGTH)
pass
# Validate it's actually an image by trying to open it
try:
file.seek(0)
img = Image.open(file)
img.verify() # Verify it's a valid image
file.seek(0) # Reset after verification
except Exception as e:
return False, f"Invalid image file: {str(e)}", None
return True, None, filename
def validate_pagination_params(limit: Optional[str], offset: Optional[str]) -> Tuple[int, int, Optional[str]]:
"""
Validate pagination parameters.
Returns:
Tuple of (limit, offset, error_message)
"""
try:
limit_val = int(limit) if limit else 20
limit_val = max(1, min(200, limit_val))
except ValueError:
return 20, 0, "Invalid limit parameter. Must be an integer."
try:
offset_val = int(offset) if offset else 0
offset_val = max(0, offset_val)
except ValueError:
return limit_val, 0, "Invalid offset parameter. Must be an integer."
return limit_val, offset_val, None
def validate_confidence_range(min_conf: Optional[str], max_conf: Optional[str]) -> Tuple[Optional[float], Optional[float], Optional[str]]:
"""
Validate confidence range parameters.
Returns:
Tuple of (min_confidence, max_confidence, error_message)
"""
min_val = None
max_val = None
if min_conf:
try:
min_val = float(min_conf)
if not 0 <= min_val <= 1:
return None, None, "min_confidence must be between 0 and 1"
except ValueError:
return None, None, "Invalid min_confidence parameter. Must be a number."
if max_conf:
try:
max_val = float(max_conf)
if not 0 <= max_val <= 1:
return None, None, "max_confidence must be between 0 and 1"
except ValueError:
return None, None, "Invalid max_confidence parameter. Must be a number."
if min_val is not None and max_val is not None and min_val > max_val:
return None, None, "min_confidence cannot be greater than max_confidence"
return min_val, max_val, None