Files
mule-image/backend/app/schemas/photos.py
claudio a27267f7ad refactor: drop AI/vision pipeline + plain Postgres + full-refresh script
Removes the OpenCLIP-on-ONNX classifier and everything that fed or
consumed it:
  - backend: app/services/vision/, app/tasks/vision.py,
    app/services/feature_flags.py, app/routers/features.py — all
    deleted; admin AI/feature-flag endpoints and the worker-vision
    bootstrap call gone. Photo.needs_review and its index dropped.
  - frontend: AI Settings tab, useFeaturesQuery hook, FeatureFlag
    types, "Needs Review" sidebar entry + filter, needs_review filter
    URL param all gone.
  - infra: worker-vision compose service + models_data volume deleted;
    worker-light command no longer runs bootstrap_models; the db
    image switches from pgvector/pgvector:pg16 to postgres:16; backend
    Dockerfile drops the dedicated torch RUN layer; requirements.txt
    drops torch/torchvision/open-clip-torch/onnxruntime.

Alembic 0019_drop_ai_remnants:
  - drops photos.needs_review + ix_photos_needs_review
  - DROP EXTENSION IF EXISTS vector (must run before the image swap;
    the new postgres:16 doesn't ship pgvector)

New scripts/full_refresh.py: one-shot DB ↔ filesystem reconciliation.
Runs cleanup_data_integrity, scans every active SourceRoot inline
(no celery dependency so the worker can be stopped), hard-prunes
photo + folder rows for files that are gone, removes orphan
/data/thumbs/{user}/{photo}/ directories. New helper
prune_orphan_thumbnails in cleanup.py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 00:20:38 +02:00

73 lines
2.3 KiB
Python

"""
Pydantic schemas for photos
"""
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Any
from datetime import datetime
class PhotoBase(BaseModel):
"""Base photo schema"""
filename: str
media_type: str
original_format: Optional[str] = None
width: Optional[int] = None
height: Optional[int] = None
file_size: Optional[int] = None
taken_at: Optional[datetime] = None
taken_at_source: Optional[str] = None
user_title: Optional[str] = None
user_notes: Optional[str] = None
rating: int = 0
color_label: Optional[str] = None
class PhotoResponse(PhotoBase):
"""Photo response schema"""
id: str
filepath: str
folder_id: Optional[str] = None
file_hash: Optional[str] = None
added_at: datetime
updated_at: Optional[datetime] = None
is_discarded: bool = False
discarded_at: Optional[datetime] = None
thumb_small: Optional[str] = None
thumb_medium: Optional[str] = None
thumb_large: Optional[str] = None
processing_status: str = 'pending'
processing_error: Optional[str] = None
exif_json: Optional[str] = None
latitude: Optional[float] = None
longitude: Optional[float] = None
is_duplicate: bool = False
has_date_warning: bool = False
live_photo_video_id: Optional[str] = None
owner_username: Optional[str] = None
# tags: List[Dict[str, Any]] = [] # TODO: Enable when using eager loading
class Config:
orm_mode = True
from_attributes = True
class PhotoUpdate(BaseModel):
"""Photo update schema"""
filename: Optional[str] = None
user_title: Optional[str] = None
user_notes: Optional[str] = None
rating: Optional[int] = Field(None, ge=0, le=5)
color_label: Optional[str] = None
is_discarded: Optional[bool] = None
taken_at: Optional[datetime] = None
class PhotoListResponse(BaseModel):
"""Photo list response with pagination"""
photos: List[PhotoResponse]
total: int
page: int
per_page: int
pages: int
class BulkAction(BaseModel):
"""Bulk action on photos"""
ids: List[str]
action: str # 'discard', 'restore', 'delete_permanent', 'move', 'copy', 'add_tag', 'remove_tag', 'set_rating', 'set_color'
value: Optional[Any] = None # For actions that need a value (rating, color, tag_id, folder_id)