Files
tfm_ainventory/alembic/versions/add_photo_fields.py
Daniel Bedeleanu ea49cd6e4a feat(phase1): add image storage utilities
- Create backend/services/image_storage.py with 4 core functions:
  - sanitize_filename(): remove unsafe chars, limit to 255 chars, convert to lowercase
  - get_unique_filename(): handle collisions with UUID suffix (format: {name}_{uuid8}_{variant}.jpg)
  - ensure_image_directories(): create /images/ root and category subdirs on startup
  - save_image(): save bytes to /images/{category}/{filename}, returns relative path
- Create comprehensive test suite (22 tests) covering all functionality
- Integrate ensure_image_directories() into FastAPI startup event
- Directory structure: /images/{category}/{filename}
- Collision handling: auto-suffix with UUID if filename exists
- All tests passing, pathlib.Path for safe operations
2026-04-20 21:57:26 +03:00

64 lines
2.2 KiB
Python

"""Add photo fields to Item and Box models.
Revision ID: 001_add_photo_fields
Revises: None
Create Date: 2026-04-20
This migration adds three photo-related columns to both Item and Box tables:
- photo_path: String, nullable (original photo filename/path)
- photo_thumbnail_path: String, nullable (thumbnail photo filename/path)
- photo_upload_date: DateTime, nullable (when photo was uploaded)
These fields support the Phase 1 Image System implementation.
All fields are nullable to maintain backward compatibility with existing items/boxes.
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '001_add_photo_fields'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
"""Add photo fields to items table."""
# Add photo_path column to items table
op.add_column('items', sa.Column('photo_path', sa.String(), nullable=True))
# Add photo_thumbnail_path column to items table
op.add_column('items', sa.Column('photo_thumbnail_path', sa.String(), nullable=True))
# Add photo_upload_date column to items table
op.add_column('items', sa.Column('photo_upload_date', sa.DateTime(), nullable=True))
# Create boxes table with photo fields
op.create_table(
'boxes',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('box_label', sa.String(), nullable=False),
sa.Column('description', sa.String(), nullable=True),
sa.Column('photo_path', sa.String(), nullable=True),
sa.Column('photo_thumbnail_path', sa.String(), nullable=True),
sa.Column('photo_upload_date', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('box_label', name='unique_box_label')
)
# Create index on box_label for fast lookups
op.create_index('ix_boxes_box_label', 'boxes', ['box_label'], unique=True)
def downgrade():
"""Remove photo fields from items table and drop boxes table."""
# Drop boxes table
op.drop_table('boxes')
# Remove photo columns from items table
op.drop_column('items', 'photo_upload_date')
op.drop_column('items', 'photo_thumbnail_path')
op.drop_column('items', 'photo_path')