library.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. """Pydantic schemas for library (File Manager) functionality."""
  2. from datetime import datetime
  3. from pydantic import BaseModel, Field
  4. # ============ Folder Schemas ============
  5. class FolderCreate(BaseModel):
  6. """Schema for creating a new folder."""
  7. name: str = Field(..., min_length=1, max_length=255)
  8. parent_id: int | None = None
  9. project_id: int | None = None
  10. archive_id: int | None = None
  11. class ExternalFolderCreate(BaseModel):
  12. """Schema for linking an external folder."""
  13. name: str = Field(..., min_length=1, max_length=255)
  14. external_path: str = Field(..., min_length=1, max_length=500)
  15. readonly: bool = True
  16. show_hidden: bool = False
  17. parent_id: int | None = None
  18. class FolderUpdate(BaseModel):
  19. """Schema for updating a folder."""
  20. name: str | None = Field(None, min_length=1, max_length=255)
  21. parent_id: int | None = None
  22. project_id: int | None = None # 0 to unlink
  23. archive_id: int | None = None # 0 to unlink
  24. class FolderResponse(BaseModel):
  25. """Schema for folder response."""
  26. id: int
  27. name: str
  28. parent_id: int | None
  29. project_id: int | None = None
  30. archive_id: int | None = None
  31. project_name: str | None = None
  32. archive_name: str | None = None
  33. is_external: bool = False
  34. external_path: str | None = None
  35. external_readonly: bool = False
  36. external_show_hidden: bool = False
  37. file_count: int = 0 # Computed field
  38. created_at: datetime
  39. updated_at: datetime
  40. class Config:
  41. from_attributes = True
  42. class FolderTreeItem(BaseModel):
  43. """Schema for folder tree item (includes children)."""
  44. id: int
  45. name: str
  46. parent_id: int | None
  47. project_id: int | None = None
  48. archive_id: int | None = None
  49. project_name: str | None = None
  50. archive_name: str | None = None
  51. is_external: bool = False
  52. external_path: str | None = None
  53. external_readonly: bool = False
  54. file_count: int = 0
  55. children: list["FolderTreeItem"] = []
  56. class Config:
  57. from_attributes = True
  58. # ============ File Schemas ============
  59. class FileCreate(BaseModel):
  60. """Schema for creating a file entry (internal use after upload)."""
  61. filename: str
  62. file_path: str
  63. file_type: str
  64. file_size: int
  65. file_hash: str | None = None
  66. thumbnail_path: str | None = None
  67. metadata: dict | None = None
  68. folder_id: int | None = None
  69. project_id: int | None = None
  70. class FileUpdate(BaseModel):
  71. """Schema for updating a file."""
  72. filename: str | None = Field(None, min_length=1, max_length=255)
  73. folder_id: int | None = None
  74. project_id: int | None = None
  75. notes: str | None = None
  76. class FileDuplicate(BaseModel):
  77. """Reference to a duplicate file."""
  78. id: int
  79. filename: str
  80. folder_id: int | None
  81. folder_name: str | None
  82. created_at: datetime
  83. class FileResponse(BaseModel):
  84. """Schema for file response."""
  85. id: int
  86. folder_id: int | None
  87. folder_name: str | None = None
  88. project_id: int | None
  89. project_name: str | None = None
  90. is_external: bool = False
  91. filename: str
  92. file_path: str
  93. file_type: str
  94. file_size: int
  95. file_hash: str | None
  96. thumbnail_path: str | None
  97. metadata: dict | None
  98. print_count: int
  99. last_printed_at: datetime | None
  100. notes: str | None
  101. # Duplicate detection
  102. duplicates: list[FileDuplicate] | None = None
  103. duplicate_count: int = 0
  104. # User tracking (Issue #206)
  105. created_by_id: int | None = None
  106. created_by_username: str | None = None
  107. created_at: datetime
  108. updated_at: datetime
  109. # Metadata fields
  110. print_name: str | None = None
  111. print_time_seconds: int | None = None
  112. filament_used_grams: float | None = None
  113. sliced_for_model: str | None = None
  114. class Config:
  115. from_attributes = True
  116. class FileListResponse(BaseModel):
  117. """Schema for file list item (lighter than full response)."""
  118. id: int
  119. folder_id: int | None
  120. is_external: bool = False
  121. filename: str
  122. file_type: str
  123. file_size: int
  124. thumbnail_path: str | None
  125. print_count: int
  126. duplicate_count: int = 0
  127. # User tracking (Issue #206)
  128. created_by_id: int | None = None
  129. created_by_username: str | None = None
  130. created_at: datetime
  131. # Key metadata fields for display
  132. print_name: str | None = None
  133. print_time_seconds: int | None = None
  134. filament_used_grams: float | None = None
  135. sliced_for_model: str | None = None
  136. class Config:
  137. from_attributes = True
  138. class FileMoveRequest(BaseModel):
  139. """Schema for moving files to a folder."""
  140. file_ids: list[int]
  141. folder_id: int | None = None # None = move to root
  142. class FilePrintRequest(BaseModel):
  143. """Schema for printing a file from the library.
  144. Note: printer_id is passed as a query parameter, not in the body.
  145. """
  146. # Print options (same as archive reprint)
  147. plate_id: int | None = None
  148. plate_name: str | None = None
  149. ams_mapping: list[int] | None = None
  150. bed_levelling: bool = True
  151. flow_cali: bool = False
  152. vibration_cali: bool = True
  153. layer_inspect: bool = False
  154. timelapse: bool = False
  155. use_ams: bool = True
  156. # Project to associate the resulting archive with
  157. project_id: int | None = None
  158. class FileUploadResponse(BaseModel):
  159. """Schema for file upload response."""
  160. id: int
  161. filename: str
  162. file_type: str
  163. file_size: int
  164. thumbnail_path: str | None
  165. duplicate_of: int | None = None # ID of existing file with same hash
  166. metadata: dict | None = None
  167. # ============ Bulk Operations ============
  168. class BulkDeleteRequest(BaseModel):
  169. """Schema for bulk delete operations."""
  170. file_ids: list[int] = []
  171. folder_ids: list[int] = []
  172. class BulkDeleteResponse(BaseModel):
  173. """Schema for bulk delete response."""
  174. deleted_files: int
  175. deleted_folders: int
  176. # ============ Queue Operations ============
  177. class AddToQueueRequest(BaseModel):
  178. """Schema for adding library files to the print queue."""
  179. file_ids: list[int] = Field(..., min_length=1)
  180. class AddToQueueResult(BaseModel):
  181. """Result for a single file added to queue."""
  182. file_id: int
  183. filename: str
  184. queue_item_id: int
  185. class AddToQueueError(BaseModel):
  186. """Error for a file that couldn't be added to queue."""
  187. file_id: int
  188. filename: str
  189. error: str
  190. class AddToQueueResponse(BaseModel):
  191. """Schema for add-to-queue response."""
  192. added: list[AddToQueueResult]
  193. errors: list[AddToQueueError]
  194. # ============ ZIP Extraction ============
  195. class ZipExtractResult(BaseModel):
  196. """Result for a single file extracted from ZIP."""
  197. filename: str
  198. file_id: int
  199. folder_id: int | None = None
  200. class ZipExtractError(BaseModel):
  201. """Error for a file that couldn't be extracted."""
  202. filename: str
  203. error: str
  204. class ZipExtractResponse(BaseModel):
  205. """Schema for ZIP extraction response."""
  206. extracted: int
  207. folders_created: int
  208. files: list[ZipExtractResult]
  209. errors: list[ZipExtractError]
  210. # ============ STL Thumbnail Generation ============
  211. class BatchThumbnailRequest(BaseModel):
  212. """Schema for batch STL thumbnail generation request."""
  213. file_ids: list[int] | None = None
  214. folder_id: int | None = None
  215. all_missing: bool = False
  216. class BatchThumbnailResult(BaseModel):
  217. """Result for a single file thumbnail generation."""
  218. file_id: int
  219. filename: str
  220. success: bool
  221. error: str | None = None
  222. class BatchThumbnailResponse(BaseModel):
  223. """Schema for batch thumbnail generation response."""
  224. processed: int
  225. succeeded: int
  226. failed: int
  227. results: list[BatchThumbnailResult]