Add archive_id to SpoolUsageHistory model and update cost calculations in usage_tracker

This commit is contained in:
Matteo Parenti
2026-02-20 13:39:41 +01:00
parent 1dd266b66e
commit 427b08f9f6
3 changed files with 56 additions and 10 deletions
+21 -10
View File
@@ -862,7 +862,7 @@ async def rescan_archive(
if archive.filament_used_grams and archive.filament_type:
usage_result = await db.execute(
select(func.sum(SpoolUsageHistory.cost)).where(SpoolUsageHistory.print_name == archive.print_name)
select(func.sum(SpoolUsageHistory.cost)).where(SpoolUsageHistory.archive_id == archive.id)
)
usage_cost = usage_result.scalar()
if usage_cost is not None and usage_cost > 0:
@@ -912,24 +912,35 @@ async def recalculate_all_costs(
default_cost_setting = await get_setting(db, "default_filament_cost")
default_cost_per_kg = float(default_cost_setting) if default_cost_setting else 25.0
# Pre-fetch all usage costs in one query
# Pre-fetch all usage costs by archive_id
usage_costs_result = await db.execute(
select(SpoolUsageHistory.print_name, func.sum(SpoolUsageHistory.cost)).group_by(SpoolUsageHistory.print_name)
select(SpoolUsageHistory.archive_id, func.sum(SpoolUsageHistory.cost)).group_by(SpoolUsageHistory.archive_id)
)
usage_costs = usage_costs_result.fetchall()
cost_map = {row[0]: row[1] for row in usage_costs if row[1] is not None and row[1] > 0}
cost_map = {row[0]: row[1] for row in usage_costs if row[0] is not None and row[1] is not None and row[1] > 0}
updated = 0
for archive in archives:
usage_cost = cost_map.get(archive.print_name)
usage_cost = cost_map.get(archive.id)
if usage_cost is not None:
new_cost = round(usage_cost, 2)
elif archive.filament_used_grams and archive.filament_type:
primary_type = archive.filament_type.split(",")[0].strip()
cost_per_kg = filaments.get(primary_type, default_cost_per_kg)
new_cost = round((archive.filament_used_grams / 1000) * cost_per_kg, 2)
else:
new_cost = None
# Fallback: sum costs for old records by print_name
usage_result = await db.execute(
select(func.sum(SpoolUsageHistory.cost)).where(
SpoolUsageHistory.print_name == archive.print_name,
SpoolUsageHistory.archive_id is None,
)
)
fallback_cost = usage_result.scalar()
if fallback_cost is not None and fallback_cost > 0:
new_cost = round(fallback_cost, 2)
elif archive.filament_used_grams and archive.filament_type:
primary_type = archive.filament_type.split(",")[0].strip()
cost_per_kg = filaments.get(primary_type, default_cost_per_kg)
new_cost = round((archive.filament_used_grams / 1000) * cost_per_kg, 2)
else:
new_cost = None
if new_cost is not None and archive.cost != new_cost:
archive.cost = new_cost
updated += 1
@@ -15,6 +15,7 @@ class SpoolUsageHistory(Base):
spool_id: Mapped[int] = mapped_column(ForeignKey("spool.id", ondelete="CASCADE"))
printer_id: Mapped[int | None] = mapped_column(ForeignKey("printers.id", ondelete="SET NULL"))
print_name: Mapped[str | None] = mapped_column(String(500))
archive_id: Mapped[int | None] = mapped_column(ForeignKey("print_archives.id"), nullable=True)
weight_used: Mapped[float] = mapped_column(Float, default=0)
percent_used: Mapped[int] = mapped_column(Integer, default=0)
status: Mapped[str] = mapped_column(String(20), default="completed") # completed/failed/aborted
+34
View File
@@ -253,7 +253,10 @@ async def on_print_complete(
Returns a list of dicts describing what was logged (for WebSocket broadcast).
"""
from sqlalchemy import select
from backend.app.api.routes.settings import get_setting
from backend.app.models.spool_usage_history import SpoolUsageHistory
session = _active_sessions.pop(printer_id, None)
status = data.get("status", "completed")
@@ -375,6 +378,7 @@ async def on_print_complete(
percent_used=delta_pct,
status=status,
cost=cost,
archive_id=archive_id,
)
db.add(history)
@@ -405,6 +409,35 @@ async def on_print_complete(
if results:
await db.commit()
# --- Update PrintArchive.cost to sum all SpoolUsageHistory costs for this archive ---
if archive_id:
from sqlalchemy import func, select
from backend.app.models.archive import PrintArchive
# First try: sum by archive_id
cost_result = await db.execute(
select(func.coalesce(func.sum(SpoolUsageHistory.cost), 0)).where(SpoolUsageHistory.archive_id == archive_id)
)
total_cost = cost_result.scalar() or 0
# Fallback: if no cost found, sum by print_name and printer_id (legacy)
archive_result = await db.execute(select(PrintArchive).where(PrintArchive.id == archive_id))
archive = archive_result.scalar_one_or_none()
if archive and total_cost == 0 and archive.print_name and archive.printer_id:
legacy_cost_result = await db.execute(
select(func.coalesce(func.sum(SpoolUsageHistory.cost), 0)).where(
SpoolUsageHistory.archive_id is None,
SpoolUsageHistory.print_name == archive.print_name,
SpoolUsageHistory.printer_id == archive.printer_id,
)
)
total_cost = legacy_cost_result.scalar() or 0
if archive:
archive.cost = total_cost
await db.commit()
return results
@@ -678,6 +711,7 @@ async def _track_from_3mf(
percent_used=percent,
status=status,
cost=cost,
archive_id=archive_id,
)
db.add(history)