mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 19:21:33 +02:00
- Fix total print hours to include all prints
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user