Add Dispatch_V0.1.1
This commit is contained in:
168
Dispatch_V0.1.1/domain/task.py
Normal file
168
Dispatch_V0.1.1/domain/task.py
Normal file
@@ -0,0 +1,168 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# hub/ticket/domain/task.py
|
||||
|
||||
"""Доменная сущность задачи Ticket и сериализация её состояния."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Any, Mapping
|
||||
|
||||
from .ticket_types import TicketTaskSnapshot
|
||||
|
||||
|
||||
def _normalize_datetime(value: Any) -> datetime | None:
|
||||
"""Преобразовать строку или datetime в datetime."""
|
||||
if value is None or value == "":
|
||||
return None
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
if not isinstance(value, str):
|
||||
return None
|
||||
try:
|
||||
if "T" in value:
|
||||
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||
return datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def _normalize_color(value: Any) -> str:
|
||||
"""Нормализовать представление цвета до hex-строки."""
|
||||
if isinstance(value, str) and value.startswith("#") and len(value) >= 4:
|
||||
return value
|
||||
name_getter = getattr(value, "name", None)
|
||||
if callable(name_getter):
|
||||
try:
|
||||
normalized = name_getter()
|
||||
if isinstance(normalized, str) and normalized.startswith("#"):
|
||||
return normalized
|
||||
except Exception as _exc:
|
||||
return "#FFFFFF"
|
||||
return "#FFFFFF"
|
||||
|
||||
|
||||
def _normalize_int(value: Any, default: int | None = None) -> int | None:
|
||||
"""Нормализовать число, сохранив валидное значение 0."""
|
||||
if value is None or value == "":
|
||||
return default
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class TicketTask:
|
||||
"""Каноническая доменная задача Ticket."""
|
||||
|
||||
task_id: int
|
||||
location: str
|
||||
state_code: int
|
||||
state_name: str
|
||||
action_text: str = ""
|
||||
color_hex: str = "#FFFFFF"
|
||||
created_at: datetime | None = None
|
||||
completed_at: datetime | None = None
|
||||
refused_from_state: int | None = None
|
||||
refusal_reason: str = ""
|
||||
assigned_specialist: str = ""
|
||||
specialist_photo: str = ""
|
||||
diagnostic_report_signed: bool = False
|
||||
repair_report_signed: bool = False
|
||||
acceptance_report_signed: bool = False
|
||||
sequence_number: int = 0
|
||||
|
||||
@classmethod
|
||||
def from_record(cls, record: Mapping[str, Any]) -> TicketTask | None:
|
||||
"""Создать задачу из записи файлового хранилища."""
|
||||
raw_task_id = record.get("button_id")
|
||||
if raw_task_id is None:
|
||||
return None
|
||||
try:
|
||||
task_id = int(raw_task_id)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
return cls(
|
||||
task_id=task_id,
|
||||
location=str(record.get("location", "")),
|
||||
state_code=_normalize_int(record.get("state"), 1) or 0,
|
||||
state_name=str(record.get("state_name", "")),
|
||||
action_text=str(record.get("action", "")),
|
||||
color_hex=_normalize_color(record.get("color", "#FFFFFF")),
|
||||
created_at=_normalize_datetime(record.get("created_time")) or datetime.now(),
|
||||
completed_at=_normalize_datetime(record.get("completed_time")),
|
||||
refused_from_state=_normalize_int(record.get("refused_from_state")),
|
||||
refusal_reason=str(record.get("refusal_reason", "")),
|
||||
assigned_specialist=str(record.get("assigned_specialist", "")),
|
||||
specialist_photo=str(record.get("specialist_photo", "")),
|
||||
diagnostic_report_signed=bool(record.get("diagnostic_report_signed", False)),
|
||||
repair_report_signed=bool(record.get("repair_report_signed", False)),
|
||||
acceptance_report_signed=bool(record.get("acceptance_report_signed", False)),
|
||||
sequence_number=_normalize_int(record.get("sequence_number"), 0) or 0,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_snapshot(cls, snapshot: TicketTaskSnapshot) -> TicketTask:
|
||||
"""Создать доменную сущность из snapshot."""
|
||||
return cls(
|
||||
task_id=snapshot.task_id,
|
||||
location=snapshot.location,
|
||||
state_code=snapshot.state_code,
|
||||
state_name=snapshot.state_name,
|
||||
action_text=snapshot.action_text,
|
||||
color_hex=snapshot.color_hex,
|
||||
created_at=snapshot.created_at,
|
||||
completed_at=snapshot.completed_at,
|
||||
refused_from_state=snapshot.refused_from_state,
|
||||
refusal_reason=snapshot.refusal_reason,
|
||||
assigned_specialist=snapshot.assigned_specialist,
|
||||
specialist_photo=snapshot.specialist_photo,
|
||||
diagnostic_report_signed=snapshot.diagnostic_report_signed,
|
||||
repair_report_signed=snapshot.repair_report_signed,
|
||||
acceptance_report_signed=snapshot.acceptance_report_signed,
|
||||
sequence_number=snapshot.sequence_number,
|
||||
)
|
||||
|
||||
def to_record(self) -> dict[str, Any]:
|
||||
"""Преобразовать доменную сущность в запись для JSON."""
|
||||
return {
|
||||
"button_id": self.task_id,
|
||||
"location": self.location,
|
||||
"state": self.state_code,
|
||||
"action": self.action_text,
|
||||
"state_name": self.state_name,
|
||||
"color": self.color_hex,
|
||||
"created_time": self.created_at,
|
||||
"completed_time": self.completed_at,
|
||||
"refused_from_state": self.refused_from_state,
|
||||
"refusal_reason": self.refusal_reason,
|
||||
"assigned_specialist": self.assigned_specialist,
|
||||
"specialist_photo": self.specialist_photo,
|
||||
"diagnostic_report_signed": self.diagnostic_report_signed,
|
||||
"repair_report_signed": self.repair_report_signed,
|
||||
"acceptance_report_signed": self.acceptance_report_signed,
|
||||
"sequence_number": self.sequence_number,
|
||||
}
|
||||
|
||||
def to_snapshot(self) -> TicketTaskSnapshot:
|
||||
"""Вернуть неизменяемый снимок задачи для внешних слоёв."""
|
||||
return TicketTaskSnapshot(
|
||||
task_id=self.task_id,
|
||||
location=self.location,
|
||||
state_code=self.state_code,
|
||||
state_name=self.state_name,
|
||||
action_text=self.action_text,
|
||||
color_hex=self.color_hex,
|
||||
created_at=self.created_at,
|
||||
completed_at=self.completed_at,
|
||||
refused_from_state=self.refused_from_state,
|
||||
refusal_reason=self.refusal_reason,
|
||||
assigned_specialist=self.assigned_specialist,
|
||||
specialist_photo=self.specialist_photo,
|
||||
diagnostic_report_signed=self.diagnostic_report_signed,
|
||||
repair_report_signed=self.repair_report_signed,
|
||||
acceptance_report_signed=self.acceptance_report_signed,
|
||||
sequence_number=self.sequence_number,
|
||||
)
|
||||
Reference in New Issue
Block a user