feat: add miner detail view
This commit is contained in:
1 parent
b8b741ccee
commit
19fc29c72c
22 files changed
+1882
-10
No files matched your search
@@ -0,0 +1,152 @@
|
||||
"""Composition service for the Internal Alpha Miner Detail overview."""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from math import isfinite
|
||||
from uuid import UUID
|
||||
|
||||
from app.inventory.models import InventoryMinerDetail
|
||||
from app.inventory.service import InventoryService
|
||||
from app.locations.service import LocationMapService
|
||||
from app.miner_detail.models import (
|
||||
MinerDetailAccess,
|
||||
MinerDetailCapabilities,
|
||||
MinerDetailLocationSection,
|
||||
MinerDetailOverviewData,
|
||||
MinerDetailOverviewMeta,
|
||||
MinerDetailOverviewResponse,
|
||||
MinerDetailRepairEpisode,
|
||||
MinerDetailRepairSection,
|
||||
OperationalState,
|
||||
)
|
||||
from app.repairs.center_service import RepairCenterService
|
||||
from app.repairs.models import RepairCenterAccess, RepairCenterItem
|
||||
|
||||
|
||||
class MinerDetailService:
|
||||
"""Coordinate domain-owned current projections without transport logic."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
inventory_service: InventoryService,
|
||||
location_service: LocationMapService,
|
||||
repair_service: RepairCenterService,
|
||||
):
|
||||
self.inventory_service = inventory_service
|
||||
self.location_service = location_service
|
||||
self.repair_service = repair_service
|
||||
|
||||
@staticmethod
|
||||
def _episode(item: RepairCenterItem) -> MinerDetailRepairEpisode:
|
||||
return MinerDetailRepairEpisode(
|
||||
repair_id=item.repair_id,
|
||||
source=item.source,
|
||||
active=item.active,
|
||||
status=item.repair_status,
|
||||
reason=item.reason,
|
||||
opened_at=item.opened_at,
|
||||
closed_at=item.closed_at,
|
||||
latest_context=item.latest_context,
|
||||
latest_context_at=item.latest_context_at,
|
||||
parts_count=item.parts_count,
|
||||
parts_summary=item.parts_summary,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _operational_state(
|
||||
miner: InventoryMinerDetail,
|
||||
active_repair: MinerDetailRepairEpisode | None,
|
||||
) -> OperationalState:
|
||||
if active_repair is not None or miner.lifecycle_status == "in_repair":
|
||||
return "repair"
|
||||
if miner.lifecycle_status == "retired":
|
||||
return "retired"
|
||||
if miner.lifecycle_status == "spare":
|
||||
return "spare"
|
||||
runtime = (miner.runtime_status or "").strip().lower()
|
||||
if runtime == "offline":
|
||||
return "offline"
|
||||
if miner.hash_stable is False:
|
||||
return "unstable"
|
||||
if runtime == "online":
|
||||
return "online"
|
||||
return "unknown"
|
||||
|
||||
def get_overview(
|
||||
self,
|
||||
miner_id: UUID,
|
||||
access: MinerDetailAccess,
|
||||
) -> MinerDetailOverviewResponse:
|
||||
miner = self.inventory_service.get_current_miner(miner_id)
|
||||
# Location is owned by the permission-aware section below. Keep the
|
||||
# shared projection from leaking location identifiers. Non-finite
|
||||
# telemetry is not a factual display value and remains unavailable.
|
||||
redacted = {
|
||||
field: None
|
||||
for field in (
|
||||
"site_id", "site_name", "room_id", "room_name",
|
||||
"location_position_id", "rack_label", "shelf_label",
|
||||
"position_label", "position_index",
|
||||
)
|
||||
}
|
||||
for field in ("hashrate_ths", "power_w", "chip_temp_max_c"):
|
||||
value = getattr(miner, field)
|
||||
if value is not None and not isfinite(value):
|
||||
redacted[field] = None
|
||||
miner = miner.model_copy(
|
||||
update=redacted
|
||||
)
|
||||
location = (
|
||||
self.location_service.get_miner_location(miner_id)
|
||||
if access.can_view_locations
|
||||
else None
|
||||
)
|
||||
repair_items = (
|
||||
self.repair_service.list_miner_repairs(
|
||||
miner_id,
|
||||
RepairCenterAccess(
|
||||
can_read_repairs=True,
|
||||
can_view_miners=True,
|
||||
),
|
||||
)
|
||||
if access.can_read_repairs
|
||||
else []
|
||||
)
|
||||
episodes = [self._episode(item) for item in repair_items]
|
||||
active_repair = next(
|
||||
(
|
||||
episode for episode in episodes
|
||||
if episode.active and episode.source == "modular"
|
||||
),
|
||||
None,
|
||||
)
|
||||
query_count = 1 + int(access.can_view_locations) + int(access.can_read_repairs)
|
||||
return MinerDetailOverviewResponse(
|
||||
data=MinerDetailOverviewData(
|
||||
miner=miner,
|
||||
operational_state=self._operational_state(miner, active_repair),
|
||||
location=MinerDetailLocationSection(
|
||||
available=access.can_view_locations,
|
||||
restricted=not access.can_view_locations,
|
||||
location=location,
|
||||
),
|
||||
repair=MinerDetailRepairSection(
|
||||
available=access.can_read_repairs,
|
||||
restricted=not access.can_read_repairs,
|
||||
active_repair=active_repair,
|
||||
episodes=episodes,
|
||||
),
|
||||
capabilities=MinerDetailCapabilities(
|
||||
can_view_map=bool(
|
||||
access.can_view_locations and location is not None
|
||||
and location.site_id and location.room_id
|
||||
),
|
||||
can_open_repair_center=access.can_view_repairs,
|
||||
can_read_repair_history=access.can_read_repairs,
|
||||
can_read_components=access.can_read_components,
|
||||
),
|
||||
),
|
||||
meta=MinerDetailOverviewMeta(
|
||||
generated_at=datetime.now(timezone.utc),
|
||||
database_query_count=query_count,
|
||||
),
|
||||
)
|
||||
Reference in new issue
Block a user