feat: Add date validation for photo import process

This commit introduces a new function, `validate_date_taken`, to ensure that the date taken for photos is valid before being saved. The function checks for valid date objects, prevents future dates, and filters out dates that are too old. The photo import process is updated to utilize this validation, enhancing data integrity and preventing corrupted date data from being stored. Additionally, the `date_added` field is explicitly set to the current UTC time to ensure validity.
This commit is contained in:
Tanya 2026-01-02 14:46:57 -05:00
parent e624d203d5
commit 03d3a28b21

View File

@ -6,7 +6,7 @@ import hashlib
import os
from pathlib import Path
from datetime import datetime, date
from typing import Callable, Optional, Tuple
from typing import Callable, Optional, Tuple, Union
from PIL import Image
from sqlalchemy.orm import Session
@ -15,6 +15,45 @@ from backend.config import SUPPORTED_IMAGE_FORMATS, SUPPORTED_VIDEO_FORMATS
from backend.db.models import Photo
def validate_date_taken(date_taken: Optional[Union[date, datetime]]) -> Optional[date]:
"""Validate and normalize date_taken value.
Ensures the date is:
- A valid date object (not datetime, not invalid type)
- Not in the future
- Not too old (before 1900)
Returns:
Valid date object or None if invalid
"""
if date_taken is None:
return None
try:
# Convert datetime to date if needed
if isinstance(date_taken, datetime):
date_taken = date_taken.date()
elif not isinstance(date_taken, date):
# Invalid type
return None
# Validate date is reasonable
if date_taken > date.today():
# Date in the future is invalid
return None
elif date_taken < date(1900, 1, 1):
# Date too old is likely invalid
return None
return date_taken
except (ValueError, TypeError, AttributeError) as e:
# If any validation fails, return None
import logging
logger = logging.getLogger(__name__)
logger.warning(f"Invalid date_taken value: {date_taken}, error: {e}")
return None
def extract_exif_date(image_path: str) -> Optional[date]:
"""Extract date taken from photo EXIF data - returns Date (not DateTime) to match desktop schema.
@ -299,6 +338,8 @@ def import_photo_from_path(
date_taken = extract_video_date(photo_path)
else:
date_taken = extract_photo_date(photo_path)
# Validate date_taken before setting
date_taken = validate_date_taken(date_taken)
if date_taken:
existing.date_taken = date_taken
db.commit()
@ -319,6 +360,8 @@ def import_photo_from_path(
date_taken = extract_video_date(photo_path)
else:
date_taken = extract_photo_date(photo_path)
# Validate date_taken before setting
date_taken = validate_date_taken(date_taken)
if date_taken:
existing_by_path.date_taken = date_taken
db.commit()
@ -331,15 +374,23 @@ def import_photo_from_path(
else:
date_taken = extract_photo_date(photo_path)
# Validate date_taken - ensure it's a valid date object or None
# This prevents corrupted date data from being saved
date_taken = validate_date_taken(date_taken)
# For videos, mark as processed immediately (we don't process videos for faces)
# For images, start as unprocessed
processed = media_type == "video"
# Explicitly set date_added to current UTC time to ensure it's always valid
date_added = datetime.utcnow()
# Create new photo record with file_hash and media_type
photo = Photo(
path=photo_path,
filename=filename,
date_taken=date_taken,
date_added=date_added, # Explicitly set to ensure valid DateTime
date_taken=date_taken, # Validated date or None
processed=processed,
file_hash=file_hash,
media_type=media_type,