feat(T08): Bildretusche via Flux.1-Pro + Preisvergleich + Retouch UI with before/after slider

This commit is contained in:
2026-07-17 09:31:28 +02:00
parent 5cc9f8e9a9
commit f1d6de0e47
16 changed files with 2278 additions and 30 deletions
+89
View File
@@ -0,0 +1,89 @@
"""SQLAlchemy model for image retouch results."""
import enum
import uuid
from datetime import datetime
from sqlalchemy import DateTime, ForeignKey, String, Text, func
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class RetouchStatus(str, enum.Enum):
pending = "pending"
processing = "processing"
completed = "completed"
failed = "failed"
class RetouchResult(Base):
"""Retouch result entity tracking image retouching via Flux.1-Pro."""
__tablename__ = "retouch_results"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
primary_key=True,
default=uuid.uuid4,
)
vehicle_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True),
ForeignKey("vehicles.id", ondelete="SET NULL"),
nullable=True,
index=True,
)
original_file_path: Mapped[str] = mapped_column(
String(512), nullable=False
)
original_file_name: Mapped[str] = mapped_column(
String(255), nullable=False
)
mime_type: Mapped[str] = mapped_column(
String(100), nullable=False, default="image/png"
)
retouched_file_path: Mapped[str | None] = mapped_column(
String(512), nullable=True
)
status: Mapped[str] = mapped_column(
String(20), nullable=False, default="pending"
)
error_message: Mapped[str | None] = mapped_column(
Text, nullable=True
)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
nullable=False,
server_default=func.now(),
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
nullable=False,
server_default=func.now(),
onupdate=func.now(),
)
vehicle: Mapped["Vehicle"] = relationship("Vehicle")
def __repr__(self) -> str:
return f"<RetouchResult id={self.id} status={self.status}>"
def to_dict(self) -> dict:
"""Serialize retouch result for API responses."""
return {
"id": str(self.id),
"vehicle_id": str(self.vehicle_id) if self.vehicle_id else None,
"original_file_path": self.original_file_path,
"original_file_name": self.original_file_name,
"mime_type": self.mime_type,
"retouched_file_path": self.retouched_file_path,
"status": self.status,
"error_message": self.error_message,
"created_at": (
self.created_at.isoformat() if self.created_at else None
),
"updated_at": (
self.updated_at.isoformat() if self.updated_at else None
),
}