96 lines
3.3 KiB
Python
96 lines
3.3 KiB
Python
"""Mine Map read-model composition independent of HTTP."""
|
|
|
|
from datetime import datetime, timezone
|
|
from typing import Any
|
|
from uuid import UUID
|
|
|
|
from app.locations.models import (
|
|
LocationSummary,
|
|
MapActiveRepair,
|
|
MapPosition,
|
|
MapRoom,
|
|
MapSite,
|
|
MapVirtualMiner,
|
|
MineMapData,
|
|
MineMapMeta,
|
|
MineMapResponse,
|
|
)
|
|
from app.locations.repository import LocationMapRepository
|
|
|
|
|
|
MINER_FIELDS = {
|
|
"miner_id", "asset_id", "current_mac", "expected_mac", "current_ip", "miner_status",
|
|
"status",
|
|
"hostname", "miner_sn", "power_sn", "model", "miner_type",
|
|
"firmware_version", "lifecycle_status", "last_seen_at", "asset_state",
|
|
"observed_at", "hashrate_ths", "power_w", "chip_temp_max_c",
|
|
"chip_temp_avg_c", "hash_stable", "pool_rejected_percent",
|
|
}
|
|
|
|
|
|
class LocationMapService:
|
|
def __init__(self, repository: LocationMapRepository):
|
|
self.repository = repository
|
|
|
|
@staticmethod
|
|
def _repair(row: dict[str, Any]) -> MapActiveRepair | None:
|
|
repair_id = row.pop("active_repair_id", None)
|
|
status = row.pop("active_repair_status", None)
|
|
reason = row.pop("active_repair_reason", None)
|
|
if repair_id is None:
|
|
return None
|
|
return MapActiveRepair(id=repair_id, status=status, reason=reason)
|
|
|
|
@classmethod
|
|
def _redact(cls, row: dict[str, Any]) -> dict[str, Any]:
|
|
redacted = dict(row)
|
|
for field in MINER_FIELDS:
|
|
redacted[field] = None
|
|
redacted["active_repair_id"] = None
|
|
redacted["active_repair_status"] = None
|
|
redacted["active_repair_reason"] = None
|
|
return redacted
|
|
|
|
def get_map(
|
|
self,
|
|
*,
|
|
site_id: UUID | None = None,
|
|
room_id: UUID | None = None,
|
|
include_miner_details: bool,
|
|
) -> MineMapResponse:
|
|
rows = self.repository.read_map(site_id=site_id, room_id=room_id)
|
|
positions = []
|
|
for source in rows["positions"]:
|
|
row = dict(source) if include_miner_details else self._redact(source)
|
|
row["active_repair"] = self._repair(row)
|
|
positions.append(MapPosition.model_validate(row))
|
|
virtual_miners = []
|
|
if include_miner_details:
|
|
for source in rows["virtual_miners"]:
|
|
row = dict(source)
|
|
row["active_repair"] = self._repair(row)
|
|
virtual_miners.append(MapVirtualMiner.model_validate(row))
|
|
return MineMapResponse(
|
|
data=MineMapData(
|
|
sites=[MapSite.model_validate(row) for row in rows["sites"]],
|
|
rooms=[MapRoom.model_validate(row) for row in rows["rooms"]],
|
|
positions=positions,
|
|
virtual_miners=virtual_miners,
|
|
),
|
|
meta=MineMapMeta(
|
|
generated_at=datetime.now(timezone.utc),
|
|
site_id=site_id,
|
|
room_id=room_id,
|
|
position_count=len(positions),
|
|
virtual_miner_count=len(virtual_miners),
|
|
miner_details_included=include_miner_details,
|
|
),
|
|
)
|
|
|
|
def summarize_locations(self, *, include_miner_counts: bool) -> LocationSummary:
|
|
return LocationSummary.model_validate(
|
|
self.repository.summarize_locations(
|
|
include_miner_counts=include_miner_counts
|
|
)
|
|
)
|