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.
186 lines
6.8 KiB
Python
186 lines
6.8 KiB
Python
import pytest
|
|
from decimal import Decimal
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from bambu_run.models import Printer, PrinterMetrics, FilamentSnapshot
|
|
|
|
|
|
@pytest.fixture
|
|
def logged_in_client(client, django_user_model):
|
|
user = django_user_model.objects.create_user(username="tester", password="pw")
|
|
client.force_login(user)
|
|
return client
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_dashboard_filaments_carry_ams_unit_info(logged_in_client):
|
|
printer = Printer.objects.create(name="Printer A", model="H2C", is_active=True)
|
|
metric = PrinterMetrics.objects.create(device=printer, timestamp=timezone.now())
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=0, ams_type="AMS",
|
|
type="PLA", remain_percent=80,
|
|
)
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=128, ams_type="AMS HT",
|
|
type="PA-CF", remain_percent=50,
|
|
)
|
|
|
|
resp = logged_in_client.get(
|
|
reverse("bambu_run:printer_dashboard", kwargs={"pk": printer.pk})
|
|
)
|
|
|
|
filaments = resp.context["stats"]["filaments"]
|
|
assert len(filaments) == 2
|
|
units = {(f["ams_unit_id"], f["ams_type"]) for f in filaments}
|
|
assert units == {(0, "AMS"), (128, "AMS HT")}
|
|
|
|
ams_units = resp.context["stats"]["ams_units"]
|
|
assert ams_units == [
|
|
{"ams_unit_id": 0, "ams_type": "AMS"},
|
|
{"ams_unit_id": 128, "ams_type": "AMS HT"},
|
|
]
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_filament_timeline_keeps_same_tray_id_units_separate(logged_in_client):
|
|
from bambu_run.views import PrinterDashboardView
|
|
|
|
printer = Printer.objects.create(name="Printer A", model="H2C", is_active=True)
|
|
metric = PrinterMetrics.objects.create(device=printer, timestamp=timezone.now())
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=0, ams_type="AMS",
|
|
type="PLA", sub_type="PLA Basic", color="FF0000", remain_percent=80,
|
|
)
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=128, ams_type="AMS HT",
|
|
type="PLA", sub_type="PLA Basic", color="FF0000", remain_percent=50,
|
|
)
|
|
|
|
from bambu_run.views import fetch_snapshots_by_metric
|
|
|
|
view = PrinterDashboardView()
|
|
metrics = list(PrinterMetrics.objects.filter(pk=metric.pk))
|
|
timeline = view._prepare_filament_timeline(
|
|
metrics, fetch_snapshots_by_metric(metrics)
|
|
)
|
|
|
|
assert len(timeline) == 2
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_dashboard_renders_unit_pills_and_badges_with_multiple_units(logged_in_client):
|
|
printer = Printer.objects.create(name="Printer A", model="H2C", is_active=True)
|
|
metric = PrinterMetrics.objects.create(device=printer, timestamp=timezone.now())
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=0, ams_type="AMS",
|
|
type="PLA", color="FF0000FF", remain_percent=80,
|
|
)
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=128, ams_type="AMS HT",
|
|
type="PA-CF", color="00FF00FF", remain_percent=50,
|
|
)
|
|
|
|
resp = logged_in_client.get(
|
|
reverse("bambu_run:printer_dashboard", kwargs={"pk": printer.pk})
|
|
)
|
|
|
|
assert resp.status_code == 200
|
|
html = resp.content.decode()
|
|
assert "ams-filter-pills" in html
|
|
assert "ams-badge-ams" in html
|
|
assert "ams-badge-ams-ht" in html
|
|
assert 'data-ams-unit-id="0"' in html
|
|
assert 'data-ams-unit-id="128"' in html
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_dashboard_groups_filaments_by_ams_unit(logged_in_client):
|
|
printer = Printer.objects.create(name="Printer A", model="H2C", is_active=True)
|
|
metric = PrinterMetrics.objects.create(
|
|
device=printer, timestamp=timezone.now(),
|
|
ams_units=[
|
|
{"unit_id": "0", "ams_type": "AMS 2 Pro", "humidity": 5, "temp": 22.5},
|
|
{"unit_id": "128", "ams_type": "AMS HT", "humidity": 8, "temp": 60.0},
|
|
],
|
|
)
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=0, ams_type="AMS 2 Pro",
|
|
type="ABS", remain_percent=80,
|
|
)
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=1, ams_unit_id=0, ams_type="AMS 2 Pro",
|
|
type="ABS", remain_percent=60,
|
|
)
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=128, ams_type="AMS HT",
|
|
type="PA-CF", remain_percent=50,
|
|
)
|
|
|
|
resp = logged_in_client.get(
|
|
reverse("bambu_run:printer_dashboard", kwargs={"pk": printer.pk})
|
|
)
|
|
|
|
groups = resp.context["stats"]["ams_groups"]
|
|
assert len(groups) == 2
|
|
|
|
ams2pro_group, ht_group = groups
|
|
assert ams2pro_group["unit_id"] == 0
|
|
assert ams2pro_group["label"] == "AMS 2 Pro (Unit 0)"
|
|
assert ams2pro_group["humidity"] == 5
|
|
assert ams2pro_group["temp"] == 22.5
|
|
assert len(ams2pro_group["filaments"]) == 2
|
|
|
|
assert ht_group["unit_id"] == 128
|
|
assert ht_group["label"] == "AMS HT (Unit 128)"
|
|
assert ht_group["humidity"] == 8
|
|
assert len(ht_group["filaments"]) == 1
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_dashboard_renders_wide_and_compact_panels(logged_in_client):
|
|
printer = Printer.objects.create(name="Printer A", model="H2C", is_active=True)
|
|
metric = PrinterMetrics.objects.create(
|
|
device=printer, timestamp=timezone.now(),
|
|
ams_units=[
|
|
{"unit_id": "0", "ams_type": "AMS 2 Pro", "humidity": 5, "temp": 22.5},
|
|
{"unit_id": "128", "ams_type": "AMS HT", "humidity": 8, "temp": 60.0},
|
|
],
|
|
)
|
|
for tray_id in range(4):
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=tray_id, ams_unit_id=0, ams_type="AMS 2 Pro",
|
|
type="ABS", remain_percent=80,
|
|
)
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=128, ams_type="AMS HT",
|
|
type="PA-CF", remain_percent=50,
|
|
)
|
|
|
|
resp = logged_in_client.get(
|
|
reverse("bambu_run:printer_dashboard", kwargs={"pk": printer.pk})
|
|
)
|
|
|
|
html = resp.content.decode()
|
|
assert "col-12 ams-group" in html # wide group: col-12 only
|
|
assert "col-lg-3 ams-group" in html # compact group: col-lg-3
|
|
assert "AMS 2 Pro (Unit 0)" in html
|
|
assert "AMS HT (Unit 128)" in html
|
|
|
|
|
|
@pytest.mark.django_db
|
|
def test_dashboard_hides_unit_pills_with_single_unit(logged_in_client):
|
|
printer = Printer.objects.create(name="Printer A", model="H2C", is_active=True)
|
|
metric = PrinterMetrics.objects.create(device=printer, timestamp=timezone.now())
|
|
FilamentSnapshot.objects.create(
|
|
printer_metric=metric, tray_id=0, ams_unit_id=0, ams_type="AMS",
|
|
type="PLA", color="FF0000FF", remain_percent=80,
|
|
)
|
|
|
|
resp = logged_in_client.get(
|
|
reverse("bambu_run:printer_dashboard", kwargs={"pk": printer.pk})
|
|
)
|
|
|
|
assert resp.status_code == 200
|
|
assert "ams-filter-pills" not in resp.content.decode()
|