Refactor Spoolman per-filament tracking (PR #277 follow-up)

Extract Spoolman tracking from main.py into dedicated service module,
DRY up repeated helpers, add i18n for new settings UI, and add tests.

- Move ~460 lines from main.py to services/spoolman_tracking.py
- Extract _resolve_spool_tag, _resolve_global_tray_id, build_ams_tray_lookup helpers
- Replace fragile tuple return in get_spoolman_settings() with dict
- Wire 4 new UI strings through i18n (en/de/ja)
- Add 42 backend unit tests (spoolman tracking helpers + 3MF parsing)
- Add 6 frontend tests for weight sync and partial usage toggles
- Update CHANGELOG, wiki, and website docs

Closes PR #277
This commit is contained in:
maziggy
2026-02-06 10:03:27 +01:00
parent 73be96497c
commit dc82b5ff2a
14 changed files with 1049 additions and 571 deletions
+6 -7
View File
@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { Loader2, Check, X, RefreshCw, Link2, Link2Off, Database, ChevronDown, Info, AlertTriangle } from 'lucide-react';
import { api } from '../api/client';
@@ -7,6 +8,7 @@ import { Card, CardContent, CardHeader } from './Card';
import { Button } from './Button';
export function SpoolmanSettings() {
const { t } = useTranslation();
const queryClient = useQueryClient();
const [localEnabled, setLocalEnabled] = useState(false);
const [localUrl, setLocalUrl] = useState('');
@@ -258,11 +260,9 @@ export function SpoolmanSettings() {
{localSyncMode === 'auto' && (
<div className="flex items-center justify-between">
<div>
<p className="text-white">Disable AMS Estimated Weight Sync</p>
<p className="text-white">{t('spoolman.disableWeightSync')}</p>
<p className="text-sm text-bambu-gray">
Don't update remaining capacity from AMS estimates. Use this if you prefer
Spoolman's usage tracking over AMS percentage-based estimates. New spools
will still use the AMS estimate as their initial weight.
{t('spoolman.disableWeightSyncDesc')}
</p>
</div>
<label className="relative inline-flex items-center cursor-pointer">
@@ -282,10 +282,9 @@ export function SpoolmanSettings() {
{localDisableWeightSync && (
<div className="flex items-center justify-between">
<div>
<p className="text-white">Report Partial Usage for Failed Prints</p>
<p className="text-white">{t('spoolman.reportPartialUsage')}</p>
<p className="text-sm text-bambu-gray">
When a print fails or is cancelled, report the estimated filament used
up to that point based on layer progress.
{t('spoolman.reportPartialUsageDesc')}
</p>
</div>
<label className="relative inline-flex items-center cursor-pointer">