fix(labels): replace incorrect ams_30x15 preset with correct AMS holder sizes (#1426)

Reporter — the same person who originally requested the labels
  feature in #809 — discovered that the ams_30x15 preset's 30x15 mm
  dimension didn't actually fit any variant of the MakerWorld AMS
  Filament Label Holder (model 752566) it advertised. Two new
  presets replace it:

  - ams_holder_74x33 (74 x 33 mm) matches the printable label STL
    bundled in the MakerWorld project
  - ams_holder_75x55 (75 x 55 mm) fits the cardstock-insert variant
    the reporter validated on bench

  Both cross the 20 mm height threshold so they land in the roomy
  layout branch — swatch on the left, QR on the right, multi-line
  text (brand, material, hex code, spool ID) in the middle. The
  old 30x15 mm preset couldn't fit a QR code; the new ones do.

  No DB migration: the preset name was never persisted. Callers
  scripting the old ams_30x15 value get a clean 422 at the route's
  Literal validator with the new valid values listed.

  i18n: replaced inventory.labels.templates.ams.{label,hint} with
  amsHolderSmall and amsHolderLarge across all 8 locales with real
  translations; parity guard cleaned of the stale English-fallback
  cognate entries. Parity holds at 4856 leaves per locale.

  Tests: backend label renderer + integration tests cover both new
  presets; LabelTemplatePickerModal test updated for the 6-button
  grid and the new template value in the API-call assertion.
This commit is contained in:
maziggy
2026-05-19 13:14:03 +02:00
parent 0b33862ae9
commit 1677efb2c6
19 changed files with 175 additions and 82 deletions
+8 -2
View File
@@ -66,7 +66,13 @@ class TestLocalInventoryLabels:
@pytest.mark.integration
async def test_all_four_templates_succeed(self, async_client: AsyncClient, spool_factory):
s = await spool_factory()
for template in ("ams_30x15", "box_62x29", "avery_5160", "avery_l7160"):
for template in (
"ams_holder_74x33",
"ams_holder_75x55",
"box_62x29",
"avery_5160",
"avery_l7160",
):
resp = await async_client.post(
"/api/v1/inventory/labels",
json={"spool_ids": [s.id], "template": template},
@@ -100,7 +106,7 @@ class TestLocalInventoryLabels:
s = await spool_factory()
resp = await async_client.post(
"/api/v1/inventory/labels",
json={"spool_ids": [s.id, 99999], "template": "ams_30x15"},
json={"spool_ids": [s.id, 99999], "template": "ams_holder_74x33"},
)
assert resp.status_code == 404
assert "99999" in resp.text
@@ -6,7 +6,14 @@ import pytest
from backend.app.services.label_renderer import LabelData, render_labels
ALL_TEMPLATES = ("ams_30x15", "box_40x30", "box_62x29", "avery_5160", "avery_l7160")
ALL_TEMPLATES = (
"ams_holder_74x33",
"ams_holder_75x55",
"box_40x30",
"box_62x29",
"avery_5160",
"avery_l7160",
)
def _sample(spool_id: int = 1, **overrides) -> LabelData:
@@ -58,7 +65,7 @@ def test_missing_optional_fields_does_not_crash():
deeplink_url="https://example.test/inventory?spool=42",
)
]
pdf = render_labels("ams_30x15", data)
pdf = render_labels("ams_holder_74x33", data)
assert pdf.startswith(b"%PDF")
@@ -74,7 +81,7 @@ def test_long_strings_are_truncated_not_overflowed():
long_brand = "A" * 200
long_name = "B" * 300
data = [_sample(brand=long_brand, name=long_name)]
pdf = render_labels("ams_30x15", data)
pdf = render_labels("ams_holder_74x33", data)
assert pdf.startswith(b"%PDF")
@@ -121,9 +128,10 @@ def _render_uncompressed(template, data):
from backend.app.services.label_renderer import _draw_label # noqa: PLC0415
# Mirror the page-size choice from render_labels but force pageCompression=0.
if template in ("ams_30x15", "box_40x30", "box_62x29"):
if template in ("ams_holder_74x33", "ams_holder_75x55", "box_40x30", "box_62x29"):
sizes = {
"ams_30x15": (30.0, 15.0),
"ams_holder_74x33": (74.0, 33.0),
"ams_holder_75x55": (75.0, 55.0),
"box_40x30": (40.0, 30.0),
"box_62x29": (62.0, 29.0),
}
@@ -167,9 +175,9 @@ def _render_uncompressed(template, data):
def test_ams_template_actually_renders_text():
"""Regression: the first cut of the AMS-holder layout produced labels with
only swatch + QR and no text at all because the side-by-side layout left
<5 mm for the text column. The redesign drops the QR on this template and
gives the right side to brand + material + spool ID. This pins that the
rendered PDF contains all three fields.
<5 mm for the text column. The current AMS templates use the roomy layout
(swatch + QR + multi-line text); this pins that the rendered PDF contains
brand + material + spool ID for the smaller AMS preset.
"""
data = [
LabelData(
@@ -182,7 +190,7 @@ def test_ams_template_actually_renders_text():
deeplink_url="https://example.test/inventory?spool=42",
)
]
pdf = _render_uncompressed("ams_30x15", data)
pdf = _render_uncompressed("ams_holder_74x33", data)
assert b"Polymaker" in pdf, "AMS template must render the brand"
assert b"PLA" in pdf, "AMS template must render the material"
# The bracketed-hash style is what the renderer uses for the spool ID;