slicer_pipeline.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. """Pydantic schemas for the Slicer Pipeline API (#1425, PR A).
  2. A pipeline bundles printer / process / filament(s) / bed-type picks under a
  3. reusable name. PR A surfaces only the bundle; target_kind / target_printer_id /
  4. target_model_class / fanout_strategy are persisted but the API treats them as
  5. opaque defaults — they come alive in PR B (single-target dispatch) and PR C
  6. (multi-copy + class targeting + fanout).
  7. """
  8. from datetime import datetime
  9. from typing import Literal
  10. from pydantic import BaseModel, Field
  11. from backend.app.schemas.slicer import PresetRef
  12. class SlicerPipelineBase(BaseModel):
  13. """Fields editable on create + update."""
  14. name: str = Field(..., min_length=1, max_length=200)
  15. description: str | None = Field(default=None, max_length=1000)
  16. printer_preset: PresetRef
  17. process_preset: PresetRef
  18. filament_presets: list[PresetRef] = Field(
  19. ...,
  20. min_length=1,
  21. description="One PresetRef per AMS slot. Order matches the source plate's filament-slot order.",
  22. )
  23. bed_type: str | None = Field(default=None, max_length=64)
  24. class SlicerPipelineCreate(SlicerPipelineBase):
  25. """Payload for POST /slicer-pipelines."""
  26. class SlicerPipelineUpdate(BaseModel):
  27. """Payload for PUT /slicer-pipelines/{id}. All fields optional; only those
  28. present are written. Preset and filament list are replaced wholesale when
  29. set (we don't support partial filament-slot edits)."""
  30. name: str | None = Field(default=None, min_length=1, max_length=200)
  31. description: str | None = Field(default=None, max_length=1000)
  32. printer_preset: PresetRef | None = None
  33. process_preset: PresetRef | None = None
  34. filament_presets: list[PresetRef] | None = Field(default=None, min_length=1)
  35. bed_type: str | None = Field(default=None, max_length=64)
  36. # PR B target binding. ``target_kind='specific_printer'`` requires
  37. # ``target_printer_id`` to be set OR cleared in the same payload (route
  38. # handler enforces). ``target_kind='printer_class'`` is wired by PR C
  39. # together with ``target_model_class`` (a Bambu model code like 'X1C')
  40. # and the fanout strategy that distributes copies across matching
  41. # printers.
  42. target_kind: Literal["specific_printer", "printer_class"] | None = None
  43. target_printer_id: int | None = None
  44. target_model_class: str | None = Field(default=None, max_length=20)
  45. fanout_strategy: Literal["max_parallel", "fill_one_first", "round_robin"] | None = None
  46. class SlicerPipelineResponse(SlicerPipelineBase):
  47. """A single pipeline as returned by the API."""
  48. id: int
  49. created_by: int | None
  50. created_at: datetime
  51. updated_at: datetime
  52. # Echoed for PR B+ readiness; PR A always returns the persisted defaults.
  53. target_kind: Literal["specific_printer", "printer_class"] = "printer_class"
  54. target_printer_id: int | None = None
  55. target_model_class: str | None = None
  56. fanout_strategy: Literal["max_parallel", "fill_one_first", "round_robin"] = "max_parallel"
  57. model_config = {"from_attributes": True}
  58. class SlicerPipelineListResponse(BaseModel):
  59. """Wraps the list so the response stays additive when run/job counts get
  60. surfaced in PR B+ (e.g. a ``meta`` field for last-run timestamps)."""
  61. pipelines: list[SlicerPipelineResponse] = []