follow-up(queue): cache per-plate 3MF metadata so queue polling stops re-parsing every row (#2573)

This commit is contained in:
maziggy
2026-07-17 08:26:23 +02:00
parent f8e49fed91
commit 3f21e0b8ff
2 changed files with 54 additions and 18 deletions
+23 -18
View File
@@ -493,19 +493,23 @@ def _parse_plate_metadata_uncached(file_path: Path, plate_id: int | None) -> Pla
out: list[dict] = []
for f in plate_elem.findall("filament"):
filament_id = f.get("id")
# Both the used_g float() and the id int() must stay inside the guard:
# a non-numeric id or used_g is silently skipped (matches the legacy
# helpers, which tolerated garbage rows rather than raising — a raise
# here would 500 the whole queue listing).
try:
used_amount = float(f.get("used_g", "0"))
if filament_id:
out.append(
{
"slot_id": int(filament_id),
"used_g": used_amount,
"type": f.get("type", ""),
"color": f.get("color", ""),
}
)
except (ValueError, TypeError):
continue
if filament_id:
out.append(
{
"slot_id": int(filament_id),
"used_g": used_amount,
"type": f.get("type", ""),
"color": f.get("color", ""),
}
)
return out
print_time: int | None = None
@@ -539,19 +543,20 @@ def _parse_plate_metadata_uncached(file_path: Path, plate_id: int | None) -> Pla
# Legacy plate_id=None usage: every filament in the file, not just plate 1.
for f in root.findall(".//filament"):
filament_id = f.get("id")
# int()/float() both guarded — a garbage id/used_g row is skipped, not raised.
try:
used_amount = float(f.get("used_g", "0"))
if filament_id:
filament_usage.append(
{
"slot_id": int(filament_id),
"used_g": used_amount,
"type": f.get("type", ""),
"color": f.get("color", ""),
}
)
except (ValueError, TypeError):
continue
if filament_id:
filament_usage.append(
{
"slot_id": int(filament_id),
"used_g": used_amount,
"type": f.get("type", ""),
"color": f.get("color", ""),
}
)
return PlateMetadata(
print_time_seconds=print_time,
+31
View File
@@ -1151,6 +1151,37 @@ class TestExtractPlateMetadataFrom3mf:
assert len(cached.filament_usage) == 2
assert all(f["used_g"] > 0 for f in cached.filament_usage)
def test_non_numeric_filament_id_is_skipped_not_raised(self, tmp_path):
# A garbage filament id (or used_g) must be silently skipped, exactly as
# the legacy helpers did — a raise here would 500 the queue listing that
# calls this per row. Guards both the plate-specific and plate_id=None paths.
from backend.app.utils.threemf_tools import (
extract_filament_usage_from_3mf,
extract_plate_metadata_from_3mf,
)
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
<config>
<plate>
<metadata key="index" value="1"/>
<metadata key="prediction" value="3600"/>
<filament id="abc" used_g="5.0" type="PLA" color="#FFFFFF"/>
<filament id="1" used_g="10.0" type="PLA" color="#FF0000"/>
<filament id="2" used_g="bad" type="PLA" color="#00FF00"/>
</plate>
</config>
"""
file_path = self._write(tmp_path, xml_content)
meta = extract_plate_metadata_from_3mf(file_path, plate_id=1)
assert [f["slot_id"] for f in meta.filament_usage] == [1]
assert meta.filament_used_grams == 10.0
assert meta.print_time_seconds == 3600
# plate_id=None path (collects all filaments in the file) must skip too.
none_result = extract_filament_usage_from_3mf(file_path, plate_id=None)
assert [f["slot_id"] for f in none_result] == [1]
def test_missing_file_returns_empty_and_is_not_cached(self, tmp_path):
from unittest.mock import patch