feat: add operational Dashboard

This commit is contained in:
lussierj committed 2026-08-17 17:11:40 +00:00
1 parent 4a1959ecd6
commit b8b741ccee
23 files changed
+1278 -59

No files matched your search

+18
View File
@@ -171,3 +171,21 @@ class InventoryAssetIDHistoryResponse(BaseModel):
data: list[InventoryAssetIDHistoryEntry]
meta: dict[str, object] = Field(default_factory=dict)
error: None = None
class InventoryFleetAttentionSummary(BaseModel):
offline: int
unknown: int
unstable: int
repair: int
class InventoryFleetSummary(BaseModel):
total: int
runtime_online: int
runtime_offline: int
runtime_unknown: int
lifecycle_in_repair: int
lifecycle_spare: int
lifecycle_retired: int
attention: InventoryFleetAttentionSummary
+64
View File
@@ -292,6 +292,70 @@ class InventoryRepository:
include_location_details=include_location_details,
)
def summarize_fleet(self) -> dict[str, Any]:
"""Return exact fleet-wide counts using Inventory state semantics."""
with self._connection_scope() as connection:
with connection.cursor(row_factory=dict_row) as cur:
cur.execute(
f"""
WITH fleet_state AS (
SELECT miners.status, miners.lifecycle_status,
({OPERATIONAL_STATE_SQL}) AS operational_state
FROM miners
LEFT JOIN LATERAL (
SELECT snapshots.hash_stable
FROM miner_status_snapshots AS snapshots
WHERE snapshots.miner_id = miners.id
ORDER BY snapshots.observed_at DESC, snapshots.id DESC
LIMIT 1
) AS latest ON true
LEFT JOIN LATERAL (
SELECT repairs.id
FROM repairs
WHERE repairs.miner_id = miners.id
AND repairs.status = ANY(%s)
ORDER BY repairs.opened_at, repairs.id
LIMIT 1
) AS active_repair ON true
)
SELECT count(*)::integer AS total,
count(*) FILTER (
WHERE lower(trim(status)) = 'online'
)::integer AS runtime_online,
count(*) FILTER (
WHERE lower(trim(status)) = 'offline'
)::integer AS runtime_offline,
count(*) FILTER (
WHERE lower(trim(status)) NOT IN ('online', 'offline')
OR status IS NULL
)::integer AS runtime_unknown,
count(*) FILTER (
WHERE lifecycle_status = 'in_repair'
)::integer AS lifecycle_in_repair,
count(*) FILTER (
WHERE lifecycle_status = 'spare'
)::integer AS lifecycle_spare,
count(*) FILTER (
WHERE lifecycle_status = 'retired'
)::integer AS lifecycle_retired,
count(*) FILTER (
WHERE operational_state = 'offline'
)::integer AS attention_offline,
count(*) FILTER (
WHERE operational_state = 'unknown'
)::integer AS attention_unknown,
count(*) FILTER (
WHERE operational_state = 'unstable'
)::integer AS attention_unstable,
count(*) FILTER (
WHERE operational_state = 'repair'
)::integer AS attention_repair
FROM fleet_state
""",
(list(ACTIVE_REPAIR_STATUSES),),
)
return dict(cur.fetchone())
def search_miners(
self,
*,
+20
View File
@@ -14,6 +14,8 @@ from app.inventory.exceptions import (
from app.inventory.models import (
InventoryAssetIDHistoryEntry,
InventoryCreateRequest,
InventoryFleetAttentionSummary,
InventoryFleetSummary,
InventoryMetadataUpdateRequest,
InventoryMinerDetail,
InventoryMinerSummary,
@@ -157,6 +159,24 @@ class InventoryService:
for row in self.repository.list_miners(limit=limit, offset=offset)
]
def summarize_fleet(self) -> InventoryFleetSummary:
row = self.repository.summarize_fleet()
return InventoryFleetSummary(
total=row["total"],
runtime_online=row["runtime_online"],
runtime_offline=row["runtime_offline"],
runtime_unknown=row["runtime_unknown"],
lifecycle_in_repair=row["lifecycle_in_repair"],
lifecycle_spare=row["lifecycle_spare"],
lifecycle_retired=row["lifecycle_retired"],
attention=InventoryFleetAttentionSummary(
offline=row["attention_offline"],
unknown=row["attention_unknown"],
unstable=row["attention_unstable"],
repair=row["attention_repair"],
),
)
def search_miners(
self,
filters: InventorySearchFilters,