Files
bambuddy/backend/app/services/print_log.py
maziggy 8d618678a2 fix(stats): don't bank the disconnect gap as print time on reconnect (#2592)
reconcile_stale_active_prints closes out stale status="printing" archives by
synthesising an aborted on_print_complete, which logged a PrintLogEntry whose
duration was completed_at - started_at — the whole multi-day disconnect gap,
since a reconciled archive's real end time is unknown. Across a farm of stale
rows this inflated Total Print Time by hundreds of hours, and the Stats total
recomputed the same value from the timestamps even when duration was NULL/0.

Reconciled completions now log duration_seconds=0, the two Stats time paths
trust a stored 0 instead of recomputing, and reconciled aborts get an honest
"Stale - reconciled ..." failure_reason instead of "User cancelled". Genuine
long prints are untouched (no cap; still-running >24h prints aren't stale).
2026-07-18 09:58:21 +02:00

80 lines
2.6 KiB
Python

"""Service for writing independent print log entries.
Log entries are written to a separate table and never touch archives or queue items.
"""
import logging
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession
from backend.app.models.print_log import PrintLogEntry
logger = logging.getLogger(__name__)
async def write_log_entry(
db: AsyncSession,
*,
status: str,
archive_id: int | None = None,
print_name: str | None = None,
printer_name: str | None = None,
printer_id: int | None = None,
started_at: datetime | None = None,
completed_at: datetime | None = None,
filament_type: str | None = None,
filament_color: str | None = None,
filament_used_grams: float | None = None,
cost: float | None = None,
energy_kwh: float | None = None,
energy_cost: float | None = None,
failure_reason: str | None = None,
thumbnail_path: str | None = None,
created_by_id: int | None = None,
created_by_username: str | None = None,
reconciled: bool = False,
) -> PrintLogEntry:
"""Write a print log entry.
``reconciled`` marks a synthetic completion written when a stale
``status="printing"`` archive is closed out at reconnect. Its real end time
is unknown — the print stopped somewhere during the disconnect and
``completed_at`` is only the reconnect moment — so ``completed_at -
started_at`` would bank the entire disconnect gap as print time, adding
hundreds of fictitious hours across a farm of stale rows (#2592). For those
entries we store an explicit ``0`` ("no measured runtime") rather than a
fabricated duration; the stats total trusts a stored 0 instead of
recomputing from the stale timestamps.
"""
if reconciled:
duration: int | None = 0
elif started_at and completed_at:
duration = int((completed_at - started_at).total_seconds())
else:
duration = None
entry = PrintLogEntry(
archive_id=archive_id,
print_name=print_name,
printer_name=printer_name,
printer_id=printer_id,
status=status,
started_at=started_at,
completed_at=completed_at,
duration_seconds=duration,
filament_type=filament_type,
filament_color=filament_color,
filament_used_grams=filament_used_grams,
cost=cost,
energy_kwh=energy_kwh,
energy_cost=energy_cost,
failure_reason=failure_reason,
thumbnail_path=thumbnail_path,
created_by_id=created_by_id,
created_by_username=created_by_username,
)
db.add(entry)
await db.flush()
return entry