feat: add operational Dashboard
This commit is contained in:
1 parent
4a1959ecd6
commit
b8b741ccee
23 files changed
+1278
-59
No files matched your search
@@ -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
|
||||
@@ -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,
|
||||
*,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user