feat: add Fleet operations view

This commit is contained in:
lussierj committed 2026-08-11 17:37:36 +00:00
1 parent c6d01df31c
commit 786a1ab910
12 files changed
+1062 -148

No files matched your search

+179 -11
View File
@@ -59,6 +59,101 @@ MINER_SELECT = f"""
) tag_data ON true
"""
ACTIVE_REPAIR_STATUSES = (
"OPEN",
"IN_PROGRESS",
"WAITING_FOR_PARTS",
"READY_FOR_TEST",
)
OPERATIONAL_STATE_SQL = """
CASE
WHEN active_repair.id IS NOT NULL OR miners.lifecycle_status = 'in_repair'
THEN 'repair'
WHEN miners.lifecycle_status = 'retired' THEN 'retired'
WHEN miners.lifecycle_status = 'spare' THEN 'spare'
WHEN lower(trim(miners.status)) = 'offline' THEN 'offline'
WHEN latest.hash_stable = false THEN 'unstable'
WHEN lower(trim(miners.status)) = 'online' THEN 'online'
ELSE 'unknown'
END
"""
FLEET_SELECT = f"""
SELECT
miners.id, miners.asset_tag, miners.current_mac,
host(miners.current_ip) AS current_ip, miners.model, miners.miner_sn,
miners.lifecycle_status, miners.status, miners.created_at,
miners.updated_at, miners.last_seen_at, tag_data.tags,
latest.observed_at, latest.hashrate_ths, latest.power_w,
latest.chip_temp_max_c, latest.hash_stable,
active_repair.id AS active_repair_id,
active_repair.status AS active_repair_status,
active_repair.reason AS active_repair_reason,
{{location_fields}}
FROM miners
LEFT JOIN LATERAL (
SELECT COALESCE(
jsonb_agg(
jsonb_build_object(
'id', tags.id, 'name', tags.name,
'created_at', tags.created_at, 'updated_at', tags.updated_at
) ORDER BY lower({TAG_TRIMMED_NAME_SQL}), tags.id
), '[]'::jsonb
) AS tags
FROM miner_tags JOIN tags ON tags.id = miner_tags.tag_id
WHERE miner_tags.miner_id = miners.id
) tag_data ON true
LEFT JOIN LATERAL (
SELECT snapshots.observed_at,
ROUND((snapshots.mhs_av / 1000000.0)::numeric, 2)::float AS hashrate_ths,
ROUND(snapshots.power_w::numeric, 2)::float AS power_w,
ROUND(snapshots.chip_temp_max_c::numeric, 2)::float AS chip_temp_max_c,
snapshots.hash_stable
FROM miner_status_snapshots snapshots
WHERE snapshots.miner_id = miners.id
ORDER BY snapshots.observed_at DESC, snapshots.id DESC
LIMIT 1
) latest ON true
LEFT JOIN LATERAL (
SELECT repairs.id, repairs.status, repairs.reason
FROM repairs
WHERE repairs.miner_id = miners.id AND repairs.status = ANY(%s)
ORDER BY repairs.opened_at, repairs.id
LIMIT 1
) active_repair ON true
{{location_join}}
"""
LOCATION_FIELDS = """
mapped.site_id, mapped.site_name, mapped.room_id, mapped.room_name,
mapped.location_position_id, mapped.rack_label, mapped.shelf_label,
mapped.position_label, mapped.position_index
"""
REDACTED_LOCATION_FIELDS = """
NULL::uuid AS site_id, NULL::text AS site_name,
NULL::uuid AS room_id, NULL::text AS room_name,
NULL::uuid AS location_position_id, NULL::text AS rack_label,
NULL::text AS shelf_label, NULL::text AS position_label,
NULL::integer AS position_index
"""
LOCATION_JOIN = """
LEFT JOIN LATERAL (
SELECT sites.id AS site_id, sites.name AS site_name, rooms.id AS room_id,
COALESCE(rooms.name, lp.room_name) AS room_name,
lp.id AS location_position_id, lp.rack_name AS rack_label,
lp.shelf_name AS shelf_label, lp.position_label, lp.position_index
FROM location_positions lp
LEFT JOIN rooms ON rooms.id = lp.room_id
LEFT JOIN sites ON sites.id = rooms.site_id
WHERE lp.miner_id = miners.id
ORDER BY lp.id
LIMIT 1
) mapped ON true
"""
MINER_WRITE_COLUMNS = {
"site_id",
"room_id",
@@ -184,8 +279,18 @@ class InventoryRepository:
(asset_id,),
)
def list_miners(self, *, limit: int = 100, offset: int = 0) -> list[dict]:
return self.search_miners(limit=limit, offset=offset)
def list_miners(
self,
*,
limit: int = 100,
offset: int = 0,
include_location_details: bool = False,
) -> list[dict]:
return self.search_miners(
limit=limit,
offset=offset,
include_location_details=include_location_details,
)
def search_miners(
self,
@@ -193,12 +298,20 @@ class InventoryRepository:
query: str | None = None,
asset_id: str | None = None,
lifecycle_status: str | None = None,
model: str | None = None,
runtime_status: str | None = None,
repair: str | None = None,
operational_state: str | None = None,
problems: bool = False,
site_id: UUID | None = None,
room_id: UUID | None = None,
tag_names: Sequence[str] = (),
limit: int = 100,
offset: int = 0,
include_location_details: bool = False,
) -> list[dict]:
conditions: list[str] = []
params: list[Any] = []
params: list[Any] = [list(ACTIVE_REPAIR_STATUSES)]
if query:
pattern = f"%{query}%"
@@ -217,15 +330,36 @@ class InventoryRepository:
"""
)
params.extend([pattern] * 8)
if asset_id:
conditions.append("lower(trim(miners.asset_tag)) = lower(trim(%s))")
params.append(asset_id)
if lifecycle_status:
conditions.append("miners.lifecycle_status = %s")
params.append(lifecycle_status)
if model:
conditions.append("lower(trim(miners.model)) = lower(trim(%s))")
params.append(model)
if runtime_status:
conditions.append("lower(trim(miners.status)) = lower(trim(%s))")
params.append(runtime_status)
if repair == "active":
conditions.append("active_repair.id IS NOT NULL")
elif repair == "none":
conditions.append("active_repair.id IS NULL")
if operational_state:
conditions.append(f"({OPERATIONAL_STATE_SQL}) = %s")
params.append(operational_state)
if problems:
conditions.append(
f"({OPERATIONAL_STATE_SQL}) = ANY(%s)"
)
params.append(["offline", "unknown", "unstable", "repair"])
if site_id:
conditions.append("mapped.site_id = %s")
params.append(site_id)
if room_id:
conditions.append("mapped.room_id = %s")
params.append(room_id)
for tag_name in tag_names:
conditions.append(
f"""
@@ -241,6 +375,14 @@ class InventoryRepository:
)
params.append(tag_name)
location_fields = (
LOCATION_FIELDS if include_location_details else REDACTED_LOCATION_FIELDS
)
location_join = LOCATION_JOIN if include_location_details else ""
select_sql = FLEET_SELECT.format(
location_fields=location_fields,
location_join=location_join,
)
where_sql = " AND ".join(conditions) if conditions else "true"
params.extend([limit, offset])
@@ -248,18 +390,44 @@ class InventoryRepository:
with connection.cursor(row_factory=dict_row) as cur:
cur.execute(
f"""
{MINER_SELECT}
{select_sql}
WHERE {where_sql}
ORDER BY
lower(trim(miners.asset_tag)) NULLS LAST,
miners.created_at,
miners.id
ORDER BY lower(trim(miners.asset_tag)) NULLS LAST,
miners.created_at, miners.id
LIMIT %s OFFSET %s
""",
params,
)
return [dict(row) for row in cur.fetchall()]
def list_location_filter_options(self) -> dict[str, list[dict]]:
with self._connection_scope() as connection:
with connection.cursor(row_factory=dict_row) as cur:
cur.execute(
"""
SELECT sites.id, sites.name
FROM location_positions lp
JOIN miners ON miners.id = lp.miner_id
JOIN rooms ON rooms.id = lp.room_id
JOIN sites ON sites.id = rooms.site_id
GROUP BY sites.id, sites.name
ORDER BY lower(sites.name), sites.id
"""
)
sites = [dict(row) for row in cur.fetchall()]
cur.execute(
"""
SELECT rooms.id, rooms.site_id, rooms.name
FROM location_positions lp
JOIN miners ON miners.id = lp.miner_id
JOIN rooms ON rooms.id = lp.room_id
GROUP BY rooms.id, rooms.site_id, rooms.name
ORDER BY lower(rooms.name), rooms.id
"""
)
rooms = [dict(row) for row in cur.fetchall()]
return {"sites": sites, "rooms": rooms}
def create_miner(self, values: Mapping[str, Any]) -> dict:
clean_values = {
key: value for key, value in values.items() if key in MINER_WRITE_COLUMNS