Files
bambuddy/backend/app/schemas/library.py
T
lietschaend f95b2acd7d Feature: print files directly from project view (closes #930) (#932)
* feat: print files directly from project view (closes #930)

Show printable files from linked library folders directly in the project
detail page, with Print Now and Add to Queue buttons per file. Removes
the detour through the File Manager for common reprint workflows.
2026-04-10 12:54:27 +02:00

330 lines
7.5 KiB
Python

"""Pydantic schemas for library (File Manager) functionality."""
from datetime import datetime
from pydantic import BaseModel, Field
# ============ Folder Schemas ============
class FolderCreate(BaseModel):
"""Schema for creating a new folder."""
name: str = Field(..., min_length=1, max_length=255)
parent_id: int | None = None
project_id: int | None = None
archive_id: int | None = None
class ExternalFolderCreate(BaseModel):
"""Schema for linking an external folder."""
name: str = Field(..., min_length=1, max_length=255)
external_path: str = Field(..., min_length=1, max_length=500)
readonly: bool = True
show_hidden: bool = False
parent_id: int | None = None
class FolderUpdate(BaseModel):
"""Schema for updating a folder."""
name: str | None = Field(None, min_length=1, max_length=255)
parent_id: int | None = None
project_id: int | None = None # 0 to unlink
archive_id: int | None = None # 0 to unlink
class FolderResponse(BaseModel):
"""Schema for folder response."""
id: int
name: str
parent_id: int | None
project_id: int | None = None
archive_id: int | None = None
project_name: str | None = None
archive_name: str | None = None
is_external: bool = False
external_path: str | None = None
external_readonly: bool = False
external_show_hidden: bool = False
file_count: int = 0 # Computed field
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
class FolderTreeItem(BaseModel):
"""Schema for folder tree item (includes children)."""
id: int
name: str
parent_id: int | None
project_id: int | None = None
archive_id: int | None = None
project_name: str | None = None
archive_name: str | None = None
is_external: bool = False
external_path: str | None = None
external_readonly: bool = False
file_count: int = 0
children: list["FolderTreeItem"] = []
class Config:
from_attributes = True
# ============ File Schemas ============
class FileCreate(BaseModel):
"""Schema for creating a file entry (internal use after upload)."""
filename: str
file_path: str
file_type: str
file_size: int
file_hash: str | None = None
thumbnail_path: str | None = None
metadata: dict | None = None
folder_id: int | None = None
project_id: int | None = None
class FileUpdate(BaseModel):
"""Schema for updating a file."""
filename: str | None = Field(None, min_length=1, max_length=255)
folder_id: int | None = None
project_id: int | None = None
notes: str | None = None
class FileDuplicate(BaseModel):
"""Reference to a duplicate file."""
id: int
filename: str
folder_id: int | None
folder_name: str | None
created_at: datetime
class FileResponse(BaseModel):
"""Schema for file response."""
id: int
folder_id: int | None
folder_name: str | None = None
project_id: int | None
project_name: str | None = None
is_external: bool = False
filename: str
file_path: str
file_type: str
file_size: int
file_hash: str | None
thumbnail_path: str | None
metadata: dict | None
print_count: int
last_printed_at: datetime | None
notes: str | None
# Duplicate detection
duplicates: list[FileDuplicate] | None = None
duplicate_count: int = 0
# User tracking (Issue #206)
created_by_id: int | None = None
created_by_username: str | None = None
created_at: datetime
updated_at: datetime
# Metadata fields
print_name: str | None = None
print_time_seconds: int | None = None
filament_used_grams: float | None = None
sliced_for_model: str | None = None
class Config:
from_attributes = True
class FileListResponse(BaseModel):
"""Schema for file list item (lighter than full response)."""
id: int
folder_id: int | None
is_external: bool = False
filename: str
file_type: str
file_size: int
thumbnail_path: str | None
print_count: int
duplicate_count: int = 0
# User tracking (Issue #206)
created_by_id: int | None = None
created_by_username: str | None = None
created_at: datetime
# Key metadata fields for display
print_name: str | None = None
print_time_seconds: int | None = None
filament_used_grams: float | None = None
sliced_for_model: str | None = None
class Config:
from_attributes = True
class FileMoveRequest(BaseModel):
"""Schema for moving files to a folder."""
file_ids: list[int]
folder_id: int | None = None # None = move to root
class FilePrintRequest(BaseModel):
"""Schema for printing a file from the library.
Note: printer_id is passed as a query parameter, not in the body.
"""
# Print options (same as archive reprint)
plate_id: int | None = None
plate_name: str | None = None
ams_mapping: list[int] | None = None
bed_levelling: bool = True
flow_cali: bool = False
vibration_cali: bool = True
layer_inspect: bool = False
timelapse: bool = False
use_ams: bool = True
# Project to associate the resulting archive with
project_id: int | None = None
class FileUploadResponse(BaseModel):
"""Schema for file upload response."""
id: int
filename: str
file_type: str
file_size: int
thumbnail_path: str | None
duplicate_of: int | None = None # ID of existing file with same hash
metadata: dict | None = None
# ============ Bulk Operations ============
class BulkDeleteRequest(BaseModel):
"""Schema for bulk delete operations."""
file_ids: list[int] = []
folder_ids: list[int] = []
class BulkDeleteResponse(BaseModel):
"""Schema for bulk delete response."""
deleted_files: int
deleted_folders: int
# ============ Queue Operations ============
class AddToQueueRequest(BaseModel):
"""Schema for adding library files to the print queue."""
file_ids: list[int] = Field(..., min_length=1)
class AddToQueueResult(BaseModel):
"""Result for a single file added to queue."""
file_id: int
filename: str
queue_item_id: int
class AddToQueueError(BaseModel):
"""Error for a file that couldn't be added to queue."""
file_id: int
filename: str
error: str
class AddToQueueResponse(BaseModel):
"""Schema for add-to-queue response."""
added: list[AddToQueueResult]
errors: list[AddToQueueError]
# ============ ZIP Extraction ============
class ZipExtractResult(BaseModel):
"""Result for a single file extracted from ZIP."""
filename: str
file_id: int
folder_id: int | None = None
class ZipExtractError(BaseModel):
"""Error for a file that couldn't be extracted."""
filename: str
error: str
class ZipExtractResponse(BaseModel):
"""Schema for ZIP extraction response."""
extracted: int
folders_created: int
files: list[ZipExtractResult]
errors: list[ZipExtractError]
# ============ STL Thumbnail Generation ============
class BatchThumbnailRequest(BaseModel):
"""Schema for batch STL thumbnail generation request."""
file_ids: list[int] | None = None
folder_id: int | None = None
all_missing: bool = False
class BatchThumbnailResult(BaseModel):
"""Result for a single file thumbnail generation."""
file_id: int
filename: str
success: bool
error: str | None = None
class BatchThumbnailResponse(BaseModel):
"""Schema for batch thumbnail generation response."""
processed: int
succeeded: int
failed: int
results: list[BatchThumbnailResult]