mirror of
https://github.com/RunLit/Bambu-Run.git
synced 2026-08-22 14:54:19 +01:00
The printer chart API was issuing one extra SELECT per row per field that the serializer read but .only() omitted. nozzle_temp_left/nozzle_target_temp_left were added to the serialization loop without being added to _METRICS_API_FIELDS, so a single-day request ran 5,507 queries and took 34s; the UI's default 48h range took 87s. Against a remote Postgres this is pure round-trip latency. - Add the two left-nozzle fields to _METRICS_API_FIELDS. - Always apply both time bounds in PrinterDataAPIView. Missing or partial date params previously left the range open, so a bare API call scanned the whole metrics table. - Give PrinterDashboardView the same treatment the API already had: .only(), sampling to _MAX_CHART_POINTS, and a targeted snapshot fetch. It also evaluated its queryset twice, because .last() on an unevaluated queryset issues its own query plus its own prefetch. - Extract sample_metrics() and fetch_snapshots_by_metric() for reuse. Printer shares the infrastructure_device table with a host project's other devices and had no category field, so Printer.objects.filter(is_active=True) could return a NAS. Add a category field and a category-scoped default manager, keeping all_objects as the unfiltered base manager so related descriptors still resolve every row. Migration 0009 creates the column in standalone deployments and skips the DDL where the host project already owns it. Measured: API 87s -> 0.67s, dashboard 3.4s -> 0.9s. Query counts are now independent of row count, asserted by tests.
72 lines
2.2 KiB
Python
72 lines
2.2 KiB
Python
"""Printer shares the `infrastructure_device` table with non-printer devices
|
|
(NAS, routers, ...) in host projects like RAE. Printer queries must never
|
|
resolve one of those rows.
|
|
"""
|
|
|
|
import pytest
|
|
from django.urls import reverse
|
|
|
|
from bambu_run.models import Printer
|
|
from bambu_run.views import resolve_printer_from_request
|
|
|
|
|
|
@pytest.fixture
|
|
def logged_in_client(client, django_user_model):
|
|
user = django_user_model.objects.create_user(username="scoping", password="pw")
|
|
client.force_login(user)
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def nas():
|
|
"""A non-printer device row sharing the table, sorting before any printer."""
|
|
return Printer.all_objects.create(
|
|
name="A NAS", model="DS920+", category="nas", is_active=True
|
|
)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_default_manager_excludes_non_printers(nas):
|
|
printer = Printer.objects.create(name="Z Printer", model="H2C", is_active=True)
|
|
|
|
assert list(Printer.objects.all()) == [printer]
|
|
assert nas in Printer.all_objects.all()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_new_printers_default_to_the_printer_category():
|
|
printer = Printer.objects.create(name="Fresh", model="H2C")
|
|
|
|
assert printer.category == Printer.CATEGORY_3D_PRINTER
|
|
assert printer in Printer.objects.all()
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_resolve_printer_skips_an_active_nas(nas):
|
|
"""The exact production failure: NAS sorts first and is active, printer is not."""
|
|
printer = Printer.objects.create(name="Z Printer", model="H2C", is_active=False)
|
|
|
|
assert resolve_printer_from_request(None) is None, "inactive printer must not resolve"
|
|
|
|
printer.is_active = True
|
|
printer.save()
|
|
assert resolve_printer_from_request(None) == printer
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_resolve_printer_by_pk_rejects_a_non_printer(nas):
|
|
from django.http import Http404
|
|
|
|
with pytest.raises(Http404):
|
|
resolve_printer_from_request(nas.pk)
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_dashboard_does_not_fall_back_to_a_nas(logged_in_client, nas):
|
|
resp = logged_in_client.get(reverse("bambu_run:printer_dashboard"))
|
|
|
|
assert resp.status_code == 200
|
|
assert "error" in resp.context
|
|
assert resp.context.get("printer_device") is None
|
|
assert list(resp.context["all_printers"]) == []
|