- Added maintenance interval type clendar days

- Minor improvements to maintenance module
This commit is contained in:
maziggy
2025-12-08 14:52:16 +00:00
parent 75776fe509
commit 1a17b547dc
11 changed files with 9169 additions and 8853 deletions
+44 -5
View File
@@ -136,6 +136,7 @@ async def create_maintenance_type(
name=data.name,
description=data.description,
default_interval_hours=data.default_interval_hours,
interval_type=data.interval_type,
icon=data.icon,
is_system=False,
)
@@ -225,11 +226,16 @@ async def _get_printer_maintenance_internal(
due_count = 0
warning_count = 0
now = datetime.utcnow()
for maint_type in all_types:
item = existing_items.get(maint_type.id)
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
enabled = item.enabled
last_performed_hours = item.last_performed_hours
last_performed_at = item.last_performed_at
@@ -246,15 +252,41 @@ async def _get_printer_maintenance_internal(
await db.flush()
interval = maint_type.default_interval_hours
interval_type = default_interval_type
enabled = True
last_performed_hours = 0.0
last_performed_at = None
item_id = item.id
hours_since = total_hours - last_performed_hours
hours_until = interval - hours_since
is_due = hours_until <= 0
is_warning = hours_until <= (interval * 0.1) and not is_due
# Calculate status based on interval type
if interval_type == "days":
# Time-based: calculate days since last performed
if last_performed_at:
days_since = (now - last_performed_at).total_seconds() / 86400.0
else:
# Never performed - consider it due
days_since = interval + 1
days_until = interval - days_since
is_due = days_until <= 0
is_warning = days_until <= (interval * 0.1) and not is_due
# For compatibility, also set hours values (but they won't be primary)
hours_since = total_hours - last_performed_hours
hours_until = 0 # Not applicable for time-based
else:
# Print-hours based (default)
hours_since = total_hours - last_performed_hours
hours_until = interval - hours_since
is_due = hours_until <= 0
is_warning = hours_until <= (interval * 0.1) and not is_due
# Calculate days for reference
if last_performed_at:
days_since = (now - last_performed_at).total_seconds() / 86400.0
else:
days_since = None
days_until = None
if enabled:
if is_due:
@@ -271,9 +303,12 @@ async def _get_printer_maintenance_internal(
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,
@@ -389,6 +424,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'
hours_since = current_hours - item.last_performed_hours
hours_until = interval - hours_since
@@ -401,9 +437,12 @@ async def perform_maintenance(
maintenance_type_icon=item.maintenance_type.icon,
enabled=item.enabled,
interval_hours=interval,
interval_type=interval_type,
current_hours=current_hours,
hours_since_maintenance=hours_since,
hours_until_due=hours_until,
hours_until_due=hours_until if interval_type == "hours" else 0,
days_since_maintenance=0 if interval_type == "days" else None,
days_until_due=interval if interval_type == "days" else None,
is_due=False,
is_warning=False,
last_performed_at=item.last_performed_at,
+18
View File
@@ -109,3 +109,21 @@ async def run_migrations(conn):
except Exception:
# Column already exists
pass
# Migration: Add interval_type column to maintenance_types
try:
await conn.execute(text(
"ALTER TABLE maintenance_types ADD COLUMN interval_type VARCHAR(20) DEFAULT 'hours'"
))
except Exception:
# Column already exists
pass
# Migration: Add custom_interval_type column to printer_maintenance
try:
await conn.execute(text(
"ALTER TABLE printer_maintenance ADD COLUMN custom_interval_type VARCHAR(20)"
))
except Exception:
# Column already exists
pass
+4
View File
@@ -15,6 +15,8 @@ class MaintenanceType(Base):
name: Mapped[str] = mapped_column(String(100))
description: Mapped[str | None] = mapped_column(Text)
default_interval_hours: Mapped[float] = mapped_column(Float, default=100.0)
# Interval type: "hours" (print hours) or "days" (calendar days)
interval_type: Mapped[str] = mapped_column(String(20), default="hours")
icon: Mapped[str | None] = mapped_column(String(50)) # Icon name for UI
is_system: Mapped[bool] = mapped_column(Boolean, default=False) # Pre-defined vs custom
created_at: Mapped[datetime] = mapped_column(
@@ -37,6 +39,8 @@ class PrinterMaintenance(Base):
# Custom interval for this printer (overrides default if set)
custom_interval_hours: Mapped[float | None] = mapped_column(Float, nullable=True)
# Custom interval type for this printer (overrides default if set)
custom_interval_type: Mapped[str | None] = mapped_column(String(20), nullable=True)
# Tracking
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
+15 -4
View File
@@ -9,6 +9,8 @@ class MaintenanceTypeBase(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
description: str | None = None
default_interval_hours: float = Field(default=100.0, ge=1.0)
# "hours" = print hours, "days" = calendar days
interval_type: str = Field(default="hours", pattern="^(hours|days)$")
icon: str | None = None
@@ -20,6 +22,7 @@ class MaintenanceTypeUpdate(BaseModel):
name: str | None = None
description: str | None = None
default_interval_hours: float | None = Field(default=None, ge=1.0)
interval_type: str | None = Field(default=None, pattern="^(hours|days)$")
icon: str | None = None
@@ -46,6 +49,7 @@ class PrinterMaintenanceCreate(PrinterMaintenanceBase):
class PrinterMaintenanceUpdate(BaseModel):
custom_interval_hours: float | None = None
custom_interval_type: str | None = Field(default=None, pattern="^(hours|days)$")
enabled: bool | None = None
@@ -94,12 +98,19 @@ class MaintenanceStatus(BaseModel):
maintenance_type_name: str
maintenance_type_icon: str | None
enabled: bool
interval_hours: float # custom or default
# Interval configuration
interval_hours: float # custom or default (hours for print-based, days for time-based)
interval_type: str # "hours" or "days"
# For print-hour based maintenance
current_hours: float # total print hours for printer
hours_since_maintenance: float # current - last_performed
hours_until_due: float # interval - hours_since
is_due: bool # hours_until_due <= 0
is_warning: bool # hours_until_due <= 10% of interval
hours_until_due: float # interval - hours_since (for hours type)
# For time-based maintenance
days_since_maintenance: float | None # days since last performed
days_until_due: float | None # for days type
# Status flags
is_due: bool # hours_until_due <= 0 OR days_until_due <= 0
is_warning: bool # within 10% of interval
last_performed_at: datetime | None
+7 -2
View File
@@ -721,6 +721,7 @@ export interface MaintenanceType {
name: string;
description: string | null;
default_interval_hours: number;
interval_type: 'hours' | 'days'; // "hours" = print hours, "days" = calendar days
icon: string | null;
is_system: boolean;
created_at: string;
@@ -730,6 +731,7 @@ export interface MaintenanceTypeCreate {
name: string;
description?: string | null;
default_interval_hours?: number;
interval_type?: 'hours' | 'days';
icon?: string | null;
}
@@ -741,10 +743,13 @@ export interface MaintenanceStatus {
maintenance_type_name: string;
maintenance_type_icon: string | null;
enabled: boolean;
interval_hours: number;
interval_hours: number; // For hours type: print hours; for days type: number of days
interval_type: 'hours' | 'days';
current_hours: number;
hours_since_maintenance: number;
hours_until_due: number;
days_since_maintenance: number | null; // For days type
days_until_due: number | null; // For days type
is_due: boolean;
is_warning: boolean;
last_performed_at: string | null;
@@ -1274,7 +1279,7 @@ export const api = {
getMaintenanceOverview: () => request<PrinterMaintenanceOverview[]>('/maintenance/overview'),
getPrinterMaintenance: (printerId: number) =>
request<PrinterMaintenanceOverview>(`/maintenance/printers/${printerId}`),
updateMaintenanceItem: (itemId: number, data: { custom_interval_hours?: number | null; enabled?: boolean }) =>
updateMaintenanceItem: (itemId: number, data: { custom_interval_hours?: number | null; custom_interval_type?: 'hours' | 'days' | null; enabled?: boolean }) =>
request<MaintenanceStatus>(`/maintenance/items/${itemId}`, {
method: 'PATCH',
body: JSON.stringify(data),
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -7,8 +7,8 @@
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/img/apple-touch-icon.png" />
<script type="module" crossorigin src="/assets/index-BOLoyzZ9.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DO3k-CBG.css">
<script type="module" crossorigin src="/assets/index-6YKRUSex.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BP1T2ML7.css">
</head>
<body>
<div id="root"></div>