"""Pydantic schemas for the unified slicer-presets endpoint. The SliceModal pulls printer/process/filament options from three sources, in priority order: cloud (the user's Bambu Cloud account), local (DB-backed imported profiles), and standard (slicer-bundled stock profiles). The endpoint returns all three lists with name-based dedup applied so each preset appears exactly once across the response. """ from typing import Literal from pydantic import BaseModel CloudStatus = Literal["ok", "not_authenticated", "expired", "unreachable"] class UnifiedPreset(BaseModel): """A single printer/process/filament preset with its source. The ``id`` shape varies by source: - cloud → Bambu Cloud setting_id (e.g. ``"PFUS9ac902733670a9"``) - local → stringified DB row id from ``local_presets`` - standard → preset name as written in the bundled JSON (the slicer resolves bundled profiles by name during inheritance walk) The frontend treats ``id`` as opaque; the slice dispatch path uses ``(source, id)`` to fetch / pass the preset content to the sidecar. ``filament_type`` and ``filament_colour`` are populated for the filament slot only — they let the SliceModal pre-pick a preset per plate slot in the multi-color flow by matching against the source 3MF's per-slot type and color. Populated when the underlying preset JSON exposes them; left as ``None`` on bundled profiles where colour is a runtime spool attribute. ``compatible_printers`` is the slicer's own list of printer-preset names a process / filament preset declares itself valid for. Populated for the local tier (stored at import time); left ``None`` for cloud (no per-preset detail is fetched — rate limits) and standard (the sidecar's bundled listing doesn't expose it). The SliceModal uses it to filter the process / filament dropdowns by the selected printer (#1325); when it is ``None`` the modal falls back to matching the preset name against the ``@BBL `` printer-model registry. ``inherits`` is the preset a printer preset was saved from, for the local and OrcaSlicer Cloud tiers. A printer preset saved under a name of its own ("Bambu Lab H2D 0.4 nozzle - Skew") is listed in no process's ``compatible_printers`` and its name says nothing reliable about the model, so the SliceModal matches against this parent as well (#3250). """ id: str name: str source: Literal["orca_cloud", "cloud", "local", "standard"] filament_type: str | None = None filament_colour: str | None = None compatible_printers: list[str] | None = None inherits: str | None = None class UnifiedPresetsBySlot(BaseModel): """Three slots in the order Bambu Studio / OrcaSlicer use.""" printer: list[UnifiedPreset] = [] process: list[UnifiedPreset] = [] filament: list[UnifiedPreset] = [] class UnifiedPresetsResponse(BaseModel): """Every tier carries its full preset list — no cross-tier dedup. Priority order: ``local > orca_cloud > cloud > standard``. The order drives auto-pick (first non-empty tier wins, name-lookup walks tiers in this order, filament scoring tiebreaks by per-tier bonus) and determines the visual rendering order of the SliceModal's optgroups, but a name that exists in multiple tiers appears in EACH of their groups so the user can pick any source. ``cloud_status`` / ``orca_cloud_status`` let the frontend show a banner explaining why a cloud tier is empty when the user expected to see it (signed out / token expired / network down). Each tier has its own status because they can fail independently. """ orca_cloud: UnifiedPresetsBySlot = UnifiedPresetsBySlot() cloud: UnifiedPresetsBySlot = UnifiedPresetsBySlot() local: UnifiedPresetsBySlot = UnifiedPresetsBySlot() standard: UnifiedPresetsBySlot = UnifiedPresetsBySlot() cloud_status: CloudStatus = "ok" orca_cloud_status: CloudStatus = "ok" class LoadedSpoolPreset(BaseModel): """The filament profile Bambuddy recorded for a slot (``slot_preset_mappings``). ``tray_info_idx`` is the filament id the slot was configured with alongside it. The SliceModal compares it with the tray's live id the same way the slot card does, so a row that outlived a spool swap isn't trusted (#3216). """ preset_id: str preset_name: str preset_source: str tray_info_idx: str | None = None class LoadedSpoolTray(BaseModel): """One AMS slot or external holder, as the printer reports it right now.""" ams_id: int tray_id: int tray_type: str | None = None tray_sub_brands: str | None = None tray_color: str | None = None tray_info_idx: str | None = None # Firmware's presence bit. A non-RFID spool it can't identify is present # (True) but has no tray_type, which reads as "loaded, unconfigured". exists: bool | None = None state: int | None = None saved_preset: LoadedSpoolPreset | None = None class LoadedSpoolUnit(BaseModel): id: int is_ams_ht: bool trays: list[LoadedSpoolTray] class LoadedSpoolPrinter(BaseModel): id: int name: str # Short model name ("X1C", "H2D") — the form the slicer's @BBL tags use. model: str | None = None ams: list[LoadedSpoolUnit] = [] # Only holders that currently have a spool in them. external: list[LoadedSpoolTray] = [] # How many holders the printer has, loaded or not: two on a dual-nozzle # printer, whose holders are labelled left and right. external_holders: int = 0 class LoadedSpoolsResponse(BaseModel): """Connected printers and what is loaded in them, for the SliceModal (#3172).""" printers: list[LoadedSpoolPrinter] = []