Przeglądaj źródła

- Fix total print hours to include all prints

maziggy 5 miesięcy temu
rodzic
commit
3ffe032635
1 zmienionych plików z 65 dodań i 81 usunięć
  1. 65 81
      backend/app/api/routes/maintenance.py

+ 65 - 81
backend/app/api/routes/maintenance.py

@@ -2,30 +2,28 @@
 
 import logging
 from datetime import datetime
-from typing import List
 
 from fastapi import APIRouter, Depends, HTTPException
-from sqlalchemy import select, func
+from sqlalchemy import func, select
 from sqlalchemy.ext.asyncio import AsyncSession
 from sqlalchemy.orm import selectinload
 
 from backend.app.core.database import get_db
-from backend.app.models.maintenance import MaintenanceType, PrinterMaintenance, MaintenanceHistory
-from backend.app.models.printer import Printer
 from backend.app.models.archive import PrintArchive
-from backend.app.services.notification_service import notification_service
+from backend.app.models.maintenance import MaintenanceHistory, MaintenanceType, PrinterMaintenance
+from backend.app.models.printer import Printer
 from backend.app.schemas.maintenance import (
-    MaintenanceTypeCreate,
-    MaintenanceTypeUpdate,
-    MaintenanceTypeResponse,
-    PrinterMaintenanceCreate,
-    PrinterMaintenanceUpdate,
-    PrinterMaintenanceResponse,
     MaintenanceHistoryResponse,
     MaintenanceStatus,
-    PrinterMaintenanceOverview,
+    MaintenanceTypeCreate,
+    MaintenanceTypeResponse,
+    MaintenanceTypeUpdate,
     PerformMaintenanceRequest,
+    PrinterMaintenanceOverview,
+    PrinterMaintenanceResponse,
+    PrinterMaintenanceUpdate,
 )
+from backend.app.services.notification_service import notification_service
 
 logger = logging.getLogger(__name__)
 
