193 lines
5.9 KiB
Python
193 lines
5.9 KiB
Python
"""Typed read-only projections consumed by the supply workbench."""
|
|
|
|
from typing import Annotated, Literal
|
|
|
|
from pydantic import AwareDatetime, BeforeValidator, Field, JsonValue, model_validator
|
|
|
|
from app.domain.schemas.shared import (
|
|
NonEmptyStr,
|
|
NonNegativeInt,
|
|
PositiveInt,
|
|
StrictBool,
|
|
StrictModel,
|
|
)
|
|
|
|
|
|
def _workbench_datetime_wire_value(value: object):
|
|
from datetime import datetime
|
|
|
|
if not isinstance(value, (datetime, str)):
|
|
raise ValueError("datetime input must be an RFC3339 string or datetime instance")
|
|
return value
|
|
|
|
|
|
WorkbenchDateTime = Annotated[
|
|
AwareDatetime, BeforeValidator(_workbench_datetime_wire_value)
|
|
]
|
|
ProjectionAvailability = Literal["ready", "not_configured", "degraded"]
|
|
|
|
|
|
class WorkbenchRuntimeCondition(StrictModel):
|
|
id: NonEmptyStr
|
|
task_id: Literal["backend-schedule"]
|
|
session_id: NonEmptyStr
|
|
title: NonEmptyStr
|
|
summary: NonEmptyStr
|
|
status: Literal["pending", "running", "completed", "warning", "error"]
|
|
risk_level: Literal["normal", "attention", "critical"]
|
|
scheduled_at: WorkbenchDateTime
|
|
updated_at: WorkbenchDateTime
|
|
detail: NonEmptyStr
|
|
recommendation: NonEmptyStr | None
|
|
evidence: list[NonEmptyStr]
|
|
model_name: Literal["TJWater scheduler"]
|
|
|
|
|
|
class WorkbenchAlertSource(StrictModel):
|
|
task_type: Literal["scheduled_task", "analysis_task", "other_task"]
|
|
task_id: NonEmptyStr
|
|
run_id: NonEmptyStr
|
|
|
|
|
|
class WorkbenchRuntimeAlert(StrictModel):
|
|
id: NonEmptyStr
|
|
condition_id: NonEmptyStr | None
|
|
source: WorkbenchAlertSource
|
|
title: NonEmptyStr
|
|
description: NonEmptyStr
|
|
occurred_at: WorkbenchDateTime
|
|
severity: Literal["warning", "critical"]
|
|
|
|
|
|
class WorkbenchPersistedAlert(StrictModel):
|
|
alert_id: NonEmptyStr
|
|
condition_id: NonEmptyStr | None
|
|
source: WorkbenchAlertSource
|
|
title: NonEmptyStr
|
|
description: NonEmptyStr
|
|
occurred_at: WorkbenchDateTime
|
|
severity: Literal["warning", "critical"]
|
|
status: Literal["active", "acknowledged", "resolved"]
|
|
producer_id: NonEmptyStr | None = None
|
|
updated_at: WorkbenchDateTime | None = None
|
|
|
|
|
|
class WorkbenchRuntimeScenario(StrictModel):
|
|
id: NonEmptyStr
|
|
run_id: NonEmptyStr
|
|
name: NonEmptyStr
|
|
description: NonEmptyStr
|
|
status: Literal["draft", "active", "review"]
|
|
run_type: NonEmptyStr
|
|
run_type_label: NonEmptyStr
|
|
run_status: NonEmptyStr
|
|
created_at: WorkbenchDateTime
|
|
|
|
|
|
class WorkbenchRuntimeAnalysisFact(StrictModel):
|
|
label: NonEmptyStr
|
|
value: NonEmptyStr
|
|
|
|
|
|
class WorkbenchRuntimeAnalysisResult(StrictModel):
|
|
result_type: NonEmptyStr
|
|
created_at: WorkbenchDateTime
|
|
facts: list[WorkbenchRuntimeAnalysisFact]
|
|
|
|
|
|
class WorkbenchRuntimeLatestAnalysis(StrictModel):
|
|
run_id: NonEmptyStr
|
|
name: NonEmptyStr
|
|
run_type: NonEmptyStr
|
|
run_type_label: NonEmptyStr
|
|
status: NonEmptyStr
|
|
status_label: NonEmptyStr
|
|
created_by: NonEmptyStr
|
|
created_at: WorkbenchDateTime
|
|
started_at: WorkbenchDateTime
|
|
results: list[WorkbenchRuntimeAnalysisResult]
|
|
map_payload: dict[str, JsonValue] | None
|
|
|
|
|
|
class WorkbenchAnalysisHistoryItem(StrictModel):
|
|
run_id: NonEmptyStr
|
|
name: NonEmptyStr
|
|
run_type: NonEmptyStr
|
|
run_type_label: NonEmptyStr
|
|
status: NonEmptyStr
|
|
status_label: NonEmptyStr
|
|
created_by: NonEmptyStr
|
|
created_at: WorkbenchDateTime
|
|
started_at: WorkbenchDateTime
|
|
has_results: StrictBool
|
|
|
|
|
|
class WorkbenchAnalysisHistoryPage(StrictModel):
|
|
items: list[WorkbenchAnalysisHistoryItem]
|
|
limit: PositiveInt
|
|
offset: NonNegativeInt
|
|
next_offset: NonNegativeInt | None
|
|
|
|
|
|
class WorkOrderActivity(StrictModel):
|
|
sequence: PositiveInt
|
|
occurred_at: WorkbenchDateTime
|
|
actor: NonEmptyStr
|
|
kind: Literal["created", "updated", "feedback"]
|
|
title: NonEmptyStr
|
|
detail: NonEmptyStr
|
|
|
|
|
|
class WorkbenchRuntimeWorkOrder(StrictModel):
|
|
id: NonEmptyStr
|
|
code: NonEmptyStr
|
|
scheduled_at: WorkbenchDateTime
|
|
title: NonEmptyStr
|
|
summary: NonEmptyStr
|
|
status: Literal["pending", "running", "awaiting_reply", "completed"]
|
|
risk_level: Literal["normal", "attention", "critical"]
|
|
updated_at: WorkbenchDateTime
|
|
detail: NonEmptyStr
|
|
recommendation: NonEmptyStr | None
|
|
duration_minutes: PositiveInt
|
|
source: NonEmptyStr
|
|
dispatcher: NonEmptyStr
|
|
location: NonEmptyStr | None = None
|
|
assignee: NonEmptyStr | None = None
|
|
priority: Literal["normal", "urgent"]
|
|
feedback_requirements: list[NonEmptyStr] = Field(default_factory=list)
|
|
activity: list[WorkOrderActivity] = Field(default_factory=list)
|
|
|
|
@model_validator(mode="before")
|
|
@classmethod
|
|
def accept_legacy_document(cls, value):
|
|
if not isinstance(value, dict) or "subtasks" not in value:
|
|
return value
|
|
document = {key: item for key, item in value.items() if key != "subtasks"}
|
|
subtasks = value.get("subtasks") or []
|
|
first = subtasks[0] if subtasks else {}
|
|
document.setdefault("location", first.get("location"))
|
|
document.setdefault("assignee", first.get("assignee"))
|
|
document.setdefault("feedback_requirements", list(dict.fromkeys(
|
|
requirement for subtask in subtasks
|
|
for requirement in subtask.get("reply_requirements", [])
|
|
)))
|
|
document.setdefault("activity", [])
|
|
return document
|
|
|
|
|
|
class WorkbenchRuntimeSnapshot(StrictModel):
|
|
availability: ProjectionAvailability
|
|
source: Literal["none", "scheduled_tasks"]
|
|
scenario_availability: ProjectionAvailability
|
|
artifact_availability: ProjectionAvailability
|
|
schedule_availability: ProjectionAvailability
|
|
work_order_availability: ProjectionAvailability
|
|
alert_availability: ProjectionAvailability
|
|
generated_at: WorkbenchDateTime
|
|
conditions: list[WorkbenchRuntimeCondition]
|
|
alerts: list[WorkbenchRuntimeAlert]
|
|
scenarios: list[WorkbenchRuntimeScenario]
|
|
latest_analysis: WorkbenchRuntimeLatestAnalysis | None
|
|
work_orders: list[WorkbenchRuntimeWorkOrder]
|