Fix plate calibration backup/restore with printer ID remapping

- Add "Plate Detection" option to backup modal
- Store printer_id_to_serial mapping in backup for calibration files
- Remap printer IDs during restore based on serial numbers so files
  like printer_3_ref_0.jpg get correctly renamed if printer 3 becomes
  printer 2 on the new system
- Add parents=True to mkdir calls in config.py to fix startup crash
- Labels/metadata are now correctly restored via the metadata.json files
This commit is contained in:
maziggy
2026-01-27 12:39:02 +01:00
parent 30f7d41ec3
commit 945121b799
3 changed files with 113 additions and 56 deletions
+64 -7
View File
@@ -435,11 +435,24 @@ async def export_backup(
if include_printers and include_plate_calibration:
plate_cal_dir = app_settings.plate_calibration_dir
if plate_cal_dir.exists():
backup["plate_calibration"] = []
backup["plate_calibration"] = {
"files": [],
"printer_id_to_serial": {}, # Map old printer IDs to serial numbers for restore
}
for cal_file in plate_cal_dir.iterdir():
if cal_file.is_file():
backup["plate_calibration"].append(cal_file.name)
if backup["plate_calibration"]:
backup["plate_calibration"]["files"].append(cal_file.name)
# Extract printer ID from filename (e.g., "printer_1_ref_0.jpg" -> 1)
if cal_file.name.startswith("printer_"):
parts = cal_file.name.split("_")
if len(parts) >= 2 and parts[1].isdigit():
old_printer_id = int(parts[1])
if old_printer_id not in backup["plate_calibration"]["printer_id_to_serial"]:
# Look up serial number for this printer ID
backup["plate_calibration"]["printer_id_to_serial"][old_printer_id] = (
printer_id_to_serial.get(old_printer_id)
)
if backup["plate_calibration"]["files"]:
backup["included"].append("plate_calibration")
# Filaments
@@ -883,6 +896,8 @@ async def import_backup(
content = await file.read()
base_dir = app_settings.base_dir
files_restored = 0
# Store plate calibration files for later (need printer ID remapping after printers restored)
plate_cal_files: dict[str, bytes] = {}
# Check if it's a ZIP file
if file.filename and file.filename.endswith(".zip"):
@@ -903,12 +918,12 @@ async def import_backup(
# Ensure path is safe (no path traversal)
if ".." in zip_path or zip_path.startswith("/"):
continue
# Plate calibration files go to plate_calibration_dir (may differ from base_dir)
# Plate calibration files - store for later processing after printers are restored
if zip_path.startswith("plate_calibration/"):
filename = zip_path.replace("plate_calibration/", "", 1)
target_path = app_settings.plate_calibration_dir / filename
else:
target_path = base_dir / zip_path
plate_cal_files[filename] = zf.read(zip_path)
continue
target_path = base_dir / zip_path
target_path.parent.mkdir(parents=True, exist_ok=True)
with zf.open(zip_path) as src, open(target_path, "wb") as dst:
dst.write(src.read())
@@ -1054,6 +1069,48 @@ async def import_backup(
# Flush printers so other sections can look them up
await db.flush()
# Restore plate calibration files (remap printer IDs based on serial numbers)
if plate_cal_files:
# Build serial_number -> new_printer_id mapping
serial_to_new_id: dict[str, int] = {}
pr_result = await db.execute(select(Printer))
for pr in pr_result.scalars().all():
serial_to_new_id[pr.serial_number] = pr.id
# Get old_id -> serial mapping from backup (supports both old list format and new dict format)
plate_cal_data = backup.get("plate_calibration", {})
if isinstance(plate_cal_data, dict):
old_id_to_serial: dict[int, str | None] = {
int(k): v for k, v in plate_cal_data.get("printer_id_to_serial", {}).items()
}
else:
old_id_to_serial = {}
# Build old_id -> new_id mapping
old_id_to_new_id: dict[int, int] = {}
for old_id, serial in old_id_to_serial.items():
if serial and serial in serial_to_new_id:
old_id_to_new_id[old_id] = serial_to_new_id[serial]
app_settings.plate_calibration_dir.mkdir(parents=True, exist_ok=True)
for filename, file_data in plate_cal_files.items():
# Parse old printer ID from filename (e.g., "printer_3_ref_0.jpg" -> 3)
new_filename = filename
if filename.startswith("printer_"):
parts = filename.split("_")
if len(parts) >= 2 and parts[1].isdigit():
old_printer_id = int(parts[1])
if old_printer_id in old_id_to_new_id:
new_printer_id = old_id_to_new_id[old_printer_id]
# Replace old ID with new ID in filename
new_filename = filename.replace(f"printer_{old_printer_id}_", f"printer_{new_printer_id}_", 1)
target_path = app_settings.plate_calibration_dir / new_filename
with open(target_path, "wb") as f:
f.write(file_data)
files_restored += 1
# Restore notification providers (skip or overwrite duplicates by name)
# Build printer serial to ID lookup (printers were restored first)
if "notification_providers" in backup:
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -23,7 +23,7 @@
<!-- Splash screens for iOS -->
<link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
<script type="module" crossorigin src="/assets/index-jEq4K9YC.js"></script>
<script type="module" crossorigin src="/assets/index-C8T392Mv.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BQIOMqJ9.css">
</head>
<body>