@@ -73,20 +71,20 @@ DEFAULT_MAINTENANCE_TYPES = [
 
 
 async def get_printer_total_hours(db: AsyncSession, printer_id: int) -> float:
-    """Calculate total print hours for a printer from archives plus offset."""
-    # Get archive hours
+    """Calculate total print hours for a printer from archives plus offset.
+
+    Includes ALL prints (completed, failed, cancelled) since the printer
+    components are being used regardless of print outcome.
+    """
+    # Get archive hours (all prints, not just completed)
     result = await db.execute(
-        select(func.sum(PrintArchive.print_time_seconds))
-        .where(PrintArchive.printer_id == printer_id)
-        .where(PrintArchive.status == "completed")
+        select(func.sum(PrintArchive.print_time_seconds)).where(PrintArchive.printer_id == printer_id)
     )
     total_seconds = result.scalar() or 0
     archive_hours = total_seconds / 3600.0
 
     # Get printer offset
-    result = await db.execute(
-        select(Printer.print_hours_offset).where(Printer.id == printer_id)
-    )
+    result = await db.execute(select(Printer.print_hours_offset).where(Printer.id == printer_id))
     offset = result.scalar() or 0.0
 
     return archive_hours + offset
@@ -94,9 +92,7 @@ async def get_printer_total_hours(db: AsyncSession, printer_id: int) -> float:
 
 async def ensure_default_types(db: AsyncSession) -> None:
     """Ensure default maintenance types exist."""
-    result = await db.execute(
-        select(MaintenanceType).where(MaintenanceType.is_system == True)
-    )
+    result = await db.execute(select(MaintenanceType).where(MaintenanceType.is_system.is_(True)))
     existing = result.scalars().all()
     existing_names = {t.name for t in existing}
 
@@ -116,13 +112,12 @@ async def ensure_default_types(db: AsyncSession) -> None:
 
 # ============== Maintenance Types ==============
 
-@router.get("/types", response_model=List[MaintenanceTypeResponse])
+
+@router.get("/types", response_model=list[MaintenanceTypeResponse])
 async def get_maintenance_types(db: AsyncSession = Depends(get_db)):
     """Get all maintenance types."""
     await ensure_default_types(db)
-    result = await db.execute(
-        select(MaintenanceType).order_by(MaintenanceType.is_system.desc(), MaintenanceType.name)
-    )
+    result = await db.execute(select(MaintenanceType).order_by(MaintenanceType.is_system.desc(), MaintenanceType.name))
     return result.scalars().all()
 
 
@@ -153,9 +148,7 @@ async def update_maintenance_type(
     db: AsyncSession = Depends(get_db),
 ):
     """Update a maintenance type."""
-    result = await db.execute(
-        select(MaintenanceType).where(MaintenanceType.id == type_id)
-    )
+    result = await db.execute(select(MaintenanceType).where(MaintenanceType.id == type_id))
     maint_type = result.scalar_one_or_none()
     if not maint_type:
         raise HTTPException(status_code=404, detail="Maintenance type not found")
@@ -175,9 +168,7 @@ async def delete_maintenance_type(
     db: AsyncSession = Depends(get_db),
 ):
     """Delete a custom maintenance type."""
-    result = await db.execute(
-        select(MaintenanceType).where(MaintenanceType.id == type_id)
-    )
+    result = await db.execute(select(MaintenanceType).where(MaintenanceType.id == type_id))
     maint_type = result.scalar_one_or_none()
     if not maint_type:
         raise HTTPException(status_code=404, detail="Maintenance type not found")
@@ -192,6 +183,7 @@ async def delete_maintenance_type(
 
 # ============== Printer Maintenance ==============
 
+
 async def _get_printer_maintenance_internal(
     printer_id: int,
     db: AsyncSession,
@@ -201,9 +193,7 @@ async def _get_printer_maintenance_internal(
     await ensure_default_types(db)
 
     # Get printer
-    result = await db.execute(
-        select(Printer).where(Printer.id == printer_id)
-    )
+    result = await db.execute(select(Printer).where(Printer.id == printer_id))
     printer = result.scalar_one_or_none()
     if not printer:
         raise HTTPException(status_code=404, detail="Printer not found")
@@ -230,12 +220,12 @@ async def _get_printer_maintenance_internal(
 
     for maint_type in all_types:
         item = existing_items.get(maint_type.id)
-        default_interval_type = getattr(maint_type, 'interval_type', 'hours') or 'hours'
+        default_interval_type = getattr(maint_type, "interval_type", "hours") or "hours"
 
         if item:
             interval = item.custom_interval_hours or maint_type.default_interval_hours
             # Use custom interval type if set, otherwise use type's default
-            interval_type = getattr(item, 'custom_interval_type', None) or default_interval_type
+            interval_type = getattr(item, "custom_interval_type", None) or default_interval_type
             enabled = item.enabled
             last_performed_hours = item.last_performed_hours
             last_performed_at = item.last_performed_at
@@ -294,25 +284,27 @@ async def _get_printer_maintenance_internal(
             elif is_warning:
                 warning_count += 1
 
-        maintenance_items.append(MaintenanceStatus(
-            id=item_id,
-            printer_id=printer_id,
-            printer_name=printer.name,
-            maintenance_type_id=maint_type.id,
-            maintenance_type_name=maint_type.name,
-            maintenance_type_icon=maint_type.icon,
-            enabled=enabled,
-            interval_hours=interval,
-            interval_type=interval_type,
-            current_hours=total_hours,
-            hours_since_maintenance=hours_since,
-            hours_until_due=hours_until,
-            days_since_maintenance=days_since if interval_type == "days" else None,
-            days_until_due=days_until if interval_type == "days" else None,
-            is_due=is_due,
-            is_warning=is_warning,
-            last_performed_at=last_performed_at,
-        ))
+        maintenance_items.append(
+            MaintenanceStatus(
+                id=item_id,
+                printer_id=printer_id,
+                printer_name=printer.name,
+                maintenance_type_id=maint_type.id,
+                maintenance_type_name=maint_type.name,
+                maintenance_type_icon=maint_type.icon,
+                enabled=enabled,
+                interval_hours=interval,
+                interval_type=interval_type,
+                current_hours=total_hours,
+                hours_since_maintenance=hours_since,
+                hours_until_due=hours_until,
+                days_since_maintenance=days_since if interval_type == "days" else None,
+                days_until_due=days_until if interval_type == "days" else None,
+                is_due=is_due,
+                is_warning=is_warning,
+                last_performed_at=last_performed_at,
+            )
+        )
 
     if commit:
         await db.commit()
@@ -336,14 +328,12 @@ async def get_printer_maintenance(
     return await _get_printer_maintenance_internal(printer_id, db, commit=True)
 
 
-@router.get("/overview", response_model=List[PrinterMaintenanceOverview])
+@router.get("/overview", response_model=list[PrinterMaintenanceOverview])
 async def get_all_maintenance_overview(db: AsyncSession = Depends(get_db)):
     """Get maintenance overview for all active printers."""
     await ensure_default_types(db)
 
-    result = await db.execute(
-        select(Printer).where(Printer.is_active == True)
-    )
+    result = await db.execute(select(Printer).where(Printer.is_active.is_(True)))
     printers = result.scalars().all()
 
     overviews = []
@@ -400,9 +390,7 @@ async def perform_maintenance(
         raise HTTPException(status_code=404, detail="Maintenance item not found")
 
     # Get printer for name
-    result = await db.execute(
-        select(Printer).where(Printer.id == item.printer_id)
-    )
+    result = await db.execute(select(Printer).where(Printer.id == item.printer_id))
     printer = result.scalar_one()
 
     # Get current hours
@@ -424,7 +412,7 @@ async def perform_maintenance(
 
     # Calculate status
     interval = item.custom_interval_hours or item.maintenance_type.default_interval_hours
-    interval_type = getattr(item.maintenance_type, 'interval_type', 'hours') or 'hours'
+    interval_type = getattr(item.maintenance_type, "interval_type", "hours") or "hours"
     hours_since = current_hours - item.last_performed_hours
     hours_until = interval - hours_since
 
@@ -449,7 +437,7 @@ async def perform_maintenance(
     )
 
 
-@router.get("/items/{item_id}/history", response_model=List[MaintenanceHistoryResponse])
+@router.get("/items/{item_id}/history", response_model=list[MaintenanceHistoryResponse])
 async def get_maintenance_history(
     item_id: int,
     db: AsyncSession = Depends(get_db),
@@ -468,9 +456,7 @@ async def get_maintenance_summary(db: AsyncSession = Depends(get_db)):
     """Get a summary of maintenance status across all printers."""
     await ensure_default_types(db)
 
-    result = await db.execute(
-        select(Printer).where(Printer.is_active == True)
-    )
+    result = await db.execute(select(Printer).where(Printer.is_active.is_(True)))
     printers = result.scalars().all()
 
     total_due = 0
@@ -482,12 +468,14 @@ async def get_maintenance_summary(db: AsyncSession = Depends(get_db)):
         total_due += overview.due_count
         total_warning += overview.warning_count
         if overview.due_count > 0 or overview.warning_count > 0:
-            printers_with_issues.append({
-                "printer_id": printer.id,
-                "printer_name": printer.name,
-                "due_count": overview.due_count,
-                "warning_count": overview.warning_count,
-            })
+            printers_with_issues.append(
+                {
+                    "printer_id": printer.id,
+                    "printer_name": printer.name,
+                    "due_count": overview.due_count,
+                    "warning_count": overview.warning_count,
+                }
+            )
 
     return {
         "total_due": total_due,
@@ -504,9 +492,7 @@ async def set_printer_hours(
 ):
     """Set the total print hours for a printer (adjusts offset to match)."""
     # Get printer
-    result = await db.execute(
-        select(Printer).where(Printer.id == printer_id)
-    )
+    result = await db.execute(select(Printer).where(Printer.id == printer_id))
     printer = result.scalar_one_or_none()
     if not printer:
         raise HTTPException(status_code=404, detail="Printer not found")
@@ -541,9 +527,7 @@ async def set_printer_hours(
         ]
 
         if items_needing_attention:
-            await notification_service.on_maintenance_due(
-                printer_id, printer.name, items_needing_attention, db
-            )
+            await notification_service.on_maintenance_due(printer_id, printer.name, items_needing_attention, db)
             logger.info(
                 f"Sent maintenance notification for printer {printer_id}: "
                 f"{len(items_needing_attention)} items need attention"