Files
minerops/backend/app/repairs/models.py
T

197 lines
5.1 KiB
Python

"""Typed contracts for the Repairs domain."""
from datetime import datetime
from typing import Any, Literal
from uuid import UUID
from pydantic import AwareDatetime, BaseModel, ConfigDict, Field
RepairReason = Literal[
"HARDWARE_FAILURE",
"LOW_HASHRATE",
"CONTROL_BOARD",
"HASHBOARD",
"PSU",
"FAN",
"OVERHEATING",
"NETWORKING",
"PREVENTIVE_MAINTENANCE",
"INSPECTION",
"OTHER",
]
RepairStatus = Literal[
"OPEN",
"IN_PROGRESS",
"WAITING_FOR_PARTS",
"READY_FOR_TEST",
"COMPLETED",
"CANCELLED",
]
RepairEventType = Literal[
"REPAIR_STARTED",
"INITIAL_NOTE",
"REPAIR_COMPLETED",
"CONTROL_BOARD_REPLACED",
]
RepairEligibleLifecycle = Literal["active", "spare"]
RepairPostLifecycle = Literal["active", "spare"]
RepairMinerLifecycle = Literal["active", "spare", "retired", "in_repair"]
class BeginRepairRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
miner_id: UUID
reason: RepairReason
notes: str | None = Field(default=None, max_length=4000)
class RepairCompleteRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
target_lifecycle: RepairPostLifecycle
completion_summary: str | None = Field(default=None, max_length=4000)
ControlBoardReplacementReason = Literal[
"failed",
"upgrade",
"diagnostic_swap",
"preventive_maintenance",
"temporary_donor",
"unknown",
]
RemovedBoardDisposition = Literal[
"repair_inventory",
"sent_for_repair",
"scrapped",
"retained_for_diagnosis",
"returned_to_vendor",
"unknown",
]
class ControlBoardReplacementRequest(BaseModel):
model_config = ConfigDict(extra="forbid")
new_mac_address: str = Field(min_length=1, max_length=64)
new_control_board_serial: str | None = Field(..., max_length=255)
board_model: str | None = Field(default=None, max_length=255)
board_revision: str | None = Field(default=None, max_length=255)
firmware_version: str | None = Field(default=None, max_length=255)
removed_board_disposition: RemovedBoardDisposition
replacement_reason: ControlBoardReplacementReason
notes: str | None = Field(default=None, max_length=4000)
class RepairStartedPayload(BaseModel):
reason: RepairReason
from_lifecycle: RepairEligibleLifecycle
to_lifecycle: Literal["in_repair"] = "in_repair"
class InitialRepairNotePayload(BaseModel):
note: str = Field(min_length=1, max_length=4000)
class RepairCompletedPayload(BaseModel):
previous_miner_lifecycle: Literal["in_repair"] = "in_repair"
resulting_miner_lifecycle: RepairPostLifecycle
pre_repair_lifecycle: RepairEligibleLifecycle | None = None
completion_summary: str | None = Field(default=None, max_length=4000)
class ControlBoardReplacedPayload(BaseModel):
old_mac_address: str | None
new_mac_address: str
old_control_board_serial: str | None
new_control_board_serial: str | None
old_board_model: str | None = None
new_board_model: str | None = None
old_board_revision: str | None = None
new_board_revision: str | None = None
new_firmware_version: str | None = None
removed_board_disposition: RemovedBoardDisposition
replacement_reason: ControlBoardReplacementReason
notes: str | None = Field(default=None, max_length=4000)
class ControlBoardReplacementResult(BaseModel):
miner_id: UUID
asset_id: str | None = None
repair_id: UUID
installation_id: UUID
event_id: UUID
event_sequence: int = Field(ge=1)
current_mac: str
control_board_serial: str | None
replaced_at: AwareDatetime
replaced_by: UUID
class ControlBoardReplacementResponse(BaseModel):
data: ControlBoardReplacementResult
meta: dict[str, object] = Field(default_factory=dict)
error: None = None
class BeginRepairResult(BaseModel):
repair_id: UUID
miner_id: UUID
status: Literal["OPEN"]
reason: RepairReason
opened_at: datetime
opened_by: UUID
class BeginRepairResponse(BaseModel):
data: BeginRepairResult
meta: dict[str, object] = Field(default_factory=dict)
error: None = None
class RepairActor(BaseModel):
user_id: UUID
display_name: str
class RepairDetail(BaseModel):
repair_id: UUID
miner_id: UUID
miner_asset_id: str | None = None
status: RepairStatus
reason: RepairReason
opened_at: AwareDatetime
opened_by: RepairActor
closed_at: AwareDatetime | None = None
closed_by: RepairActor | None = None
miner_lifecycle: RepairMinerLifecycle
latest_event_sequence: int | None = Field(default=None, ge=1)
class RepairTimelineEvent(BaseModel):
event_id: UUID
sequence: int = Field(ge=1)
event_type: RepairEventType
actor: RepairActor
occurred_at: AwareDatetime
payload: dict[str, Any]
class RepairTimeline(BaseModel):
repair_id: UUID
events: list[RepairTimelineEvent] = Field(default_factory=list)
class RepairDetailResponse(BaseModel):
data: RepairDetail
meta: dict[str, object] = Field(default_factory=dict)
error: None = None
class RepairTimelineResponse(BaseModel):
data: RepairTimeline
meta: dict[str, object] = Field(default_factory=dict)
error: None = None