mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
fix(slicer): preset visibility + lookup precedence + signed-out banner + AMS slot badges (#1712)
Four #1712 issues from the 2026-06-04 Orca Cloud integration: (1) Tier order put Orca Cloud above everything across SliceModal, auto-pick scoring, dropdown groups, the AMS slot picker, and the backend precedence. Bambu-Cloud-only users saw their profiles deprioritised behind an empty Orca tier. (2) Cross-tier dedup hid a same-named preset in all but the highest- priority tier. A user with both a local-imported and an Orca-synced "Bambu PLA Basic" couldn't see the Orca copy as a picker option. (3) CloudStatusBanner nagged signed-out users with a permanent "Sign in to Orca Cloud" line at the top of every slice -- even after explicit logout. Bambu Cloud had the symmetric problem. (4) ConfigureAmsSlotModal source badges were inconsistent: Orca rows showed only "Custom" (no source identity), Bambu Cloud built-in rows had no badge at all, and the orthogonal isUser-driven "Custom" badge collided with the source badge for cloud user presets. Order is local > orca_cloud > cloud > standard everywhere it lives (SliceModal SLICE_MODAL_TIER_ORDER + TIER_BONUS + dropdown tier list, ConfigureAmsSlotModal sourceOrder, and backend precedence). The order drives auto-pick + visual group rendering; it does NOT hide profiles. _dedupe_by_name replaced with _enrich_cloud_metadata: every tier returns its full list across all three slots (printer / process / filament). The function still backfills Bambu Cloud filament metadata from same-named local / orca_cloud / standard entries so cloud filaments score in pickFilamentForSlot. CloudStatusBanner silently no-ops on not_authenticated for both clouds; expired / unreachable still surface. The not_authenticated i18n keys stay in the locale files dormant. ConfigureAmsSlotModal: one source badge per row, one colour per source (green Local / purple Orca Cloud / bambu-blue Bambu Cloud / amber Built-in). The legacy isUser-driven "Custom" badge is gone; every row identifies its tier consistently.
This commit is contained in:
@@ -173,11 +173,10 @@ async def _fetch_cloud_presets(
|
||||
# one-by-one trips Bambu's limiter and returns 429 on every request
|
||||
# for users with large preset libraries (#1150 follow-up).
|
||||
#
|
||||
# The dedup pass (see _dedupe_by_name) compensates: when a cloud entry
|
||||
# wins over a same-named local entry, the cloud entry inherits the
|
||||
# local entry's filament_type / filament_colour. So cloud presets that
|
||||
# also exist locally still get metadata-aware pre-pick in the
|
||||
# SliceModal; cloud-only presets fall back to plain priority order.
|
||||
# The metadata-enrich pass (see _enrich_cloud_metadata) compensates:
|
||||
# a Bambu Cloud entry without its own filament_type/colour inherits
|
||||
# those values from a same-named local / orca_cloud / standard entry
|
||||
# so it can still score for type/colour matches in pickFilamentForSlot.
|
||||
_cloud_cache[cache_key] = (now, slots)
|
||||
return slots, "ok"
|
||||
finally:
|
||||
@@ -420,7 +419,7 @@ async def _resolve_slicer_api_url(db: AsyncSession) -> str | None:
|
||||
return url or None
|
||||
|
||||
|
||||
def _dedupe_by_name(
|
||||
def _enrich_cloud_metadata(
|
||||
orca_cloud: dict[str, list[UnifiedPreset]],
|
||||
cloud: dict[str, list[UnifiedPreset]],
|
||||
local: dict[str, list[UnifiedPreset]],
|
||||
@@ -431,26 +430,29 @@ def _dedupe_by_name(
|
||||
dict[str, list[UnifiedPreset]],
|
||||
dict[str, list[UnifiedPreset]],
|
||||
]:
|
||||
"""Filter so each preset name appears in exactly one tier.
|
||||
"""Backfill Bambu Cloud filament metadata; do NOT dedup tiers.
|
||||
|
||||
Precedence: ``orca_cloud > cloud > local > standard``. Orca Cloud is
|
||||
highest because a user who set up Orca sync is explicitly curating
|
||||
those profiles for use here; Bambu Cloud follows for the same reason
|
||||
one tier down. Order within each tier is preserved.
|
||||
Every tier surfaces its full list — a name that exists in both ``local``
|
||||
and ``orca_cloud`` shows up in BOTH dropdown groups so the user can pick
|
||||
either source. Tier ORDER (``local > orca_cloud > cloud > standard``)
|
||||
is communicated by the SliceModal's group rendering and by the
|
||||
name-collision fallback in ``findPresetByName``; this function does not
|
||||
enforce it.
|
||||
|
||||
Filament metadata merges across tiers: a Bambu Cloud entry without its
|
||||
own ``filament_type`` / ``filament_colour`` (Bambu Cloud doesn't surface
|
||||
Filament metadata merge: a Bambu Cloud entry without its own
|
||||
``filament_type`` / ``filament_colour`` (Bambu Cloud doesn't surface
|
||||
these in the list response for rate-limiting reasons — see
|
||||
:func:`_fetch_cloud_presets`) inherits values from the same-named local
|
||||
or standard entry. Orca Cloud already carries metadata inline, so no
|
||||
backfill is needed for it.
|
||||
:func:`_fetch_cloud_presets`) inherits values from a same-named entry
|
||||
in ``local`` / ``orca_cloud`` / ``standard``. This is the only reason
|
||||
this function exists post-#1712 — without the enrich the Bambu Cloud
|
||||
tier can't score in ``pickFilamentForSlot``.
|
||||
"""
|
||||
# Build a name → metadata lookup from the tiers that carry it (orca_cloud,
|
||||
# local, standard). Bambu cloud is intentionally skipped — it doesn't
|
||||
# populate filament_type/colour in the list response. Take whichever
|
||||
# non-empty entry shows up first.
|
||||
# Build a name → metadata lookup from the tiers that carry it (local,
|
||||
# orca_cloud, standard). Bambu cloud is intentionally skipped — it
|
||||
# doesn't populate filament_type/colour in the list response. Take
|
||||
# whichever non-empty entry shows up first.
|
||||
metadata_by_name: dict[str, tuple[str | None, str | None]] = {}
|
||||
for tier in (orca_cloud, local, standard):
|
||||
for tier in (local, orca_cloud, standard):
|
||||
for p in tier["filament"]:
|
||||
if p.name in metadata_by_name:
|
||||
continue
|
||||
@@ -466,27 +468,7 @@ def _dedupe_by_name(
|
||||
if p.filament_colour is None and c is not None:
|
||||
p.filament_colour = c
|
||||
|
||||
deduped_cloud = _empty_slots()
|
||||
deduped_local = _empty_slots()
|
||||
deduped_standard = _empty_slots()
|
||||
for slot in ("printer", "process", "filament"):
|
||||
seen = {p.name for p in orca_cloud[slot]}
|
||||
for p in cloud[slot]:
|
||||
if p.name in seen:
|
||||
continue
|
||||
deduped_cloud[slot].append(p)
|
||||
seen.add(p.name)
|
||||
for p in local[slot]:
|
||||
if p.name in seen:
|
||||
continue
|
||||
deduped_local[slot].append(p)
|
||||
seen.add(p.name)
|
||||
for p in standard[slot]:
|
||||
if p.name in seen:
|
||||
continue
|
||||
deduped_standard[slot].append(p)
|
||||
seen.add(p.name)
|
||||
return orca_cloud, deduped_cloud, deduped_local, deduped_standard
|
||||
return orca_cloud, cloud, local, standard
|
||||
|
||||
|
||||
@router.get("/printer-models")
|
||||
@@ -541,7 +523,7 @@ async def list_unified_presets(
|
||||
local = await _fetch_local_presets(db)
|
||||
standard = await _fetch_bundled_presets(db, refresh=refresh)
|
||||
|
||||
orca_cloud, cloud, local, standard = _dedupe_by_name(orca_cloud, cloud, local, standard)
|
||||
orca_cloud, cloud, local, standard = _enrich_cloud_metadata(orca_cloud, cloud, local, standard)
|
||||
|
||||
return UnifiedPresetsResponse(
|
||||
orca_cloud=UnifiedPresetsBySlot(**orca_cloud),
|
||||
|
||||
@@ -59,13 +59,14 @@ class UnifiedPresetsBySlot(BaseModel):
|
||||
|
||||
|
||||
class UnifiedPresetsResponse(BaseModel):
|
||||
"""Each tier carries only the names that didn't appear in a higher tier.
|
||||
"""Every tier carries its full preset list — no cross-tier dedup.
|
||||
|
||||
Priority order: ``orca_cloud > cloud > local > standard``. Orca Cloud is
|
||||
highest because it's the most-recently-explicitly-curated source for
|
||||
users who set up Orca sync (they did it on purpose; their Orca picks
|
||||
should outrank everything else). Bambu Cloud follows as the next-most-
|
||||
curated tier. Local imports beat the slicer's stock fallback.
|
||||
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
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
"""Tests for the unified slicer-presets endpoint helpers.
|
||||
|
||||
The endpoint stitches together three preset sources (cloud / local /
|
||||
standard) with name-based dedup. These tests pin the dedup logic, the
|
||||
cloud-status mapping, and the per-user / sidecar caches at the
|
||||
helper level — full HTTP integration is covered by the routes test.
|
||||
The endpoint stitches together four preset sources (local / orca_cloud /
|
||||
cloud / standard). It does NOT dedup across tiers — every tier surfaces
|
||||
its full list so the user can pick any source. Bambu Cloud filament
|
||||
metadata is enriched from same-named entries in the other tiers so it
|
||||
can still score in the SliceModal's auto-pick. These tests pin the
|
||||
enrich behaviour, the cloud-status mapping, and the per-user / sidecar
|
||||
caches at the helper level — full HTTP integration is covered by the
|
||||
routes test.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -28,50 +32,31 @@ def _slot(items: list[tuple[str, str, str]]) -> dict[str, list[UnifiedPreset]]:
|
||||
}
|
||||
|
||||
|
||||
class TestDedupeByName:
|
||||
"""Cloud > local > standard, by ``name``, order preserved within tier."""
|
||||
class TestEnrichCloudMetadata:
|
||||
"""No cross-tier dedup — every tier's full list comes back; Bambu Cloud
|
||||
filament metadata is enriched from same-named entries in other tiers."""
|
||||
|
||||
def test_cloud_wins_over_local_and_standard(self):
|
||||
def test_same_name_in_all_tiers_appears_in_every_tier(self):
|
||||
"""Critical regression guard for #1712: a user who has imported a
|
||||
local profile AND signed in to Orca AND has Bambu Cloud with the
|
||||
same name should see it under EACH source, not just the highest-
|
||||
priority tier. The order is used for auto-pick + group rendering;
|
||||
it is NOT used to hide profiles."""
|
||||
orca = _slot([("oid1", "Bambu PLA Basic", "orca_cloud")])
|
||||
cloud = _slot([("cid1", "Bambu PLA Basic", "cloud")])
|
||||
local = _slot([("lid1", "Bambu PLA Basic", "local")])
|
||||
standard = _slot([("Bambu PLA Basic", "Bambu PLA Basic", "standard")])
|
||||
|
||||
_oc, c, l_, s = sp._dedupe_by_name(_slot([]), cloud, local, standard)
|
||||
oc, c, l_, s = sp._enrich_cloud_metadata(orca, cloud, local, standard)
|
||||
|
||||
assert [p.source for p in l_["printer"]] == ["local"]
|
||||
assert [p.source for p in oc["printer"]] == ["orca_cloud"]
|
||||
assert [p.source for p in c["printer"]] == ["cloud"]
|
||||
assert l_["printer"] == []
|
||||
assert s["printer"] == []
|
||||
|
||||
def test_local_filtered_only_when_present_in_cloud(self):
|
||||
cloud = _slot([("cid1", "Custom PLA", "cloud")])
|
||||
local = _slot(
|
||||
[
|
||||
("lid1", "Custom PLA", "local"), # filtered (in cloud)
|
||||
("lid2", "My Workhorse PLA", "local"), # kept
|
||||
]
|
||||
)
|
||||
standard = _slot([])
|
||||
|
||||
_oc, _c, l_, _s = sp._dedupe_by_name(_slot([]), cloud, local, standard)
|
||||
assert [p.name for p in l_["printer"]] == ["My Workhorse PLA"]
|
||||
|
||||
def test_standard_filtered_against_both_higher_tiers(self):
|
||||
cloud = _slot([("c1", "A", "cloud")])
|
||||
local = _slot([("l1", "B", "local")])
|
||||
standard = _slot(
|
||||
[
|
||||
("A", "A", "standard"), # filtered (in cloud)
|
||||
("B", "B", "standard"), # filtered (in local)
|
||||
("C", "C", "standard"), # kept
|
||||
]
|
||||
)
|
||||
|
||||
_oc, _c, _l, s = sp._dedupe_by_name(_slot([]), cloud, local, standard)
|
||||
assert [p.name for p in s["printer"]] == ["C"]
|
||||
assert [p.source for p in s["printer"]] == ["standard"]
|
||||
|
||||
def test_preserves_order_within_tier(self):
|
||||
"""A tier's input order must be preserved in its output — nothing in
|
||||
the dedupe pass should sort, reverse, or otherwise reorder entries."""
|
||||
"""A tier's input order must be preserved — nothing in the enrich
|
||||
pass should sort, reverse, or otherwise reorder entries."""
|
||||
cloud = _slot(
|
||||
[
|
||||
("c1", "Z-First", "cloud"),
|
||||
@@ -79,25 +64,95 @@ class TestDedupeByName:
|
||||
("c3", "M-Third", "cloud"),
|
||||
]
|
||||
)
|
||||
_oc, c, _l, _s = sp._dedupe_by_name(_slot([]), cloud, _slot([]), _slot([]))
|
||||
_oc, c, _l, _s = sp._enrich_cloud_metadata(_slot([]), cloud, _slot([]), _slot([]))
|
||||
assert [p.name for p in c["printer"]] == ["Z-First", "A-Second", "M-Third"]
|
||||
|
||||
def test_dedupe_is_per_slot(self):
|
||||
"""A name colliding across DIFFERENT slots must NOT cross-filter —
|
||||
a "Custom" filament shouldn't hide a "Custom" printer."""
|
||||
def test_bambu_cloud_filament_metadata_backfilled_from_local(self):
|
||||
"""Bambu Cloud's list response omits filament_type/colour for
|
||||
rate-limit reasons. A same-named local entry's metadata fills in
|
||||
so the cloud entry can still score in pickFilamentForSlot."""
|
||||
local = {
|
||||
"printer": [],
|
||||
"process": [],
|
||||
"filament": [
|
||||
UnifiedPreset(
|
||||
id="lp1",
|
||||
name="Bambu PLA Basic",
|
||||
source="local",
|
||||
filament_type="PLA",
|
||||
filament_colour="#FF0000",
|
||||
)
|
||||
],
|
||||
}
|
||||
cloud = {
|
||||
"printer": [],
|
||||
"process": [],
|
||||
"filament": [UnifiedPreset(id="cf1", name="Custom", source="cloud")],
|
||||
"filament": [UnifiedPreset(id="cp1", name="Bambu PLA Basic", source="cloud")],
|
||||
}
|
||||
local = {
|
||||
"printer": [UnifiedPreset(id="lp1", name="Custom", source="local")],
|
||||
_oc, c, _l, _s = sp._enrich_cloud_metadata(_slot([]), cloud, local, _slot([]))
|
||||
# Cloud entry now carries the local entry's metadata.
|
||||
assert c["filament"][0].filament_type == "PLA"
|
||||
assert c["filament"][0].filament_colour == "#FF0000"
|
||||
# Local entry is untouched.
|
||||
assert local["filament"][0].filament_type == "PLA"
|
||||
|
||||
def test_bambu_cloud_metadata_falls_back_through_orca_and_standard(self):
|
||||
"""When local doesn't carry the name, orca_cloud / standard fill in."""
|
||||
orca = {
|
||||
"printer": [],
|
||||
"process": [],
|
||||
"filament": [],
|
||||
"filament": [
|
||||
UnifiedPreset(
|
||||
id="o1",
|
||||
name="Bambu PLA Basic",
|
||||
source="orca_cloud",
|
||||
filament_type="PLA",
|
||||
filament_colour="#00FF00",
|
||||
)
|
||||
],
|
||||
}
|
||||
_oc, _c, l_, _s = sp._dedupe_by_name(_slot([]), cloud, local, _slot([]))
|
||||
# The filament-tier collision must NOT remove the printer-tier "Custom".
|
||||
assert [p.name for p in l_["printer"]] == ["Custom"]
|
||||
cloud = {
|
||||
"printer": [],
|
||||
"process": [],
|
||||
"filament": [UnifiedPreset(id="cp1", name="Bambu PLA Basic", source="cloud")],
|
||||
}
|
||||
_oc, c, _l, _s = sp._enrich_cloud_metadata(orca, cloud, _slot([]), _slot([]))
|
||||
assert c["filament"][0].filament_type == "PLA"
|
||||
assert c["filament"][0].filament_colour == "#00FF00"
|
||||
|
||||
def test_bambu_cloud_keeps_its_own_metadata_when_present(self):
|
||||
"""If Bambu Cloud already has filament_type / filament_colour the
|
||||
enrich pass must not overwrite them with a different same-named
|
||||
entry's values."""
|
||||
local = {
|
||||
"printer": [],
|
||||
"process": [],
|
||||
"filament": [
|
||||
UnifiedPreset(
|
||||
id="lp1",
|
||||
name="Bambu PLA Basic",
|
||||
source="local",
|
||||
filament_type="PETG",
|
||||
filament_colour="#000000",
|
||||
)
|
||||
],
|
||||
}
|
||||
cloud = {
|
||||
"printer": [],
|
||||
"process": [],
|
||||
"filament": [
|
||||
UnifiedPreset(
|
||||
id="cp1",
|
||||
name="Bambu PLA Basic",
|
||||
source="cloud",
|
||||
filament_type="PLA",
|
||||
filament_colour="#FFFFFF",
|
||||
)
|
||||
],
|
||||
}
|
||||
_oc, c, _l, _s = sp._enrich_cloud_metadata(_slot([]), cloud, local, _slot([]))
|
||||
assert c["filament"][0].filament_type == "PLA"
|
||||
assert c["filament"][0].filament_colour == "#FFFFFF"
|
||||
|
||||
|
||||
def _user_with_cloud_auth(user_id: int = 1) -> MagicMock:
|
||||
|
||||
Reference in New Issue
Block a user