feat: extend operational investigation workflows
This commit is contained in:
@@ -26,6 +26,8 @@ from app.auth.project_dependencies import (
|
||||
from app.domain.domain_errors import DomainError
|
||||
from app.domain.schemas.workbench_runtime import WorkbenchDateTime
|
||||
from app.domain.schemas.operational_data import TaskDataQuery, TaskDataSnapshot
|
||||
from app.domain.schemas.task_question import TaskQuestionCreate, TaskQuestionReply, TaskQuestionRequest
|
||||
from app.services.task_questions import create_question, answer_question
|
||||
from app.domain.schemas.operational_task import (
|
||||
ActionDecisionCommand,
|
||||
ActionExecuteCommand,
|
||||
@@ -74,6 +76,7 @@ from app.domain.schemas.operational_task import (
|
||||
TaskPresentationEventCommand,
|
||||
TaskPresentationEventResult,
|
||||
TaskPresentationCursor,
|
||||
TaskPresentationHistoryPage,
|
||||
)
|
||||
from app.infra.db.postgresql.operational_tasks import OperationalTaskRepository
|
||||
from app.infra.integrations.operational_actions import (
|
||||
@@ -521,6 +524,21 @@ async def get_operational_task_presentation_cursor(
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.get("/operational-tasks/{task_id}/presentation-events", response_model=TaskPresentationHistoryPage)
|
||||
async def get_operational_task_presentation_history(
|
||||
task_id: UUID,
|
||||
after: int = Query(default=0, ge=0),
|
||||
through: int | None = Query(default=None, ge=0),
|
||||
limit: int = Query(default=100, ge=1, le=200),
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_VIEW)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await _service(ctx, conn).presentation_history(task_id, after=after, through=through, limit=limit)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.post("/operational-tasks/{task_id}/presentation-events", response_model=TaskPresentationEventResult)
|
||||
async def publish_operational_task_presentation_event(
|
||||
task_id: UUID,
|
||||
@@ -771,3 +789,27 @@ async def withdraw_operational_task_external_source(
|
||||
return await _service(ctx, conn).withdraw_external_source(task_id, command)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.post("/operational-tasks/{task_id}/questions", response_model=TaskQuestionRequest)
|
||||
async def create_operational_task_question(
|
||||
task_id: UUID, command: TaskQuestionCreate,
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_EXECUTE)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await create_question(_service(ctx, conn), task_id, command)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
|
||||
@router.post("/operational-tasks/{task_id}/questions/{question_id}/answer", response_model=OperationalTaskDetail)
|
||||
async def answer_operational_task_question(
|
||||
task_id: UUID, question_id: UUID, command: TaskQuestionReply,
|
||||
ctx: ProjectContext = Depends(require_permission(OPERATIONAL_TASK_WRITE)),
|
||||
conn: AsyncConnection = Depends(get_project_pg_connection),
|
||||
):
|
||||
try:
|
||||
return await answer_question(_service(ctx, conn), task_id, question_id, command)
|
||||
except DomainError as exc:
|
||||
_raise_domain(exc)
|
||||
|
||||
@@ -161,6 +161,9 @@ def valve_isolation_endpoint(
|
||||
network: str = Query(..., description="管网名称(或数据库名称)"),
|
||||
accident_element: List[str] = Query(..., description="发生事故的管段/节点ID列表"),
|
||||
disabled_valves: List[str] = Query(None, description="已故障的阀门ID列表(可选)"),
|
||||
accident_element_type: Literal["auto", "link", "node"] = Query(
|
||||
"auto", description="事故要素类型;auto优先管段再节点;分析同名节点须指定node,纯管段分析可指定link",
|
||||
),
|
||||
):
|
||||
"""
|
||||
阀门隔离分析
|
||||
@@ -168,13 +171,19 @@ def valve_isolation_endpoint(
|
||||
- **network**: 管网名称(或数据库名称)
|
||||
- **accident_element**: 发生事故的管段/节点ID列表
|
||||
- **disabled_valves**: 已故障的阀门ID列表(可选)
|
||||
- **accident_element_type**: auto优先管段;link仅匹配管段;node仅匹配节点
|
||||
|
||||
返回隔离方案,包括:
|
||||
- must_close_valves: 必须关闭的阀门列表
|
||||
- optional_valves: 可选关闭的阀门列表
|
||||
- affected_nodes: 受影响的节点列表;不可隔离时为空列表
|
||||
- affected_node_count: 受影响的节点总数
|
||||
- isolatable: 是否可以有效隔离
|
||||
- affected_nodes / affected_node_count: 最近阀门围成的直接隔离区;不可隔离时列表为空,计数保留
|
||||
- supply_affected_nodes / supply_affected_node_count: 按建议关阀后新增失去水源连接的节点;未验证或不可隔离时为null
|
||||
- source_check_performed: 是否已执行水源连通检查
|
||||
- isolation_failure_reason: 不可隔离原因;可隔离时为null
|
||||
- isolatable: 存在关阀边界,且直接隔离区不再连通任何水源
|
||||
|
||||
使用模型初始开关状态进行无向拓扑分析,水池按潜在水源处理。
|
||||
不包含时变控制、水池蓄水量或压力流量计算,不是现场停水范围的水力证明。
|
||||
"""
|
||||
# result = {
|
||||
# "accident_element": "P461309",
|
||||
@@ -193,7 +202,10 @@ def valve_isolation_endpoint(
|
||||
# "must_close_valves": ["210521658", "V12974", "V12986", "V12993"],
|
||||
# "optional_valves": [],
|
||||
# }
|
||||
result = analyze_valve_isolation(network, accident_element, disabled_valves)
|
||||
result = analyze_valve_isolation(
|
||||
network, accident_element, disabled_valves,
|
||||
accident_element_type=accident_element_type,
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user