mirror of
https://github.com/RunLit/Bambu-Run.git
synced 2026-06-22 22:19:03 +01:00
* added mcp initial trail files * timestamp use your local django timezone * added bambu cloud task sync with correct endpoint other than py cloud api * back fill and relink print name using cloud if there is * use correct bump-version
141 lines
5.5 KiB
Python
141 lines
5.5 KiB
Python
"""
|
|
Management command: bambu_sync_cloud
|
|
|
|
Backfill BambuCloudTask records from the Bambu Cloud API and link them to
|
|
existing PrintJob records. Primarily useful for jobs created before this
|
|
feature existed, or for re-syncing if the collector was offline at job end.
|
|
|
|
Usage:
|
|
python manage.py bambu_sync_cloud
|
|
python manage.py bambu_sync_cloud --limit 100
|
|
python manage.py bambu_sync_cloud --dry-run
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
|
|
from django.core.management.base import BaseCommand, CommandError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = "Backfill BambuCloudTask records from Bambu Cloud API and link to PrintJob"
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument(
|
|
'--limit', type=int, default=20,
|
|
help='Number of recent cloud tasks to fetch (default: 20)'
|
|
)
|
|
parser.add_argument(
|
|
'--dry-run', action='store_true',
|
|
help='Show what would be synced without writing to DB'
|
|
)
|
|
|
|
def handle(self, *args, **options):
|
|
limit = options['limit']
|
|
dry_run = options['dry_run']
|
|
|
|
bambu_token = os.environ.get('BAMBU_TOKEN')
|
|
bambu_username = os.environ.get('BAMBU_USERNAME')
|
|
bambu_password = os.environ.get('BAMBU_PASSWORD')
|
|
|
|
if not bambu_token and not all([bambu_username, bambu_password]):
|
|
raise CommandError(
|
|
"Either BAMBU_TOKEN or both BAMBU_USERNAME and BAMBU_PASSWORD must be set"
|
|
)
|
|
|
|
try:
|
|
from bambulab import BambuClient
|
|
from bambulab.auth import BambuAuthenticator
|
|
except ImportError:
|
|
raise CommandError("bambu-lab-cloud-api is not installed")
|
|
|
|
if bambu_token:
|
|
client = BambuClient(token=bambu_token)
|
|
else:
|
|
auth = BambuAuthenticator()
|
|
token = auth.login(bambu_username, bambu_password)
|
|
client = BambuClient(token=token)
|
|
|
|
from bambu_run.bambu_cloud import get_tasks, upsert_cloud_task
|
|
from bambu_run.models import PrintJob
|
|
|
|
self.stdout.write(f"Fetching last {limit} tasks from Bambu Cloud...")
|
|
try:
|
|
response = get_tasks(client, limit=limit)
|
|
except Exception as e:
|
|
raise CommandError(f"Cloud API request failed: {e}")
|
|
|
|
hits = response.get('hits', response.get('tasks', []))
|
|
self.stdout.write(f"Got {len(hits)} tasks from cloud")
|
|
|
|
created_count = updated_count = linked_count = 0
|
|
|
|
for task_dict in hits:
|
|
task_id = task_dict.get('id')
|
|
design_title = task_dict.get('designTitle') or ''
|
|
plate_title = task_dict.get('title') or ''
|
|
display_name = design_title or plate_title or f"task-{task_id}"
|
|
|
|
if dry_run:
|
|
self.stdout.write(
|
|
f" [dry-run] Would upsert task {task_id}: {display_name!r}"
|
|
)
|
|
# Check if we'd link to a PrintJob
|
|
job = PrintJob.objects.filter(cloud_task_id_raw=task_id).first()
|
|
if job:
|
|
self.stdout.write(f" → would link to PrintJob #{job.id}")
|
|
continue
|
|
|
|
try:
|
|
cloud_task, created = upsert_cloud_task(task_dict)
|
|
if created:
|
|
created_count += 1
|
|
self.stdout.write(f" Created: {display_name!r} (task {task_id})")
|
|
else:
|
|
updated_count += 1
|
|
|
|
# Link to any matching PrintJob by cloud_task_id_raw
|
|
linked = PrintJob.objects.filter(
|
|
cloud_task_id_raw=task_id, cloud_task__isnull=True
|
|
).update(cloud_task=cloud_task)
|
|
if linked:
|
|
linked_count += linked
|
|
self.stdout.write(f" Linked {linked} PrintJob(s) for task {task_id}")
|
|
|
|
# Historical backfill: match by cloud start_time ± 2 min + device serial
|
|
if cloud_task.cloud_start_time and cloud_task.device_serial:
|
|
from datetime import timedelta
|
|
from bambu_run.models import Printer
|
|
printer = Printer.objects.filter(
|
|
serial_number=cloud_task.device_serial
|
|
).first()
|
|
if printer:
|
|
window_start = cloud_task.cloud_start_time - timedelta(minutes=5)
|
|
window_end = cloud_task.cloud_start_time + timedelta(minutes=5)
|
|
historical = PrintJob.objects.filter(
|
|
device=printer,
|
|
start_time__gte=window_start,
|
|
start_time__lte=window_end,
|
|
cloud_task__isnull=True,
|
|
).update(cloud_task=cloud_task)
|
|
if historical:
|
|
linked_count += historical
|
|
self.stdout.write(
|
|
f" Historically linked {historical} PrintJob(s) by time for task {task_id}"
|
|
)
|
|
|
|
except Exception as e:
|
|
self.stderr.write(f" Error processing task {task_id}: {e}")
|
|
|
|
if not dry_run:
|
|
self.stdout.write(
|
|
self.style.SUCCESS(
|
|
f"\nDone: {created_count} created, {updated_count} updated, "
|
|
f"{linked_count} PrintJob(s) linked"
|
|
)
|
|
)
|
|
else:
|
|
self.stdout.write(self.style.WARNING("\nDry run complete — no changes written"))
|