Browse Source

Add archive_id to SpoolUsageHistory model and update cost calculations in usage_tracker

Matteo Parenti 3 months ago
parent
commit
427b08f9f6

+ 21 - 10
backend/app/api/routes/archives.py

@@ -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

+ 1 - 0
backend/app/models/spool_usage_history.py

@@ -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 - 0
backend/app/services/usage_tracker.py

@@ -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)