| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- from datetime import datetime
- from typing import Annotated, Literal
- from pydantic import BaseModel, PlainSerializer
- # Custom serializer to ensure UTC datetimes have Z suffix
- def serialize_utc_datetime(dt: datetime | None) -> str | None:
- if dt is None:
- return None
- # Add Z suffix to indicate UTC
- return dt.isoformat() + "Z"
- UTCDatetime = Annotated[datetime | None, PlainSerializer(serialize_utc_datetime)]
- class PrintQueueItemCreate(BaseModel):
- printer_id: int | None = None # None = unassigned, user assigns later
- target_model: str | None = None # Target printer model (mutually exclusive with printer_id)
- target_location: str | None = None # Target location filter (only used with target_model)
- required_filament_types: list[str] | None = None # Required filament types for model-based assignment
- filament_overrides: list[dict] | None = None # Filament overrides for model-based assignment
- # Either archive_id OR library_file_id must be provided
- archive_id: int | None = None
- library_file_id: int | None = None
- scheduled_time: datetime | None = None # None = ASAP (next when idle)
- require_previous_success: bool = False
- auto_off_after: bool = False # Power off printer after print completes
- manual_start: bool = False # Requires manual trigger to start (staged)
- # AMS mapping: list of global tray IDs for each filament slot
- # Format: [5, -1, 2, -1] where position = slot_id-1, value = global tray ID (-1 = unused)
- ams_mapping: list[int] | None = None
- # Plate ID for multi-plate 3MF files (1-indexed, None = auto-detect/plate 1)
- plate_id: int | None = None
- # Print options
- bed_levelling: bool = True
- flow_cali: bool = False
- vibration_cali: bool = True
- layer_inspect: bool = False
- timelapse: bool = False
- use_ams: bool = True
- # Auto-print G-code injection
- gcode_injection: bool = False
- # Batch: create multiple copies (creates a batch if > 1)
- quantity: int = 1
- # Project to associate the resulting archive with
- project_id: int | None = None
- class PrintQueueItemUpdate(BaseModel):
- printer_id: int | None = None
- target_model: str | None = None # Target printer model (mutually exclusive with printer_id)
- target_location: str | None = None # Target location filter (only used with target_model)
- filament_overrides: list[dict] | None = None # Filament overrides for model-based assignment
- position: int | None = None
- scheduled_time: datetime | None = None
- require_previous_success: bool | None = None
- auto_off_after: bool | None = None
- manual_start: bool | None = None
- ams_mapping: list[int] | None = None
- plate_id: int | None = None
- # Print options
- bed_levelling: bool | None = None
- flow_cali: bool | None = None
- vibration_cali: bool | None = None
- layer_inspect: bool | None = None
- timelapse: bool | None = None
- use_ams: bool | None = None
- # Auto-print G-code injection
- gcode_injection: bool | None = None
- class PrintQueueItemResponse(BaseModel):
- id: int
- printer_id: int | None # None = unassigned
- target_model: str | None = None # Target printer model for model-based assignment
- target_location: str | None = None # Target location filter for model-based assignment
- required_filament_types: list[str] | None = None # Required filament types for model-based assignment
- filament_overrides: list[dict] | None = None # Filament overrides for model-based assignment
- waiting_reason: str | None = None # Why a model-based job hasn't started yet
- archive_id: int | None # None if library_file_id is set (archive created at print start)
- library_file_id: int | None # For queue items from library files
- position: int
- scheduled_time: UTCDatetime
- require_previous_success: bool
- auto_off_after: bool
- manual_start: bool
- # True when the dispatch scheduler last evaluated this item and the
- # assigned spool could not satisfy at least one slot's required grams
- # (#1496). Display-only — the ▶ click recomputes deficit against live
- # spool state.
- filament_short: bool = False
- ams_mapping: list[int] | None = None
- plate_id: int | None = None # Plate ID for multi-plate 3MF files
- # Print options
- bed_levelling: bool = True
- flow_cali: bool = False
- vibration_cali: bool = True
- layer_inspect: bool = False
- timelapse: bool = False
- use_ams: bool = True
- status: Literal["pending", "printing", "completed", "failed", "skipped", "cancelled"]
- started_at: UTCDatetime
- completed_at: UTCDatetime
- error_message: str | None
- created_at: UTCDatetime
- # Nested info for UI (populated in route)
- archive_name: str | None = None
- archive_thumbnail: str | None = None
- # True when the linked archive has been soft-deleted (its files are gone
- # from disk). In that case the *archive_name* / *archive_thumbnail* /
- # downstream metadata fields are intentionally left None so the frontend
- # doesn't 404-storm the now-missing thumbnail / plates / plate-thumbnail
- # endpoints (#1348 follow-up). Frontends can render a "source deleted"
- # badge based on this flag.
- archive_deleted: bool = False
- library_file_name: str | None = None # Name of library file (if library_file_id is set)
- library_file_thumbnail: str | None = None # Thumbnail of library file
- printer_name: str | None = None
- print_time_seconds: int | None = None # Estimated print time from archive or library file
- filament_used_grams: float | None = None # Estimated print weight from archive or library file
- filament_type: str | None = None # e.g. "PLA", "PETG" (from archive/library file)
- filament_color: str | None = None # e.g. "#FFFFFF" (from archive/library file)
- layer_height: float | None = None # e.g. 0.2 (from archive/library file)
- nozzle_diameter: float | None = None # e.g. 0.4 (from archive/library file)
- sliced_for_model: str | None = None # e.g. "P1S" (from archive/library file)
- # User tracking (Issue #206)
- created_by_id: int | None = None
- created_by_username: str | None = None
- # Batch grouping
- batch_id: int | None = None
- batch_name: str | None = None
- # Shortest-job-first scheduling
- been_jumped: bool = False
- # Auto-print G-code injection
- gcode_injection: bool = False
- class Config:
- from_attributes = True
- class PrintQueueReorderItem(BaseModel):
- id: int
- position: int
- class PrintQueueReorder(BaseModel):
- items: list[PrintQueueReorderItem]
- class PrintQueueBulkUpdate(BaseModel):
- """Bulk update multiple queue items with the same values."""
- item_ids: list[int]
- # Fields to update (all optional - only set fields are applied)
- printer_id: int | None = None
- scheduled_time: datetime | None = None
- require_previous_success: bool | None = None
- auto_off_after: bool | None = None
- manual_start: bool | None = None
- # Print options
- bed_levelling: bool | None = None
- flow_cali: bool | None = None
- vibration_cali: bool | None = None
- layer_inspect: bool | None = None
- timelapse: bool | None = None
- use_ams: bool | None = None
- # Auto-print G-code injection
- gcode_injection: bool | None = None
- class PrintQueueBulkUpdateResponse(BaseModel):
- """Response for bulk update operation."""
- updated_count: int
- skipped_count: int # Items that were not pending
- message: str
- class PrintBatchResponse(BaseModel):
- """Response for a print batch with progress stats."""
- id: int
- name: str
- archive_id: int | None = None
- library_file_id: int | None = None
- quantity: int
- status: str
- created_at: UTCDatetime
- created_by_id: int | None = None
- created_by_username: str | None = None
- # Derived counts
- pending_count: int = 0
- printing_count: int = 0
- completed_count: int = 0
- failed_count: int = 0
- cancelled_count: int = 0
- class Config:
- from_attributes = True
|