197 lines
5.1 KiB
Python
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
|